I've upgraded an application using Hibernate v5 to v6, but after doing this a query has got very slow - >10x slower.
Take the following simple application that persists 500,000 MyEntity
s to a new in-memory database, retrieves them and prints performance metrics. It can be run with either Hibernate v5 or v6, as per the commented out section in pom.xml
:
MyApplication.java
:
package com.me;
import java.text.MessageFormat;
import java.time.Duration;
import java.time.Instant;
import java.util.Properties;
import java.util.stream.IntStream;
import org.h2.Driver;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.schema.Action;
public class MyApplication {
public static void main(final String[] args) {
Instant start = Instant.now();
final Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.connection.url", "jdbc:h2:mem:");
jpaProperties.put("jakarta.persistence.jdbc.driver", Driver.class.getName());
jpaProperties.put("jakarta.persistence.schema-generation.database.action", Action.CREATE);
try (Session session = new Configuration().addAnnotatedClass(MyEntity.class).addProperties(jpaProperties)
.buildSessionFactory().openSession()) {
session.beginTransaction();
IntStream.range(0, 500000).mapToObj(i -> new MyEntity()).forEach(session::persist);
printTiming(start, "Setup / Publish");
start = Instant.now();
session.createQuery("FROM MyEntity", MyEntity.class).getResultList();
printTiming(start, "Get");
}
}
private static void printTiming(final Instant startTime, final String label) {
System.out.println(MessageFormat.format("{0} took {1}", label, Duration.between(startTime, Instant.now())));
}
}
MyEntity.java
:
package com.me;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class MyEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
protected Long id;
}
pom.xml
:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<groupId>com.me</groupId>
<modelVersion>4.0.0</modelVersion>
<artifactId>hibernate-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.1.214</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.1.5.Final</version>
</dependency>
<!--<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core-jakarta</artifactId>
<version>5.6.14.Final</version>
</dependency>-->
</dependencies>
</project>
hibernate-core
6.1.5
:
Setup / Publish took PT2.6288547S
Get took PT35.0881315S
hibernate-core-jakarta
5.6.14.Final
:
Setup / Publish took PT3.486003S
Get took PT2.3955987S
I've profiled it I can see Hibernate 6 is spending ~90% of the time in org.hibernate.sql.results.spi.ListResultsConsumer.withDuplicationCheck()
- some kind of results post-processing.
CodePudding user response:
It is possible that the performance degradation you are experiencing is due to the changes made in Hibernate v6. Hibernate v6 introduces a number of changes and improvements over v5, some of which may affect performance in various ways. It is difficult to say exactly what is causing the performance degradation in your specific case without further information, but you may want to try running your application with Hibernate v5 again to see if the performance improves.
CodePudding user response:
One potential cause of the slower performance could be changes to the way Hibernate manages sessions and transactions. In Hibernate 6, the Session
interface has been replaced with the org.hibernate.Session
class, and the Transaction
interface has been replaced with the org.hibernate.Transaction
class. This change may have resulted in less optimized performance for your specific use case.
Another potential cause of the slower performance could be changes to the way Hibernate manages entity mapping and loading. Hibernate 6 introduces a new BootstrapServiceRegistry
class, which is responsible for managing the mapping of entities to database tables and the loading of entities from the database. This new class may not be as optimized as the previous implementation, resulting in slower performance in your specific use case.
CodePudding user response:
It is possible to find why the performance of a query is slower after upgrading from Hibernate v5 to v6. One way to investigate this issue is to enable logging and examine the log messages to see what Hibernate is doing during the query execution.
To enable logging, you can add the following properties to the jpaProperties object in your code:
jpaProperties.put("hibernate.show_sql", "true");
jpaProperties.put("hibernate.format_sql", "true");
jpaProperties.put("hibernate.type", "trace");
This will enable SQL logging and formatting, as well as logging of all Hibernate events.
After running the query again, you can examine the log messages to see what Hibernate is doing during the query execution. This can help identify the cause of the slower performance and suggest potential solutions.
For example, if the log messages show that Hibernate is performing additional operations or taking longer to execute certain operations compared to Hibernate v5, this could be the cause of the slower performance. In this case, further investigation may be needed to understand why these additional operations are being performed and how they can be optimized.
It is also possible that the slower performance is due to changes in the underlying database engine or other external factors. In this case, further investigation and analysis may be needed to determine the cause and potential solutions.
CodePudding user response:
It looks like you are using the jakarta.persistence package in your code, which is part of the Jakarta EE platform. This is not supported by Hibernate 5.x. You will need to use the javax.persistence package instead.
You can update your code to use the javax.persistence package by changing the following lines:
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
to:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
Once you have made this change, your code should work with Hibernate 5.x.
You may also need to update the jpaProperties object in your code to use the javax.persistence package. You can do this by changing the following line:
jpaProperties.put("jakarta.persistence.jdbc.driver", Driver.class.getName());
to:
jpaProperties.put("javax.persistence.jdbc.driver", Driver.class.getName());
and by changing this line:
jpaProperties.put("jakarta.persistence.schema-generation.database.action", Action.CREATE);
to:
jpaProperties.put("javax.persistence.schema-generation.database.action", Action.CREATE);
Once you have made these changes, your code should work with Hibernate 6.x.