Are you struggling with performance issues in your Spring, Jakarta EE, or Java EE application?
What if there were a tool that could automatically detect what caused performance issues in your JPA and Hibernate data access layer?
Wouldn’t it be awesome to have such a tool to watch your application and prevent performance issues during development, long before they affect production systems?
Well,
Hypersistence Optimizer
is that tool!
And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, Micronaut, or Play Framework.
So, rather than fixing performance issues in your production system on a Saturday night, you are better off using Hypersistence Optimizer to help you prevent those issues so that you can spend your time on the things that you love!
Introduction
In this article, we are going to see how Spring load-time weaving works so that you can apply the Hibernate bytecode enhancement mechanism at runtime.
Traditionally, the bytecode enhancement mechanism is applied when the project is built using a Maven or Gradle plugin. For more details about the build-time approach, check out
this article
.
Domain Model
Let’s consider we have the following
Attachment
entity that has a
content
property that is mapped as follows:
@Column(columnDefinition="BLOB")
@Basic(fetch = FetchType.LAZY)
private byte[] content;
The
content
property is using the
FetchType.LAZY
fetching strategy, but loading entity attributes lazily cannot be done on POJO entities, so we need the Hibernate bytecode enhancement mechanism to achieve this goal.
Hibernate Bytecode Enhancement Mechanism
The Hibernate bytecode enhancement mechanism allows us to change the bytecode of a JPA entity so that we can intercept the getter and the setter method calls in order to:
load attributes lazily
record entity modifications
Most often, the bytecode enhancement mechanism is configured via a Maven or Gradle plugin, which enhances the entity classes when the project is built, as explained in
this article
.
However, this is not the only approach we can use since we can also instrument the JPA entity classes at runtime when the project is bootstrapped.
Spring Data JPA Load-Time Weaving
To enable the runtime bytecode enhancement mechanism when bootstrapping a Spring application, you have to add the
@EnableLoadTimeWeaving
annotation to your Spring configuration:
@EnableLoadTimeWeaving
public class BytecodeEnhancementConfiguration {
Now, we need to provide the Spring Instrumentation library to the JVM via the
javaagent
argument. If you’re using Windows, you could do it like this:
-javaagent:%M2_REPOSITORY%\org\springframework\spring-instrument\%SPRING_VERSION%\spring-instrument-%SPRING_VERSION%.jar
That’s it!
Testing Time
When loading an
Attachment
entity and accessing the
content
property:
Attachment book = attachmentRepository.findById(1L).orElseThrow(null);
LOGGER.debug("Fetched book: {}", book.getName());
assertArrayEquals(readBytes(bookFilePath), book.getContent());
We can see that the
content
column is not fetched by the first SQL query, being fetched on demand in a secondary SQL query:
SELECT
a.id,
a.media_type,
a.name
attachment a
WHERE
a.id = 1
-- Fetched book: High-Performance Java Persistence
SELECT
a.content
attachment a
WHERE
a.id = 1
Awesome, right?
Conclusion
Using the Spring load-time weaving mechanism can be very useful if we want to enhance the JPA entity classes without having access to the project build.
The
@EnableLoadTimeWeaving
annotation tells Spring to register a
LoadTimeWeaver
bean that will be provided to the JPA
EntityManagerFactory
to enhance the JPA entities that get loaded, giving us the opportunity to enable the bytecode enhancement lazy loading or dirty tracking mechanisms.
It doesn’t work for me, it says I need special class loader
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘loadTimeWeaver’ defined in class path resource [org/springframework/context/annotation/LoadTimeWeavingConfiguration.class]:
I tried to run the example as well but got this problem:
Caused by: java.lang.LinkageError: loader 'app' attempted duplicate class definition for jakarta.persistence.TemporalType. (jakarta.persistence.TemporalType is in unnamed module of loader 'app')
That is caused by HHH-18108 which was fixed in 6.5.3, but I don’t see it on Maven Central. I will have to check it again after that release to see if the test is working. If it’s not, it’s because of some Spring or Hibernate bug.