Query sui criteri JPA

1. Panoramica

In questo tutorial, discuteremo una funzionalità JPA molto utile: le query sui criteri.

Non solo ci consente di scrivere query senza eseguire SQL grezzo, ma ci fornisce anche un certo controllo Object Oriented sulle query, che è una delle caratteristiche principali di Hibernate. L'API Criteria ci consente di creare un oggetto query di criteri a livello di codice, in cui possiamo applicare diversi tipi di regole di filtraggio e condizioni logiche.

A partire da Hibernate 5.2, l'API Hibernate Criteria è obsoleta e il nuovo sviluppo è incentrato sull'API JPA Criteria. Esploreremo come utilizzare Hibernate e JPA per creare query di criteri.

2. Dipendenze di Maven

Per illustrare l'API, utilizzeremo l'implementazione JPA di riferimento: Hibernate.

Per usare Hibernate assicurati di aggiungere l'ultima versione al tuo file pom.xml :

 org.hibernate hibernate-core 5.3.2.Final 

L'ultima versione di Hibernate può essere trovata qui.

3. Semplice esempio utilizzando criteri

Iniziamo esaminando come recuperare i dati utilizzando le query dei criteri. Daremo uno sguardo a come ottenere tutte le istanze di una particolare classe dal database.

Abbiamo una classe Item che rappresenta la tupla "ITEM" nel database:

public class Item implements Serializable { private Integer itemId; private String itemName; private String itemDescription; private Integer itemPrice; // standard setters and getters }

Diamo un'occhiata a una semplice query di criteri che recupererà tutte le righe di " ITEM" dal database:

Session session = HibernateUtil.getHibernateSession(); CriteriaBuilder cb = session.getCriteriaBuilder(); CriteriaQuery cr = cb.createQuery(Item.class); Root root = cr.from(Item.class); cr.select(root); Query query = session.createQuery(cr); List results = query.getResultList();

La query precedente è una semplice dimostrazione di come ottenere tutti gli elementi. Vediamo cosa è stato fatto, passo dopo passo:

