Thursday, May 2, 2024
HomeJavaMigrating to Hibernate 6

Migrating to Hibernate 6


Hibernate 6 has been launched for some time, and I see increasingly groups migrating their persistence layers or no less than getting ready for the migration. As so typically, the work required for the migration to Hibernate 6 depends upon your code high quality and the Hibernate model you’re at present utilizing.

For many purposes utilizing Hibernate 5, the migration is comparatively fast and simple. However you’ll have to repair and replace a number of issues should you’re nonetheless utilizing an older Hibernate model or a few of the options deprecated in Hibernate 5.

On this article, I’ll present you a very powerful steps to organize your utility for migration and what you might want to do when migrating your utility.

If you wish to get into extra particulars, see an instance migration, and study what it’s best to do after the migration, it’s best to be part of the Persistence Hub. I confirmed all of that in nice element in a current Skilled Session, and Steve Ebersole (Hibernate 6 Lead Developer) did a deep dive into Hibernate 6 in a earlier Skilled Session. As a member, yow will discover the recording of each periods within the archives.

Put together your persistence layer for Hibernate 6

Not all the modifications launched in Hibernate 6 are backward appropriate. Fortunately, you may deal with most of them earlier than you carry out the migration. That allows you to implement the required modifications step-by-step whereas nonetheless utilizing Hibernate 5. So you’ll keep away from breaking your utility, and you’ll put together the migration over a number of releases or sprints.

Replace to JPA 3

One instance of such a change is the migration to JPA 3. That model of the JPA specification didn’t carry any new options. However for authorized causes, all package deal and configuration parameter names acquired renamed from javax.persistence.* to jakarta.persistence.*.

In addition to different issues, this variation impacts the import statements for all mapping annotations and the EntityManager and breaks all persistence layers. The simplest approach to repair it’s to make use of the search and substitute characteristic in your IDE. Changing all occurrences of javax.persistence with jakarta.persistence ought to repair the compiler errors and replace your configuration.

Hibernate 6 makes use of JPA 3 by default, and you can run the search and substitute command as a part of your migration. However I like to recommend altering your challenge’s dependency from hibernate-core to hibernate-core-jakarta and performing this variation when you’re nonetheless utilizing Hibernate 5.

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-core-jakarta</artifactId>
	<model>5.6.12.Ultimate</model>
</dependency>

Change Hibernate’s Standards API

One other necessary step to organize your persistence layer for Hibernate 6 is to exchange Hibernate’s Standards API. That API has been deprecated for the reason that first launch of Hibernate 5, and also you may need already changed it. However I do know that that’s not the case for a lot of purposes.

You may simply examine should you’re nonetheless utilizing Hibernate’s proprietary Standards API by checking your deprecation warnings. In the event you discover any deprecation warning telling you that the tactic createCriteria(Class) is deprecated, you’re nonetheless utilizing Hibernate’s previous API and wish to exchange it. Sadly, you may not postpone that change. Hibernate 6 not helps the previous, proprietary Standards API.

JPA’s and Hibernate’s Standards API are related. They allow you to construct a question dynamically at runtime. Most builders use that to create a question based mostly on consumer enter or the results of some enterprise guidelines. However regardless that each APIs share the identical title and objectives, there is no such thing as a simple migration path.

Your solely choice is to take away Hibernate’s Standards API out of your persistence layer. You want to reimplement your queries utilizing JPA’s Standards API. Relying on the variety of queries you might want to substitute and their complexity, this would possibly take some time. Hibernate 5 helps each Standards APIs, and I like to recommend you substitute the previous queries one after the other earlier than you improve to Hibernate 6.

Each question is completely different and requires completely different steps emigrate it. That makes it tough to estimate how lengthy such a substitute will take and learn how to do it. However some time in the past, I wrote a information explaining learn how to migrate essentially the most generally used question options from Hibernate’s to JPA’s Standards API.

Outline SELECT clauses in your queries

For all of the question statements you may statically outline whereas implementing your utility, you’re more than likely utilizing JPQL or the Hibernate-specific extension referred to as HQL.

When utilizing HQL, Hibernate can generate the SELECT clause of your question based mostly on the FROM clause. In that case, your question selects all entity courses referenced within the FROM clause. Sadly, this modified in Hibernate 6 for all queries that be part of a number of entity courses.

In Hibernate 5, a question that joins a number of entity courses returns an Object[] or a Listing<Object[]> containing all entities joined within the FROM clause.

// question with implicit SELECT clause
Listing<Object[]> outcomes = em.createQuery("FROM Creator a JOIN a.books b").getResultList();

So, for the question assertion within the earlier code snippet, Hibernate generated a SELECT clause that referenced the Creator and the E-book entity. The generated assertion was equivalent to the next one.

--generated question utilizing Hibernate 5
SELECT a, b FROM Creator a JOIN a.books b

For a similar HQL assertion, Hibernate 6 solely generates a SELECT clause that selects the basis object of your FROM clause. On this instance, it will solely choose the Creator entity however not the E-book entity.

