Spring Load-Time Weaving
Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Wouldn’t that be just awesome?
Well, Hypersistence Optimizer is that tool! And it works with Spring Boot, Spring Framework, Jakarta EE, Java EE, Quarkus, or Play Framework.
So, enjoy spending your time on the things you love rather than fixing performance issues in your production system on a Saturday night!
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:
@Lob @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 FROM attachment a WHERE a.id = 1 -- Fetched book: High-Performance Java Persistence SELECT a.content FROM attachment a WHERE a.id = 1
Awesome, right?
I'm running an online workshop on the 20-21 and 23-24 of November about High-Performance Java Persistence.
If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.
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.