After migrating from Hibernate 5.6 > 6.6 version we faced performance degradation when retrieving data from cache for JPQL Queries that return a List, regardless of whether the result is empty or not.
In both Hibernate versions, we use the same version of Ehcache 3 provider. All other stuff, like Java version, DB version, etc., is the same.
We used JProfiler to detect that issue, also we enabled Hibernate statistics to be sure that the L2 cache works properly.
We started from Load Tests on our application, but in the simplest variant, we were able to recreate the issue on a simple controller just to run some JPQL Query (that returns a list) in a loop, e.g., with 1000 iterations.
In the 5.6 query.getResultList() takes 30-50 nanoseconds
In the 6.6 query.getResultList() takes 300-500 nanoseconds This is 10 times slower
For query.getSingleResult(), I have not faced the above behavior.
Is there any explanation for such performance degradation?
Let me know if you need some additional data from our side.
On the “test case template, the JProfiler shows that in 6.6, the fetch operation from the query cache for the query.getResultList() performs 2 times slower than in the 5.6 version.
In the UnitTest the overall difference ~50%, but it is difficult to isolate only the fetch from cache operation.
On my project, the difference is 10 times slower.
The steps to recreate:
Use Jcache and Ehcache
Enable L2 cache and query caching
Create a simple Entity
In the test method, persist this entity and then fetch it in the query in the loop (e.g. 10k times)
Here are my classes/methods. I hope this will help to recreate the issue:
@Entity
@Cacheable
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Getters and Setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Test
void hhh123Test() throws Exception {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
Department department = new Department();;
department.setName("name");
entityManager.persist(department);
entityManager.getTransaction().commit();
entityManager.getTransaction().begin();
long startTime = System.currentTimeMillis();
for (int i = 1; i <= 10000; i++) {
List<Department> departments = readAllDepartments(entityManager);
}
long endTime = System.currentTimeMillis();
long timeElapsed = endTime - startTime;
System.out.println("timeElapsed: " + timeElapsed);
entityManager.getTransaction().commit();
entityManager.close();
}
public List<Department> readAllDepartments(EntityManager entityManager) {
TypedQuery<Department> query = entityManager.createQuery(
"SELECT d FROM Department d",
Department.class
);
query.setHint(QueryHints.HINT_CACHEABLE, true);
query.setHint(QueryHints.HINT_CACHE_REGION, "query.Department");
return query.getResultList();
}