--generated question utilizing Hibernate 6
SELECT a FROM Creator a JOIN a.books

This modification doesn’t trigger any compiler errors however causes issues within the code that processes the question consequence. In the very best circumstances, you might have some take a look at circumstances that can discover these bugs.

However I like to recommend including a SELECT clause that references the Creator and E-book entity when you’re nonetheless utilizing Hibernate 5. This is not going to change something for Hibernate 5, however it ensures that you simply get the identical question consequence utilizing Hibernate 6 as you bought utilizing Hibernate 5.

// outline SELECT clause
Listing<Object[]> outcomes = em.createQuery("SELECT a, b FROM Creator a JOIN a.books b").getResultList();

Migrating to Hibernate 6

After implementing the modifications described within the earlier part, your migration to Hibernate 6 ought to be simple and solely require a number of configuration modifications.

Default sequence names

The era of distinctive major key values is the first factor it’s best to examine after you migrate your persistence layer to Hibernate 6. You want to apply a small change should you use database sequences and don’t specify a sequence for each entity.

Right here you may see an instance of an Creator entity that solely units the era technique to sequence however doesn’t specify which sequence Hibernate shall use. In these conditions, Hibernate makes use of a default sequence.

@Entity
public class Creator {
    
    @Id
    @GeneratedValue(technique = GenerationType.SEQUENCE)
    personal Lengthy id;
	
	...
}

In variations 4 and 5, Hibernate used 1 default sequence for your complete persistence unit. It was referred to as hibernate_sequence.

08:18:36,724 DEBUG [org.hibernate.SQL] - 
    choose
        nextval('hibernate_sequence')
08:18:36,768 DEBUG [org.hibernate.SQL] - 
    insert 
    into
        Creator
        (firstName, lastName, model, id) 
    values
        (?, ?, ?, ?)

As I confirmed in a current article, Hibernate 6 modified this strategy. By default, it makes use of a distinct sequence for each entity class. The title of that sequence consists of the entity’s title and the postfix _SEQ.

08:24:21,772 DEBUG [org.hibernate.SQL] - 
    choose
        nextval('Author_SEQ')
08:24:21,778 WARN  [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] - SQL Error: 0, SQLState: 42P01
08:24:21,779 ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] - ERROR: relation "author_seq" doesn't exist
  Place: 16

This strategy is ok, and plenty of builders will really feel extra snug with it. However it breaks current purposes as a result of the entity-specific sequences don’t exist within the database.

You will have 2 choices to resolve this drawback:

  1. Replace your database schema so as to add the brand new sequences.
  2. Add a configuration parameter to inform Hibernate to make use of the previous default sequences.

When engaged on the migration, I like to recommend utilizing the 2nd strategy. It’s the quickest and best approach to resolve the issue, and you’ll nonetheless add the brand new sequences in a future launch.

You may inform Hibernate to make use of the previous default sequences by configuring the property hibernate.id.db_structure_naming_strategy in your persistence.xml. Setting this worth to single will get you the default sequences utilized by Hibernate <5.3. And the configuration worth legacy will get you the default sequence names utilized by Hibernate >=5.3.

<persistence>
    <persistence-unit title="my-persistence-unit">
        ...
        <properties>
            <! – guarantee backward compatibility – >
            <property title="hibernate.id.db_structure_naming_strategy" worth="legacy" />

			...
        </properties>
    </persistence-unit>
</persistence>

I defined all of this in additional element in my information to the sequence naming methods utilized by Hibernate 6.

Instantaneous and Length mappings

One other change you could simply overlook till the deployment of your migrated persistence layer fails is the mapping of Instantaneous and Length.

When Hibernate launched the proprietary mapping for these sorts in model 5, it mapped Instantaneous to SqlType.TIMESTAMP and Length to Sorts.BIGINT. Hibernate 6 modifications this mapping. It now maps Instantaneous to SqlType.TIMESTAMP_UTC and Length to SqlType.INTERVAL_SECOND.

These new mappings appear to be a greater match than the previous ones. So, it’s good that they modified it in Hibernate 6. However it nonetheless breaks the desk mapping of current purposes. In the event you run into that drawback, you may set the configuration property hibernate.kind.preferred_instant_jdbc_type to TIMESTAMP and hibernate.kind.preferred_duration_jdbc_type to BIGINT.

<persistence>
    <persistence-unit title="my-persistence-unit">
        <properties>
            <! – guarantee backward compatibility – >
            <property title="hibernate.kind.preferred_duration_jdbc_type" worth="BIGINT" />
            <property title="hibernate.kind.preferred_instant_jdbc_type" worth="TIMESTAMP" />

			...
        </properties>
    </persistence-unit>
</persistence>

These are 2 new configuration parameters launched in Hibernate 6. Each are marked as incubating. Which means that they could change sooner or later. I, due to this fact, suggest you utilize them throughout your migration to Hibernate 6 and regulate your desk mannequin quickly after in order that it matches Hibernate’s new customary mapping.

New logging classes

