Friday, March 29, 2024
HomeJavaHibernate-specific extensions to the Standards API

Hibernate-specific extensions to the Standards API


Most builders know that the JPA specification defines the string-based JPQL question language and that Hibernate extends it to help issues like database-specific features, window features, and set-based operations. However most builders don’t know that since model 6, Hibernate has achieved the identical for JPA’s Standards API.

Extending an API is, in fact, a bit extra complicated than doing the identical for a string-based question language. To increase the options of JPQL, the Hibernate group solely wants so as to add extra options to the parser of the question string and doesn’t want to alter any of the official APIs. The extension of the Standards API requires further interfaces and new strategies that return these interfaces.

Hibernate 6 handles this by offering the HibernateCriteriaBuilder interface, which extends JPA’s CriteriaBuilder interface, and by including a technique to its proprietary Session interface to get a HibernateCriteriaBuilder occasion.

The HibernateCriteriaBuilder interface

Earlier than we speak in regards to the HibernateCriteriaBuilder interface, we have to take one step again and check out the creation of a regular CriteriaQuery. And after that, I’ll present you tips on how to get a HibernateCriteriaBuilder and the options it provides to JPA’s normal Standards API.

Working with JPA’s CriteriaBuilder interface

Step one to utilizing the Standards API is all the time a name of the getCriteriaBuilder methodology on the EntityManager interface. That methodology returns an occasion of JPA’s CriteriaBuilder, which you should utilize to create totally different components of your question. Within the following code snippet, I exploit it to create a really primary question that returns all ChessGame entities {that a} participant performed with the white items whose title ends on “anssen”.

EntityManager em = emf.createEntityManager();
em.getTransaction().start();

CriteriaBuilder cBuilder = em.getCriteriaBuilder();
CriteriaQuery<ChessGame> q = cBuilder.createQuery(ChessGame.class);
Root<ChessGame> recreation = q.from(ChessGame.class);
q.choose(recreation);
q.the place(cBuilder.like(recreation.get("playerWhite"), "%anssen"));

em.getTransaction().commit();
em.shut();

As you possibly can see, I exploit JPA’s CriteriaBuilder in 2 locations:

  1. To create a CriteriaQuery object that represents a question that returns ChessGame objects.
  2. To create a like predicate for the WHERE clause of the question that checks if the playerWhite attribute of the ChessGame is like “%anssen”

JPA’s CriteriaBuilder interface gives numerous different strategies that you should utilize to instantiate totally different sorts of CriteriaQuery objects, construct extra complicated WHERE clauses, and name database features. I clarify all of that in additional element within the Superior Hibernate course included within the Persistence Hub, and you will discover a full checklist of all strategies within the official  Javadoc.

get a HibernateCriteriaBuilder occasion

Hibernate’s HibernateCriteriaBuilder interface extends JPA’s CriteriaBuilder interface. Resulting from that, an implementation of the HibernateCriteriaBuilder helps the identical strategies, and you should utilize it in the identical manner I confirmed you within the earlier part. Along with that, the interface defines a couple of proprietary strategies to help issues like set operations and extra database features.

The principle distinction you’ll acknowledge in your code is the way you instantiate a HibernateCriteriaBuilder. One of the best ways to instantiate it’s by calling the getCriteriaBuilder methodology on Hibernate’s Session interface.

HibernateCriteriaBuilder cBuilder = em.unwrap(Session.class).getCriteriaBuilder();
CriteriaQuery<ChessGame> q = cBuilder.createQuery(ChessGame.class);
Root<ChessGame> recreation = q.from(ChessGame.class);
q.choose(recreation);
q.the place(cBuilder.like(recreation.get("playerWhite"), "%anssen"));

It’s also possible to forged a CriteriaBuilder interface to HibernateCriteriaBuilder. The forged is, in fact, not type-safe, and it depends on the implementation element that Hibernate makes use of the identical class to implement each interfaces. I, subsequently, suggest you get a Session and name the getCriteriaBuilder methodology.

HibernateCriteriaBuilder cBuilder = (HibernateCriteriaBuilder) em.getCriteriaBuilder();
CriteriaQuery<ChessGame> q = cBuilder.createQuery(ChessGame.class);
Root<ChessGame> recreation = q.from(ChessGame.class);
q.choose(recreation);
q.the place(cBuilder.like(recreation.get("playerWhite"), "%anssen"));

Options added by HibernateCriteriaBuilder

As you possibly can see within the official Javadoc of the HibernateCriteriaBuilder interface, the interface defines many strategies to construct totally different components of your question. A few of them are outlined by JPA’s CriteriaBuilder; others are Hibernate-specific options. Listed here are a few of the most fascinating additions outlined by the HibernateCriteriaBuilder interface.

Insert Into Choose statements

INSERT INTO SELECT statements are a widely known SQL function that lets you insert information chosen by a question as new information right into a database desk. Since model 6, Hibernate helps this for HQL statements, and Hibernate 6.1 will present this function as an extension to the Standards API.

Extra Expressions

