Hibernate Application Performance Tuning

Imagine having a tool that can automatically detect JPA and Hibernate performance issues. Hypersistence Optimizer is that tool!

Introduction

Because performance tuning is very important when it comes to developing a data access layer, in this article, I’m going to show you how you can optimize the famous Hibernate Caveat Emptor application using Hypersistence Optimizer.

The Caveat Emptor application was created by Christian Bauer and Gavin King for the Hibernate in Action book they published in 2004 and was included in both the first and second editions of the well-known Java Persistence with Hibernate book.

Getting the Caveat Emptor application

You can get all three versions of the Caveat Emptor application from the Java Persistence with Hibernate website.

For this article, I chose the 2006 Caveat Emptor native Hibernate version and wanted to see if I could run Hypersistence Optimizer to detect performance issues.

Now, since Hypersistence Optimizer requires at least Hibernate 3.3, I had to make several dependency upgrades in order to run my test:

  • hibernate3.jarhibernate-core-3.3.0.GA.jar
  • commons-collections-2.1.1.jarcommons-collections-3.2.2.jar
  • testng-5.4-jdk15.jartestng-6.14.3.jar
  • jboss-common.jarslf4j-api-1.6.1.jar and slf4j-log4j12-1.6.1.jar

And I added the following dependencies:

  • javax-jpa-1.0.jar
  • hypersistence-optimizer-2.4.0.jar

And, that’s it!

Adding a Hibernate application performance tuning test

Now, to analyze the performance issues of the Caveat Emptor application, I created the following integration test:

public class PerformanceTuningTest 
        extends HibernateIntegrationTest {

    protected void prepareSettings() {
        dataSetLocation = "auction/test/basedata.xml";
        beforeTestOperations.add(
            DatabaseOperation.CLEAN_INSERT
        );
    }

    @Test(groups = "integration-hibernate")
    public void checkPerformanceIssues() {
        List<Event> events = new HypersistenceOptimizer(
            new HibernateConfig(
                HibernateUtil.getSessionFactory()
            )
        ).getEvents();
        Assert.assertTrue(events.isEmpty());
    }
}

And, when running this integration test, I got the following outcome:

Hypersistence Optimizer - 47 issues were found: 

- 1 BLOCKER, 
- 31 CRITICAL, 
- 5 MAJOR, 
- 10 MINOR

Of all those issues, 40 are mapping-related issues:

And 7 are configuration-related problems:

IdentityGeneratorEvent

The IdentityGeneratorEvent is reported as follows:

CRITICAL - IdentityGeneratorEvent - The [id] identifier attribute 
in the [ItemEntity] entity uses the [IdentityGenerator] strategy, 
which prevents Hibernate from enabling JDBC batch inserts. 

Consider using the SEQUENCE identifier strategy instead.

If we go to the ItemEtity, we can see that, indeed, the id is mapped like this:

<!ENTITY idgenerator "identity">

<id name="id" type="long" column="ITEM_ID" node="@id">
    <generator class="&idgenerator;"/>
</id>

Using the IDENTITY generator is not recommended if the underlying database supports sequences since you won’t be able to benefit from automatic batch inserts.

EagerFetchingEvent

The EagerFetchingEvent is reported like this:

CRITICAL - EagerFetchingEvent - The [deliveryAddress] attribute 
in the [Shipment] entity uses eager fetching.

Consider using lazy fetching, which not only that is more efficient, 
but is way more flexible when it comes to fetching data.

The deliveryAddress association in the Shipment entity is mapped like this:

<many-to-one name="deliveryAddress"
    class="AddressEntity"
    column="DELIVERY_ADDRESS_ID"
    not-null="true"
    update="false"
    fetch="join"
    foreign-key="FK_DELIVERY_ADDRESS_ID"
/>

Because the fetch attribute is set to the join strategy, this association is going to be fetched eagerly every time the Shipment is loaded, even when we don’t need this association.

Using eager fetching mappings is not recommended because this can lead to very serious performance issues. Not only that the associations will be fetched even when they are not needed, but they can cause N+1 query issues as well.

For more details about this topic, check out this article.

BidirectionalSynchronizationEvent

The BidirectionalSynchronizationEvent is reported like this:

CRITICAL - BidirectionalSynchronizationEvent - The [categorizedItems] 
bidirectional association in the [Item] entity requires 
both ends to be synchronized. 

Consider adding the [addCategorizedItem(CategorizedItem categorizedItem)] and
[removeCategorizedItem(CategorizedItem categorizedItem)] synchronization methods.

Indeed, if you navigate the Item, you’ll see that it doesn’t contain the addCategorizedItem and removeCategorizedItem methods.

Not synchronizing both sides of a bidirectional association is not recommended since Hibernate doesn’t guarantee that the entity state transitions will propagate properly.

For more details about this topic, check out this article.

Cool, right?

Online Workshops

If you enjoyed this article, I bet you are going to love my upcoming 4-day Online Workshop!

Conclusion

Hypersistence Optimizer is a very versatile tool. Not only that it works with Spring Boot, Spring, Jakarta EE, Java EE, Play, or other frameworks, but you can use it even with legacy applications that are using very old Hibernate versions.

If you want to speed up your data access layer, Hypersistence Optimizer will surely help you achieve your goal. And, not only will it detect current issues, but you can use it to prevent those issues from ever happening again via an automated integration test that checks that no issue is being detected.

Transactions and Concurrency Control eBook

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

4 day training with a Java Champion 🏆 29th Nov - 2nd Dec