9 - Consultes
Hem vist com podem accedir molt còmodament a les classes que equivalen a una taula. Però en moltes ocasions ens farà falta accedir no exactament a una taula sinó a una combinació de taules, o en definitiva voldrem informació més elaborada. Per a poder "interrogar" a la Base de Dades amb consultes més complexes, Hibernate suporta un llenguatge de consulta Orientat a Objectes anomenat HQL (Hibernate Query Language), molt paregut a SQL ja que és una extensió Orientada a Objectes d'aquest.
En aquest sentit s'ha intentat fer un estàndar de llenguatge de consulta anomenat OQL (Object Query Language), desenvolupat per un grup amb ànim de crear un estàndar ODMG (Object Data Management Group). Aquest estàndar no l'ha implementat al 100% cap producte comercial, i en realitat tenim subconjunts d'aquest estàndar.
Utilitzarem la classe Query, i invocarem al mètode createQuery() de Session, que justament torna una Query. Aquest seria un exemple. Observeu que com a primer paràmetre se li passa la sentència, i com a segon paràmetre el tipus que torna; com que les classes no les hem traduïdes a Kotlin, hem d'especificar que són de Java:
Nota
Abans no calia el segon paràmetre, però des de la versió d'Hibernate 6, marca com a deprecated el createQuery si no es posa aquest segon paràmetre
Per a recuperar les dades tenim dues possibilitats, utilitzant dos mètodes de la query:
- Mètode list(): torna tots els resultats de la consulta en una col·lecció (List). Aquest mètode fa una crida única al SGBD i es duran totes les dades. Haurà d'haver, per tant, memòria suficient per a que càpiguen tots els resultats. Si és una quantitat gran de resultats, tardarà molt en executar-se.
- Mètode iterate(): torna un iterador per a poder recórrer els resultats de la consulta. Hibernate executa la sentència, però només torna els identificadors de les files del resultat, i cada vegada que fem next() del iterador, s'executa realment la consulta tornant la següent fila. Per tant fa falta molta menys memòria. Per contra es fan mots més accessos a la Base de Dades, encara que cadascun tardarà molt poc, però en total tardarà més. Es pot fins i tot fixar la quantitat de files a tornar amb el mètode setFetchSize().
En el següents dos exemples, que són equivalents, es trau una llista de totes les comarques. El primer utilitza el mètode list() i el segon el mètode iterate(). I en aquesta ocasió utilitzem un iterador per a recórrer la llista (List) en compte de foreach, per veure més d'una manera, i per similitud amb el segon exemple. Poseu a aquest primer exemple el nom de Exemple_21_AccesAmbList.kt :
package exemples
import classes.Comarca
import org.hibernate.cfg.Configuration
import java.util.logging.Level
import java.util.logging.LogManager
fun main(args: Array<String>) {
LogManager.getLogManager().getLogger("").setLevel(Level.SEVERE)
val sessio = Configuration().configure().buildSessionFactory().openSession()
val q = sessio.createQuery ("from Comarca", Comarca::class.java)
val llista = q.list ()
val it = llista.iterator ()
while (it.hasNext()) {
val com = it.next() // no fa falta fer un casting perquè ja sap que és Comarca
println(com.nomC + " - " + com.provincia)
}
sessio.close()
}
Aquest serà el resultat:
Safor - València
Horta Sud - València
Foia de Bunyol - València
Plana Baixa - Castelló
Horta Nord - València
Racó - València
Plana d'Utiel - València
Vall de Cofrents - València
Ribera Baixa - València
Ribera Alta - València
Marina Alta - Alacant
Serrans - València
València - València
Baix Maestrat - Castelló
Marina Baixa - Alacant
Vall d'Albaida - València
Canal de Navarrés - València
Horta Oest - València
Camp de Túria - València
Alt Millars - Castelló
Baix Vinalopó - Alacant
Comtat - Alacant
Alt Palància - Castelló
Plana Alta - Castelló
Alacantí - Alacant
Camp de Morvedre - València
Alt Vinalopó - Alacant
Costera - València
Alcalatén - Castelló
Alcoià - Alacant
Vinalopó Mitjà - Alacant
Alt Maestrat - Castelló
Baix Segura - Alacant
Ports - Castelló
Encara que la manera més curta i potser més clara és utilitzant un bucle foreach amb el mètode list(). Només haurem d'anar amb compte de fer un cast per a que sàpiga quin tipus d'element és. Copieu aquest tercer exemple al fitxer Exemple_22_AccesAmbListForEach.kt :
package exemples
import classes.Comarca
import org.hibernate.cfg.Configuration
import java.util.logging.Level
import java.util.logging.LogManager
fun main(args: Array<String>) {
LogManager.getLogManager().getLogger("").setLevel(Level.SEVERE)
val sessio = Configuration().configure().buildSessionFactory().openSession()
val q = sessio.createQuery ("from Comarca", Comarca::class.java)
for (c in q.list()) {
println(c.nomC + " --- " + c.provincia)
}
sessio.close()
}
Si sabem que la consulta tornarà únicament una fila, podem assignar aquesta fila a un objecte de la classe de la taula afectada, posant el mètode uniqueResult() en la creació de la query, així ens estalviem passos. Copieu el següent programa al fitxer kotlin Exemple_23_AccesAmbUniqueResult.kt :
package exemples
import classes.Comarca
import org.hibernate.cfg.Configuration
import java.util.logging.Level
import java.util.logging.LogManager
fun main(args: Array<String>) {
LogManager.getLogManager().getLogger("").setLevel(Level.SEVERE)
val sessio = Configuration().configure().buildSessionFactory().openSession()
val d = sessio.createQuery ("from Comarca where nomC='Alcalatén'",Comarca::class.java).uniqueResult()
println(d.nomC + " - " + d.provincia)
sessio.close()
}
Però en realitat en aquest exemple poca cosa hem guanyat, perquè per a agafar l'objecte corresponent a una única fila d'una taula, ja ho féiem amb session.get(). Més endavant veurem consultes més complicades on trobarem la utilitat.
Llicenciat sota la Llicència Creative Commons Reconeixement NoComercial CompartirIgual 2.5