The HibernateCriteriaBuilder defines a number of strategies to create Expressions that you should utilize to carry out calculations, rework or extract data and get the present date or time. Listed here are a couple of examples:

  • JpaExpression<Integer> signal(Expression<? extends Quantity> x)
    Returns 1 if the offered argument is optimistic, -1 if it’s destructive, and 0 if it’s precisely 0.
  • JpaExpression ceiling(Expression x)
    Returns the smallest integer better or equal to the offered argument.
  • JpaExpression flooring(Expression x)
    Returns the smallest largest integer smaller or equal to the offered argument.
  • JpaExpression spherical(Expression x, Integer n)
    Returns the first argument rounded to the variety of decimal digits offered because the 2nd argument.
  • JpaExpression exp(Expression x) and JpaExpression energy(Expression x, Expression y)
    Returns Euler’s quantity e raised to the facility of the offered argument or returns the first argument raised to the facility of the 2nd argument.
  • JpaExpression<Double> ln(Expression<? extends Quantity> x)
    Returns the pure logarithm of the offered argument.
  • JpaExpression<java.time.LocalDate> localDate(), JpaExpression localDateTime() and JpaExpression localTime()
    Returns the present date, date and time, or time of your database server.

Just like the strategies outlined by JPA’s CriteriaBuilder interface that outline expressions, you should utilize these strategies to outline your question’s projection or WHERE clause.

HibernateCriteriaBuilder cBuilder = em.unwrap(Session.class).getCriteriaBuilder();
CriteriaQuery<ChessGame> q = cBuilder.createQuery(ChessGame.class);
Root<ChessGame> recreation = q.from(ChessGame.class);
q.choose(recreation);
q.the place(cBuilder.equal(recreation.get("playedOn"), cBuilder.localDate()));

Listing<ChessGame> video games = em.createQuery(q).getResultList();

Hibernate then consists of these expressions within the generated SQL assertion. Your database processes them and returns the outcome. That is necessary when you’re processing the returned values and depend on timezones or different localizations. In these conditions, you should be sure that your Java utility and database use the identical settings.

11:58:59,183 DEBUG [org.hibernate.SQL] - choose c1_0.id,c1_0.playedOn,c1_0.playerBlack_id,c1_0.playerWhite_id,c1_0.model from ChessGame c1_0 the place c1_0.playedOn=current_date

Extra Predicates

Hibernate additionally gives a couple of further predicates, which you should utilize to outline your WHERE clause. Essentially the most fascinating ones are the totally different variations of ilike and notilike strategies, which offer a simple method to outline a case-insensitive LIKE or NOT LIKE expression.

HibernateCriteriaBuilder cBuilder = em.unwrap(Session.class).getCriteriaBuilder();
CriteriaQuery<ChessPlayer> q = cBuilder.createQuery(ChessPlayer.class);
Root<ChessPlayer> participant = q.from(ChessPlayer.class);
q.choose(participant);
q.the place(cBuilder.ilike(participant.get("firstName"), "%ikar%"));

Listing<ChessPlayer> video games = em.createQuery(q).getResultList();
video games.forEach(p -> log.data(p));
16:32:13,147 DEBUG [org.hibernate.SQL] - choose c1_0.id,c1_0.firstName,c1_0.lastName from ChessPlayer c1_0 the place c1_0.firstName ilike ? escape ''
16:32:13,148 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [1] as [VARCHAR] - [%ikar%]
16:32:13,168 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=4, firstName=Hikaru, lastName=Nakamura]

And when you modeled an affiliation as a java.util.Map, you should utilize the strategies isMapEmpty, isMapNotEmpty, and mapSize to examine if or what number of components that Map incorporates.

Ordering

JPA’s CriteriaBuilder lets you fetch the outcome set within the ascending or descending order of a number of entity attributes. As well as, the HibernateCriteriaBuilder additionally lets you outline the dealing with of null values and order by the results of an Expression, e.g., the results of a database perform.

Along with the asc and desc strategies outlined by JPA’s CriteriaBuilder, the HibernateCriteriaBuilder defines a 2nd model of every methodology that accepts a boolean because the 2nd methodology parameter. This boolean defines if null values shall be returned first.

HibernateCriteriaBuilder cBuilder = em.unwrap(Session.class).getCriteriaBuilder();
CriteriaQuery<ChessPlayer> q = cBuilder.createQuery(ChessPlayer.class);
Root<ChessPlayer> participant = q.from(ChessPlayer.class);
q.choose(participant);
q.orderBy(cBuilder.asc(participant.get("firstName"), true));

Listing<ChessPlayer> video games = em.createQuery(q).getResultList();
video games.forEach(p -> log.data(p));
17:24:56,003 DEBUG [org.hibernate.SQL] - choose c1_0.id,c1_0.firstName,c1_0.lastName from ChessPlayer c1_0 order by c1_0.firstName asc nulls first
17:24:56,017 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=2, firstName=Fabiano, lastName=Caruana]
17:24:56,017 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=4, firstName=Hikaru, lastName=Nakamura]
17:24:56,017 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=1, firstName=Magnus, lastName=Carlsen]
17:24:56,017 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=3, firstName=Richard, lastName=Rapport]

