Jednym z elementów Hibernate, który nie trafił do specyfikacji JPA (Java Persistence API) były zapytania kryteriowe (ang. criteria queries). Jest to sposób na tworzenie zapytań dynamicznie, który w Grails stał się jeszcze bardziej trywialny z pomocą wsparcia "budowniczych" (ang. builders) z Groovy. Mechanizm "budowniczych" polega na wykonaniu hierarchii metod i domknięć w celu stworzenia hierarchicznych struktur drzewiastych, jak np. dokumenty XMLowe czy hierarchia komponentów GUI.
GORM rozszerza każdą klasę dziedzinową o statyczną metodę createCriteria() do utworzenia egzemplarza zapytania kryteriowego. Następnie, korzystamy z 4 metod akceptujących domknięcie do zbudowania bardziej szczegółowego zapytania:
- get() do odszukania pojedynczego egzemplarza klasy dziedzinowej
- list() dla listy klas dziedzinowych spełniających warunek
- scroll(), który zwraca ScrollableResults
- count(), którego wynikiem jest liczba egzemplarzy
def c = Album.createCriteria()Metody w ramach domknięcia są zamieniane na odpowiednie wywołania metod na egzemplarzu klasy org.hibernate.criterion.Restrictions z Hibernate.
def results = c.list {
eq('genre', 'Alternative')
between('dateCreated', new Date()-30, new Date())
}
Domknięcie jest blokiem kodu, który może być przypisywany do zmiennej oraz może odwoływać się do zmiennych z otoczenia. W ten sposób można stworzyć wzorzec zapytania (domknięcie), przypisać do zmiennej i wykonać z nim jedną z 4 wymienionych metod, np. (Listing 10-12):
def today = new Date()Interesującym elementem w tym przykładzie jest użycie operatora "spread" (gwiazdka przy value), który dzieli listę na dwa argumenty wejściowe (tablicę dwuargumentową) dla between, która wymaga 3 argumentów (więc niewprost zakłada się, że value jako lista będzie dwuelementowe). Reszta powinna być jasna, aczkolwiek dla mnie była niezwykle odświeżająca.
def queryMap = [genre:'Alternative', dateCreated: [today-30, today]]
def query = {
queryMap.each { key, value ->
if (value instanceof List) {
between(key, *value)
} else {
like(key, value)
}
}
}
def criteria = Album.createCriteria()
println(criteria.count(query))
Wyszukiwanie egzemplarzy klas dziedzinowych po stanie związanych z nimi klas dziedzinowych, to wykonanie metod, których nazwy odpowiadają nazwom pól łączących klasy dziedzinowe w związki, np. (Listing 10-13):
def criteria = Album.withCriteria {Jakkolwiek użyliśmy nowej metody statycznej withCriteria() to dotyczy to również createCriteria().
songs {
ilike('title', '%Shake%')
}
}
GORM udostępnia metodę projections(), której parametrem wejściowym jest domknięcie, do tworzenia specjalizowanych zapytań z SQLowymi count, distrinct czy sum. Wewnątrz domknięcia wykonujemy metody org.hibernate.criterion.Projections z Hibernate, np. countDistinct().
GORM posiada mechanizm "zapytań przez przykład" (ang. query by example), gdzie metodzie find() czy findAll() przekazujemy egzemplarz poszukiwanej klasy z odpowiednim stanem. W połączeniu z tworzonym przez Groovy domyślnym konstruktorem akceptującym serię atrybut/wartość daje to interesujące konstrukcje, np. (Listing 10-15):
def album = Album.find( new Album(title:'Odelay') )Zapytanie jest budowane na podstawie wartości przekazywanych w konstruktorze.
Do pracy z zapytaniami HQL w Grails mamy metody find(), findAll() oraz executeQuery(). Podajemy im parametr tekstowy będący zapytaniem HQL. Zapytania z "?" wypełniane są wartościami z listy będącą ich drugim parametrem wejściowym (podobnie z parametrami nazwanymi), np. (Listing 10-18):
def album = Album.find(Stronicowanie w GORM to wykonanie zapytania HQL z listą z parametrami max i offset, np.
'from Album as a where a.title = :theTitle',
[theTitle:'Odelay'])
Album.findAllByGenre("Alternative", [max:10, offset:20])Konfiguracja GORM jest w pliku grails-app/conf/DataSource.groovy w sekcji dataSource. Większość parametrów konfiguracyjnych Hibernate jest dostępna w GORM, np. wyświetlanie wysyłanych zapytań to
dataSource {albo ustawienie dialektu
logSql = true
}
dataSource {Pełne nazwy parametrów konfiguracyjnych Hibernate określamy w sekcji hibernate we wspomnianym grails-app/conf/DataSource.groovy, np.
dialect = org.hibernate.dialect.MySQL5InnoDBDialect
}
hibernate {Zapisanie zmian w bazie danych jest domyślnie buforowane do późniejszego, bardziej odpowiedniego momentu niż każdorazowe zapisanie w bazie bezpośrednio po zmianie. Jeśli nie interesuje nas takie zachowanie GORM, możemy wykonać metody save() oraz delete() z parametrem flush ustawionym na true, np. (Listing 10-24):
hibernate.connection.isolation=4
}
def album = new Album(...)Oczywiście wypchnięcie zmian pojedynczego egzemplarza klasy dziedzinowej powoduje wypchnięcie zmian w całej sesji Hibernate (wykonanie metody Session.flush()).
album.save(flush:true)
Dostęp do aktualnie otwartej sesji Hibernate jest możliwy dzięki mechanizmowi wstrzeliwania zależności ze zmienną sessionFactory, np. (Listing 10-26):
def sessionFactoryIstnieje również inny sposób ze statyczną metodą withSession() dostępną w każdej klasie dziedzinowej, której domknięcie przyjmuje sesję Hibernate jako parametr wejściowy, np. (Listing 10-27):
def index = {
def session = sessionFactory.currentSession()
}
def index = {W rozdziale pojawia się wiele innych informacji nt. integracji GORM z Hibernate, jednakże są one związane z samym działaniem Hibernate i ich rozpoznanie zostawiam zainteresowanym jako dodatkową zachętę do lektury książki DGG2. Przed nami relacja z rozdziału 11. "Services" o klasach usługowych.
Album.withSession { session ->
...
}
}
Brak komentarzy:
Prześlij komentarz