In the event you learn a few of my earlier articles about Hibernate 6, it’s best to know that the Hibernate group rewrote the code that generates the question statements. One of many negative effects of that change was a small change within the logging configuration.

In Hibernate 5, you might want to activate hint logging for the class org.hibernate.kind.descriptor.sql to log all bind parameter values and the values extracted from the consequence set.

<Configuration>
  ...
  <Loggers>
    <Logger title="org.hibernate.SQL" degree="debug"/>
    <Logger title="org.hibernate.kind.descriptor.sql" degree="hint"/>
    ...
  </Loggers>
</Configuration>
19:49:20,330 DEBUG [org.hibernate.SQL] - 
    choose
        this_.id as id1_0_1_,
        this_.firstName as firstnam2_0_1_,
        this_.lastName as lastname3_0_1_,
        this_.model as version4_0_1_,
        books3_.authors_id as authors_2_2_,
        book1_.id as books_id1_2_,
        book1_.id as id1_1_0_,
        book1_.publisher_id as publishe4_1_0_,
        book1_.title as title2_1_0_,
        book1_.model as version3_1_0_ 
    from
        Creator this_ 
    internal be part of
        Book_Author books3_ 
            on this_.id=books3_.authors_id 
    internal be part of
        E-book book1_ 
            on books3_.books_id=book1_.id 
    the place
        book1_.title like ?
19:49:20,342 TRACE [org.hibernate.type.descriptor.sql.BasicBinder] - binding parameter [1] as [VARCHAR] - [%Hibernate%]
19:49:20,355 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] - extracted worth ([id1_1_0_] : [BIGINT]) - [1]
19:49:20,355 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] - extracted worth ([id1_0_1_] : [BIGINT]) - [1]
19:49:20,359 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] - extracted worth ([publishe4_1_0_] : [BIGINT]) - [1]
19:49:20,359 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] - extracted worth ([title2_1_0_] : [VARCHAR]) - [Hibernate]
19:49:20,360 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] - extracted worth ([version3_1_0_] : [INTEGER]) - [0]
19:49:20,361 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] - extracted worth ([firstnam2_0_1_] : [VARCHAR]) - [Max]
19:49:20,361 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] - extracted worth ([lastname3_0_1_] : [VARCHAR]) - [WroteABook]
19:49:20,361 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] - extracted worth ([version4_0_1_] : [INTEGER]) - [0]
19:49:20,361 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] - extracted worth ([id1_1_0_] : [BIGINT]) - [1]
19:49:20,362 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] - extracted worth ([id1_0_1_] : [BIGINT]) - [3]
19:49:20,362 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] - extracted worth ([firstnam2_0_1_] : [VARCHAR]) - [Paul]
19:49:20,362 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] - extracted worth ([lastname3_0_1_] : [VARCHAR]) - [WritesALot]
19:49:20,362 TRACE [org.hibernate.type.descriptor.sql.BasicExtractor] - extracted worth ([version4_0_1_] : [INTEGER]) - [0]

Hibernate 6 launched a separate logging class for the bind parameter values. You may activate the logging of these values by configuring hint logging for the org.hibernate.orm.jdbc.bind class.

<Configuration>
  ...
  <Loggers>
    <Logger title="org.hibernate.SQL" degree="debug"/>
    <Logger title="org.hibernate.orm.jdbc.bind" degree="hint"/>
    ...
  </Loggers>
</Configuration>
19:52:11,012 DEBUG [org.hibernate.SQL] - 
    choose
        a1_0.id,
        a1_0.firstName,
        a1_0.lastName,
        a1_0.model 
    from
        Creator a1_0 
    be part of
        (Book_Author b1_0 
    be part of
        E-book b1_1 
            on b1_1.id=b1_0.books_id) 
                on a1_0.id=b1_0.authors_id 
        the place
            b1_1.title like ? escape ''
19:52:11,022 TRACE [org.hibernate.orm.jdbc.bind] - binding parameter [1] as [VARCHAR] - [%Hibernate%]

Conclusion

Hibernate 6 launched a number of modifications that break backward compatibility. Not all of them require big modifications to the code of your persistence layer. It is perhaps sufficient to solely add a number of configuration parameters to maintain the previous habits.

However 2 modifications would require particular consideration. These are the replace to JPA 3 and the elimination of Hibernate’s deprecated Standards API. I like to recommend you deal with each when you’re nonetheless utilizing Hibernate 5.

The replace to JPA 3 requires you to vary the configuration parameter’s names, and the import statements of all courses, interfaces and annotations outlined by the specification. However don’t fear. This normally sounds worse than it really is. I migrated a number of tasks by doing a easy search and substitute operation in my IDE. This was normally finished in a couple of minutes.

The elimination of Hibernate’s deprecated Standards API will trigger greater points. You’ll need to rewrite all queries that use the previous API. I like to recommend you try this when you’re nonetheless utilizing Hibernate 5. It nonetheless helps Hibernate’s previous Standards API and JPA’s Standards API. So, you may substitute one question after the opposite with out breaking your utility.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments