JPA-Kriterienabfragen

1. Übersicht

In diesem Tutorial werden wir eine sehr nützliche JPA-Funktion diskutieren - Kriterienabfragen.

Es ermöglicht uns nicht nur, Abfragen zu schreiben, ohne unformatiertes SQL auszuführen, sondern gibt uns auch eine objektorientierte Kontrolle über die Abfragen, was eine der Hauptfunktionen von Hibernate ist. Mit der Kriterien-API können wir programmgesteuert ein Kriterienabfrageobjekt erstellen, in dem wir verschiedene Arten von Filterregeln und logischen Bedingungen anwenden können.

Seit Hibernate 5.2 ist die Hibernate Criteria API veraltet und die Neuentwicklung konzentriert sich auf die JPA Criteria API. Wir werden untersuchen, wie Sie mit Hibernate und JPA Kriterienabfragen erstellen.

2. Maven-Abhängigkeiten

Zur Veranschaulichung der API verwenden wir die Referenz-JPA-Implementierung - Hibernate.

Um Hibernate zu verwenden, stellen Sie sicher, dass Sie die neueste Version zu Ihrer pom.xml- Datei hinzufügen :

 org.hibernate hibernate-core 5.3.2.Final 

Die neueste Version von Hibernate finden Sie hier.

3. Einfaches Beispiel unter Verwendung von Kriterien

Beginnen wir mit dem Abrufen von Daten mithilfe von Kriterienabfragen. Wir werden uns ansehen, wie alle Instanzen einer bestimmten Klasse aus der Datenbank abgerufen werden.

Wir haben eine Item- Klasse, die das Tupel "ITEM" in der Datenbank darstellt:

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

Schauen wir uns eine einfache Kriterienabfrage an, mit der alle Zeilen von " ITEM" aus der Datenbank abgerufen werden:

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();

Die obige Abfrage ist eine einfache Demonstration, wie Sie alle Elemente erhalten. Mal sehen, was Schritt für Schritt getan wurde:

  1. Erstellen Sie eine Instanz von Session aus dem SessionFactory- Objekt
  2. Erstellen Sie eine Instanz von C riteriaBuilder, indem Sie die Methode getCriteriaBuilder () aufrufen
  3. Erstellen Sie eine Instanz von CriteriaQuery durch den Aufruf CriteriaBuilder Create () -Methode
  4. Erstellen Sie eine Instanz von Query, indem Sie die Session createQuery () -Methode aufrufen
  5. Rufen Sie die Methode getResultList () des Abfrageobjekts auf , die die Ergebnisse liefert

Nachdem wir uns nun mit den Grundlagen befasst haben, gehen wir zu einigen Funktionen der Kriterienabfrage über:

3.1. Mit Expressions

Der CriteriaBuilder kann verwendet werden, um Abfrageergebnisse basierend auf bestimmten Bedingungen einzuschränken. Mithilfe der CriteriaQuery where () -Methode können Sie von CriteriaBuilder erstellte Ausdrücke bereitstellen .

Hier einige Beispiele für häufig verwendete Ausdrücke :

So erhalten Sie Artikel mit einem Preis von mehr als 1000:

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

Als nächstes erhalten Sie Artikel mit einem itemPrice von weniger als 1000:

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

Artikel mit itemNames enthalten Chair :

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

Datensätze mit itemPrice zwischen 100 und 200:

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

So überprüfen Sie, ob die angegebene Eigenschaft null ist:

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

So überprüfen Sie, ob die angegebene Eigenschaft nicht null ist:

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

You can also use the methods isEmpty() and isNotEmpty() to test if a List within a class is empty or not.

Now inevitably the question comes, whether we can combine two or more of the above comparisons or not. The answer is, of course, yes – the Criteria API allows us to easily chain expressions:

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);

To add two expressions with logical operations:

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

Items with the above-defined conditions joined with Logical OR:

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

To get items matching with the above-defined conditions joined with Logical AND:

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

3.2. Sorting

Now that we know the basic usage of Criteria, let's have a look at the sorting functionalities of Criteria.

In the following example we order the list in an ascending order of the name and then in a descending order of the price:

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

In the next section, we will have a look at how to do aggregate functions.

3.3. Projections, Aggregates and Grouping Functions

So far we have covered most of the basic topics. Now let's have a look at the different aggregate functions:

Get row count:

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();

The following is an example of aggregate functions:

Aggregate function for Average:

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();

Other useful aggregate methods that are available are sum(), max(), min(), count() etc.

3.4. CriteriaUpdate

Starting from JPA 2.1, there's support for performing database updates using the Criteria API.

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.

Der hier beschriebene Code ist im Github-Repository verfügbar.