Hypersistence Optimizer initial release
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
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.

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:
Imagine having a tool that can automatically detect if you are using @Java Persistence and #Hibernate properly.
— Vlad Mihalcea (@vlad_mihalcea) February 26, 2019
No more performance issues, no more silly mistakes that can cost you a lot of time and money.
Soon, you can have this tool. Stay tuned for more details! pic.twitter.com/CRYx4tVPif
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:

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()
)
);
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
@OneToOneassociation is also fetched eagerly unless bytecode enhancement is used. Check out this article for more details. - Only if we are using
@MapsIdon the client-side of the@OneToOneassociation 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
@ManyToManyassociation works better withSetcollections. Using aListwill 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!
If you’re curious about how many issues your current project has, you can download and install the trial version.
There are also plenty of examples in the hypersitence-optimizer GitHub repository you can use as well,
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.






