Hypersistence Optimizer initial release

(Last Updated On: April 15, 2019)
Imagine having a tool that can automatically detect if you are using JPA and Hibernate properly. Hypersistence Optimizer is that tool!

Introduction

At the end of 2018, I got this idea of writing a tool which can automatically detect JPA and Hibernate issues by scanning your data access layer and provide you optimization tips.

At the beginning of February, Thodoris Chaikalis surprised me with this Facebook comment which reinforced the idea that having such a tool would be really awesome for Java developers working with JPA and Hibernate.

Facebook Comment about a tool like Hypersistence Optimizer

At the end of February, I got some time off, and I started working on it, and the reaction on social media exceeded my expectations:

Today, I’m happy to announce to you that the initial version is ready.

Hypersistence Optimizer has finally arrived!

Testing time

Let’s assume our application defines four JPA entities: Post, PostDetails, PostComment, and Tag which are associated as follows:

Hypersistence Optimizer All Table Relationship Entities

JPA entity mapping

The Post entity is mapped like this:

@Entity
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @OneToMany(
        mappedBy = "post",
        cascade = CascadeType.ALL,
        orphanRemoval = true
    )
    private List<PostComment> comments = new ArrayList<>();

    @OneToOne(
        mappedBy = "post",
        cascade = CascadeType.ALL,
        orphanRemoval = true,
        fetch = FetchType.LAZY
    )
    private PostDetails details;

    @ManyToMany
    @JoinTable(
        name = "post_tag",
        joinColumns = @JoinColumn(name = "post_id"),
        inverseJoinColumns = @JoinColumn(name = "tag_id")
    )
    private List<Tag> tags = new ArrayList<>();

    //Getters and setters omitted for brevity

    public void addComment(PostComment comment) {
        comments.add(comment);
        comment.setPost(this);
    }

    public void removeComment(PostComment comment) {
        comments.remove(comment);
        comment.setPost(null);
    }

    public void addDetails(PostDetails details) {
        this.details = details;
        details.setPost(this);
    }

    public void removeDetails() {
        this.details.setPost(null);
        this.details = null;
    }
}

The PostDetails has a one-to-one relationship with the parent Post entity and is mapped as follows:

@Entity
@Table(name = "post_details")
public class PostDetails {

    @Id
    @GeneratedValue
    private Long id;

    @Column(name = "created_on")
    private Date createdOn;

    @Column(name = "created_by")
    private String createdBy;
    
    @OneToOne
    private Post post;

    //Getters and setters omitted for brevity
}

The PostComment has a many-to-one relationship with the parent Post entity and is mapped as illustrated by the following code snippet:

@Entity
@Table(name = "post_comment")
public class PostComment {

    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne
    private Post post;

    private String review;

    //Getters and setters omitted for brevity
}

The Tag entity is mapped like this:

@Entity
@Table(name = "tag")
public class Tag {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    //Getters and setters omitted for brevity    
}

Hypersistence Optimizer Configuration

Now let’s instantiate the HypersistenceOptimizer object by passing it the current Hibernate SessionFactory and call the init method:

new HypersistenceOptimizer(
    new HibernateConfig(
        sessionFactory()
    )
).init();

When checking the application log, we can see the following optimization tips:

ERROR [main]: 
Hypersistence Optimizer - 
CRITICAL - 
EagerFetchingEvent - 
The [post] attribute in the [io.hypersistence.optimizer.hibernate.mapping.association.PostDetails] entity 
uses eager fetching. 

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

ERROR [main]: 
Hypersistence Optimizer - 
CRITICAL - 
OneToOneWithoutMapsIdEvent - 
The [post] one-to-one association in the [io.hypersistence.optimizer.hibernate.mapping.association.PostDetails] entity 
is using a separate Foreign Key to reference the parent record.
 
Consider using @MapsId so that the identifier is shared with the parent row.

ERROR [main]: Hypersistence Optimizer - 
CRITICAL - 
EagerFetchingEvent - 
The [post] attribute in the [io.hypersistence.optimizer.hibernate.mapping.association.PostComment] entity 
uses eager fetching. 

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

ERROR [main]: Hypersistence Optimizer - 
CRITICAL - 
ManyToManyListEvent - 
The [tags] many-to-many association in the [io.hypersistence.optimizer.hibernate.mapping.association.Post] entity 
is using a List so it does not render very efficient SQL statements. 

Consider using a Set instead.

ERROR [main]: Hypersistence Optimizer - 
CRITICAL - 
OneToOneParentSideEvent - 
The [details] one-to-one association in the [io.hypersistence.optimizer.hibernate.mapping.association.Post] entity 
is mapped as the parent-side of this relationship. 

The parent-side is fetched eagerly unless bytecode enhancement lazy loading is employed, 
and even then, there are limitations to how the one-to-one association can be mapped. 

You should consider mapping the child-side only with @MapsId 
so that you can always reference the parent entity while having a reference to the child.

How cool is that, right?

Now let’s review these optimization tips:

  • As I explained in this article, EAGER fetching is very bad for performance since it can lead to N+1 query issues, and the EAGER fetching strategy cannot be overridden on a per query basis.
  • The parent-side of the @OneToOne association is also fetched eagerly unless bytecode enhancement is used. Check out this article for more details.
  • Only if we are using @MapsId on the client side of the @OneToOne association will the JPA association map a real one-to-one table relationship. Otherwise, we get a one-to-many table relationship where the Foreign Key is unique.
  • The @ManyToMany association works better with Set collections. Using a List will generate more queries than necessary. For more details, check out this article.

If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.

Stay tuned for more!

To play with Hypersistece Optimizer, go the hypersitence-optimizer GitHub repository and use the current examples. You can download and install the trial version to get the hypersistence-optimizer dependency.

Now, this is just the initial release. The next releases are going to add support for Hibernate configuration, so stay tuned for more awesome performance optimizations.

Download free ebook sample

Newsletter logo
10 000 readers have found this blog worth following!

If you subscribe to my newsletter, you'll get:
  • A free sample of my Video Course about running Integration tests at warp-speed using Docker and tmpfs
  • 3 chapters from my book, High-Performance Java Persistence,
  • a 10% discount coupon for my book.

6 Comments on “Hypersistence Optimizer initial release

  1. Hi Vlad,
    I am using Spring and Gradle for my project. I did import the optimizer as described in the StackOverflow example from above.
    Now how should I receive the inspection errors? On a application build or run?

    • You need to call the init method on the HypersistenceOptimizer object. Check out the GitHub repository for 2 examples with both Spring JPA and Spring with Hibernate.

  2. Sir,if possible please make a video on how to install and how to use.it will be much helpful and easy to understand.

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.