  1. Creare un'istanza della sessione dalla SessionFactory oggetto
  2. Creare un'istanza di C riteriaBuilder chiamando il metodo getCriteriaBuilder ()
  3. Creare un'istanza di CriteriaQuery chiamando il metodo createQuery () di CriteriaBuilder
  4. Crea un'istanza di Query chiamando il metodo Session createQuery ()
  5. Chiama il metodo getResultList () dell'oggetto query che ci fornisce i risultati

Ora che abbiamo coperto le basi, passiamo ad alcune delle funzionalità della query dei criteri:

3.1. Usare le espressioni

Il CriteriaBuilder può essere utilizzato per limitare i risultati della query in base a condizioni specifiche. Utilizzando il metodo CriteriaQuery where () e fornire le espressioni create da CriteriaBuilder.

Ecco alcuni esempi di espressioni di uso comune :

Per ottenere articoli con un prezzo superiore a 1000:

cr.select(root).where(cb.gt(root.get("itemPrice"), 1000));

Successivamente, ottenere articoli con itemPrice inferiore a 1000:

cr.select(root).where(cb.lt(root.get("itemPrice"), 1000));

Gli elementi con itemNames contengono Chair :

cr.select(root).where(cb.like(root.get("itemName"), "%chair%"));

Record con itemPrice compreso tra 100 e 200:

cr.select(root).where(cb.between(root.get("itemPrice"), 100, 200));

Per verificare se la proprietà data è nulla:

cr.select(root).where(cb.isNull(root.get("itemDescription")));

Per verificare se la proprietà data non è nulla:

cr.select(root).where(cb.isNotNull(root.get("itemDescription")));

Puoi anche usare i metodi isEmpty () e isNotEmpty () per verificare se un List all'interno di una classe è vuoto o meno.

Ora inevitabilmente viene la domanda se possiamo combinare due o più dei confronti di cui sopra o meno. La risposta è, ovviamente, sì: l'API Criteria ci consente di concatenare facilmente le espressioni :

Predicate[] predicates = new Predicate[2]; predicates[0] = cb.isNull(root.get("itemDescription")); predicates[1] = cb.like(root.get("itemName"), "chair%"); cr.select(root).where(predicates);

Per aggiungere due espressioni con operazioni logiche:

Predicate greaterThanPrice = cb.gt(root.get("itemPrice"), 1000); Predicate chairItems = cb.like(root.get("itemName"), "Chair%");

Elementi con le condizioni sopra definite unite con OR logico :

cr.select(root).where(cb.or(greaterThanPrice, chairItems));

Per ottenere elementi corrispondenti alle condizioni sopra definite unite con AND logico :

cr.select(root).where(cb.and(greaterThanPrice, chairItems));

3.2. Ordinamento

Ora che conosciamo l'utilizzo di base di Criteria , diamo un'occhiata alle funzionalità di ordinamento di Criteria .

Nell'esempio seguente ordiniamo l'elenco in ordine crescente del nome e quindi in ordine decrescente del prezzo:

cr.orderBy( cb.asc(root.get("itemName")), cb.desc(root.get("itemPrice")));

Nella prossima sezione, vedremo come eseguire funzioni aggregate.

3.3. Proiezioni, aggregati e funzioni di raggruppamento

Finora abbiamo coperto la maggior parte degli argomenti di base. Ora diamo uno sguardo alle diverse funzioni aggregate:

Ottieni il conteggio delle righe:

CriteriaQuery cr = cb.createQuery(Long.class); Root root = cr.from(Item.class); cr.select(cb.count(root)); Query query = session.createQuery(cr); List itemProjected = query.getResultList();

Quello che segue è un esempio di funzioni aggregate:

Funzione aggregata per media :

CriteriaQuery cr = cb.createQuery(Double.class); Root root = cr.from(Item.class); cr.select(cb.avg(root.get("itemPrice"))); Query query = session.createQuery(cr); List avgItemPriceList = query.getResultList();

Altri utili metodi di aggregazione disponibili sono sum () , max () , min () , count () ecc.

3.4. CriteriaUpdate

A partire da JPA 2.1, è disponibile il supporto per eseguire gli aggiornamenti del database utilizzando l' API Criteria .

CriteriaUpdate has a set() method that can used to provide new values for database records:

CriteriaUpdate criteriaUpdate = cb.createCriteriaUpdate(Item.class); Root root = criteriaUpdate.from(Item.class); criteriaUpdate.set("itemPrice", newPrice); criteriaUpdate.where(cb.equal(root.get("itemPrice"), oldPrice)); Transaction transaction = session.beginTransaction(); session.createQuery(criteriaUpdate).executeUpdate(); transaction.commit();

In the above snippet, we create an instance of CriteriaUpdate from the CriteriaBuilder and use its set() method to provide new values for the itemPrice. To update multiple properties, we just need to call the set() method multiple times.

3.5. CriteriaDelete

CriteriaDelete, as its name implies, enables a delete operation using the Criteria API. All we need is to create an instance of CriteriaDelete and use the where() method to apply restrictions:

CriteriaDelete criteriaDelete = cb.createCriteriaDelete(Item.class); Root root = criteriaDelete.from(Item.class); criteriaDelete.where(cb.greaterThan(root.get("itemPrice"), targetPrice)); Transaction transaction = session.beginTransaction(); session.createQuery(criteriaDelete).executeUpdate(); transaction.commit();

4. Advantage Over HQL

In the previous sections we've covered how to use Criteria Queries.

Clearly, the main and most hard-hitting advantage of Criteria queries over HQL is the nice, clean, Object Oriented API.

We can simply write more flexible, dynamic queries compared to plain HQL. The logic can be refactored with the IDE and has all the type-safety benefits of the Java language itself.

There are of course some disadvantages as well, especially around more complex joins.

So, generally speaking, we'll have to use the best tool for the job – that can be the Criteria API in most cases, but there are definitely cases where we'll have to go lower level.

5. Conclusion

In this article, we focused on the basics of Criteria Queries in Hibernate and JPA, and also on some of the advanced features of the API.

Il codice discusso qui è disponibile nel repository Github.