Hibernate 5.3 is available for a little more than 3 months now, and last week, the team released the 3rd maintenance release. So, it’s about time to take a closer look at the new version.
In addition to more than 100 bug fixes, Hibernate 5.3 includes a few features and improvements that might motivate you to update your current project.
Improved Memory consumption
Let’s start with probably the best reason to update to Hibernate 5.3: It consumes less memory than the previous versions.
The improvement was triggered by an interesting discussion in the Hibernate forum. A user reported that he tried to migrate his application from Hibernate 3.6 to 5.3. During the migration, he recognized that the memory consumption of Hibernate’s SessionFactory went up to 950MB.
The issue was caused by the size and number of EntityLoaders that Hibernate instantiated. It was fixed in 2 steps:
- HHH-12556 – Similar EntityLoaders now share some internal data structures. This reduced the memory consumption of the application by ~50%. The fix was backported to Hibernate 5.2.18. So, if you’re using Hibernate 5.2 and don’t want to upgrade to 5.3, you should at least update to 5.2.18.
- HHH-12558 – Hibernate supports a bunch of different lock modes with specific loaders. In the past, it instantiated all loaders eagerly. Hibernate 5.3 only instantiates the 2 most common ones and loads all others lazily.
At the end of the discussion, the user who reported the issue wrote that the improved Hibernate version only used ~ 250MB. So, for his application, these two changes reduced the memory consumption by ~70%.
I obviously can’t promise that it will be equally effective for your project. However, the reported improvement is so enormous that you should at least give it a try.
JPA 2.2 compliance
Hibernate 5.3.0 is the first version that’s fully compliant with JPA 2.2. However, because the support for all the interesting features was already added in Hibernate 5.0, 5.1 and 5.2, and I already wrote extensive tutorials about all of them, I will not dive any deeper into this topic.
If you’re not already familiar with JPA 2.2, you can read more about it in the following articles:
- How To Map The Date And Time API with JPA 2.2
- How to get query results as a Stream with Hibernate 5.2 (please note that Hibernate now also offers the getResultStream method)
- JPA 2.2’s new getResultStream() method and how you should NOT use it
- JPA 2.2 Introduces @Repeatable Annotations
Small Improvements
Learn more about primary keys
Watch this video on YouTube](https://youtu.be/qn9SbW44rQ8)
Follow me on YouTube to not miss any new videos.
Implicit ID generator definitions
If you generate your primary key values, you’re probably aware of Hibernate’s @SequenceGenerator and @TableGenerator annotations. You can use them to define which sequence or table Hibernate shall use to generate the primary key value.
Here is a typical example of a primary key mapping that tells Hibernate to use the database sequence authorseq_ to generate the unique primary key values.
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "author_seq")
@SequenceGenerator(name="author_seq", sequenceName = "author_seq")
private Long id;
...
}
The mapping definition consists of 2 parts:
- The @GeneratedValue annotation defines which generation strategy Hibernate shall use and references a custom generator.
- The @SquenceGenerator annotation specifies the generator and tells Hibernate the name of the sequence you want to use.
As you can see in the code snippet, I use the same name for the generator and the database sequence. That’s a pretty common and verbose mapping.
If your mapping looks the same, I have good news for you: You no longer need to define the generator if your database sequence or table has the same name as your @SequenceGenerator or @TableGenerator.
That enables me to shorten the previous mapping and to remove the @SequenceGenerator definition.
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "author_seq")
private Long id;
...
}
Support for MySQL 8 SKIP LOCKED and NOWAIT
MySQL 8.0.1 added the SKIP LOCKED and NOWAIT feature to provide different options to handle locked rows during read-operations. The MySQL Server team explained them in great detail on their blog: MySQL 8.0.1: Using SKIP LOCKED and NOWAIT to handle hot rows.
Here’s a quick description of both features:
- SKIP LOCKED enables you to perform a non-deterministic read that skips all locked rows. That means that locked rows are missing in your result set.
- If you require a deterministic read but don’t want to wait for the locks to be released, you can use the NOWAIT feature. It causes your query to fail immediately if any records in your result set are locked.
The MySQL8Dialect included in Hibernate 5.3 supports both these features so that you can use them in your queries.
Learn more about AttributeConverter
Watch this video on YouTube](https://youtu.be/TIeeRif4ocg)
Follow me on YouTube to not miss any new videos.
Apply AttributeConverter when calling a function
AttributeConverters provide a standardized, easy way to define the mapping of a Java type. You can either use them to add support for unsupported classes or to customize the mapping of an already supported Java type.
The following code snippet shows an example from my JPA Tip: How to map a Duration attribute. JPA 2.2 doesn’t provide a mapping for _java.time.Duration_ objects. If you want to map such an object, you can use this AttributeConverter to map it to a Long.
@Converter(autoApply = true)
public class DurationConverter implements AttributeConverter<Duration, Long> {
Logger log = Logger.getLogger(DurationConverter.class.getSimpleName());
@Override
public Long convertToDatabaseColumn(Duration attribute) {
log.info("Convert to Long");
return attribute.toNanos();
}
@Override
public Duration convertToEntityAttribute(Long duration) {
log.info("Convert to Duration");
return Duration.of(duration, ChronoUnit.NANOS);
}
}
After you applied the AttributeConverter to an attribute or automatically applied them to all attributes of a specific type, it gets transparently used:
- during all lifecycle state transitions,
- to map the result of a query and
- when used in path expressions in a JPQL or CriteriaQuery.
However, for whatever reason, the usage of converted attribute values as function parameters is explicitly undefined.
Previous Hibernate versions didn’t convert the attribute value if you referenced it as a function parameter. This changed with Hibernate 5.3.2. It now converts the attribute value before it provides it as a function parameter.
Support for Java 9 and preparations for Java 11
Beginning with Hibernate 5.3.0, all Hibernate modules specify a Java 9 module name following the pattern _org.hibernate.orm.${module-name}_, e.g., the hibernate-core module defines the name _org.hibernate.orm.core._
The Hibernate team also updated the dependencies, prepared the build process and run their test suite with the latest JDK 11 build. So, we can hope for a smooth transition to JDK11.
Conclusion
Should you update to Hibernate 5.3?
You probably already know my answer. It’s a clear yes! Especially the improved memory consumption is a great reason to do the update.
However, please keep in mind that every update has its risks and that you obviously shouldn’t update any project dependency without testing it carefully.
So, what about you? Did you already update your application to use Hibernate 5.3 or will you update it in the near future?