If you wish to outline a extra complicated ORDER BY clause based mostly on an Expression, you should name one of many kind strategies. They allow you to supply the Expression by which you need to kind the outcome, if you wish to get the end in ascending or descending order and the way you need to deal with null values.

I exploit that within the following code snippet to get the question outcome within the ascending order of the size of the participant’s first names. On this instance, it doesn’t make any sense to outline the dealing with of null values. However when you’re ordering your question outcome by a special Expression, you might present a third methodology parameter to outline the dealing with of null values.

HibernateCriteriaBuilder cBuilder = em.unwrap(Session.class).getCriteriaBuilder();
CriteriaQuery<ChessPlayer> q = cBuilder.createQuery(ChessPlayer.class);
Root<ChessPlayer> participant = q.from(ChessPlayer.class);
q.choose(participant);
q.orderBy(cBuilder.kind(cBuilder.size(participant.get("firstName")), SortOrder.ASCENDING));

Listing<ChessPlayer> video games = em.createQuery(q).getResultList();
video games.forEach(p -> log.data(p));
08:15:10,477 DEBUG [org.hibernate.SQL] - choose c1_0.id,c1_0.firstName,c1_0.lastName from ChessPlayer c1_0 order by character_length(c1_0.firstName) asc
08:15:10,493 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=1, firstName=Magnus, lastName=Carlsen]
08:15:10,493 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=4, firstName=Hikaru, lastName=Nakamura]
08:15:10,493 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=2, firstName=Fabiano, lastName=Caruana]
08:15:10,493 INFO  [com.thorben.janssen.sample.TestSample] - ChessPlayer [id=3, firstName=Richard, lastName=Rapport]

Set operations

Hibernate 6 additionally launched help for set operations for HQL and Standards queries. Utilizing the HibernateCriteriaBuilder, now you can mix the outcome units of two question statements utilizing the strategies union, unionAll, intersect, intersectAll, besides, and exceptAll.

Right here you possibly can see an instance that selects the firstName and lastName of all ChessPlayer within the 1st and the firstName and lastName of all ChessStreamer within the 2nd question and creates a union of each outcome units. When utilizing set operations, please needless to say all outcome units have to comply with the identical construction.

HibernateCriteriaBuilder cBuilder = em.unwrap(Session.class).getCriteriaBuilder();
CriteriaQuery<Tuple> qPlayer = cBuilder.createTupleQuery();
Root<ChessPlayer> participant = qPlayer.from(ChessPlayer.class);
qPlayer.multiselect(participant.get("firstName").alias("firstName"), participant.get("lastName").alias("lastName"));

CriteriaQuery<Tuple> qStreamer = cBuilder.createTupleQuery();
Root<ChessStreamer> streamer = qStreamer.from(ChessStreamer.class);
qStreamer.multiselect(streamer.get("firstName").alias("firstName"), streamer.get("lastName").alias("lastName"));

CriteriaQuery<Tuple> qPlayerAndStreamer = cBuilder.union(qPlayer, qStreamer);

Listing<Tuple> individuals = em.createQuery(qPlayerAndStreamer).getResultList();
individuals.forEach(t -> log.data(t.get("firstName") + ", " + t.get("lastName")));

As you possibly can see within the log output, Hibernate generated an SQL assertion that tells the database to use the set operation union on the two outcome units that comprise the primary and final names of all ChessPlayer and ChessStreamer.

17:43:05,857 DEBUG [org.hibernate.SQL] - choose c1_0.firstName,c1_0.lastName from ChessPlayer c1_0 union choose c2_0.firstName,c2_0.lastName from ChessStreamer c2_0
17:43:05,865 INFO  [com.thorben.janssen.sample.TestSample] - Hikaru, Nakamura
17:43:05,865 INFO  [com.thorben.janssen.sample.TestSample] - Fabiano, Caruana
17:43:05,865 INFO  [com.thorben.janssen.sample.TestSample] - Magnus, Carlsen
17:43:05,865 INFO  [com.thorben.janssen.sample.TestSample] - Richard, Rapport
17:43:05,865 INFO  [com.thorben.janssen.sample.TestSample] - Levy, Rozman
17:43:05,865 INFO  [com.thorben.janssen.sample.TestSample] - Ben, Finegold

Conclusion

As you noticed on this article, Hibernate’s HibernateCriteriaBuilder interface extends JPA’s CriteriaBuilder interface and provides strategies for Hibernate’s proprietary question options. These are:

  • Extra Expressions, like spherical and exp, that you should utilize to carry out calculations, rework or extract data and get the present date or time.
  • Extra Predicates, just like the ilike Predicate, that you should utilize to outline your WHERE clauses.
  • Strategies to outline extra complicated ORDER BY clauses, e.g. based mostly on the results of an SQL perform.
  • Set operations to mix the results of a number of queries.

By including all these proprietary options, the HibernateCriteriaBuilder interface gives you with the identical question options as Hibernate’s HQL question language, which extends JPA’s JPQL language. That lets you simply change between the two approaches and use the question definition you are feeling most snug with.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments