NoClassDefFoundError SessionFactoryImplementor in Hibernate

NoClassDefFoundError: org/hibernate/engine/SessionFactoryImplementor means a class built for Hibernate 3, usually Spring’s orm.hibernate3 transaction manager, runs with Hibernate 4 or newer. Find the jar, switch to the matching Spring package or align the Hibernate modules, and unwrap the SPI interface on Hibernate 7.

NoClassDefFoundError: org/hibernate/engine/SessionFactoryImplementor means that a library compiled against Hibernate 3 runs with Hibernate 4 or newer. The interface SessionFactoryImplementor is the internal side of the SessionFactory, the object that creates Hibernate sessions. Hibernate 4.0 moved it from org.hibernate.engine to org.hibernate.engine.spi, and the old name was never brought back.

We meet the error after a Hibernate upgrade that leaves an old piece behind. In most projects, the old piece is either Spring’s orm.hibernate3 package (HibernateTransactionManager, LocalSessionFactoryBean) or an old org.hibernate module next to a newer hibernate-core.

The following example shows the error for Spring 3.2.18 and Hibernate 7.4.11 on Java 25.

java.lang.NoClassDefFoundError: org/hibernate/engine/SessionFactoryImplementor
	at org.springframework.orm.hibernate3.SessionFactoryUtils.getDataSource(SessionFactoryUtils.java:123)
	at org.springframework.orm.hibernate3.HibernateTransactionManager.afterPropertiesSet(HibernateTransactionManager.java:412)
	...
Caused by: java.lang.ClassNotFoundException: org.hibernate.engine.SessionFactoryImplementor

Notice the first at line, which names the Spring class org.springframework.orm.hibernate3.SessionFactoryUtils. In a Spring application, the NoClassDefFoundError is the cause of a BeanCreationException for the transaction manager bean, because Spring calls afterPropertiesSet() when it creates the bean. The fix is to use the Spring classes, or the Hibernate module, built for the hibernate-core version on the classpath.

Next, we see why the class is missing and how to find the jar that still uses the old name. After that, we fix the setup for each Spring and Hibernate generation.

1. Why SessionFactoryImplementor Cannot Be Found

The JVM throws a NoClassDefFoundError when a class was present when the calling code was compiled, but is missing at runtime. The org/hibernate/engine folder of each hibernate-core jar on Maven Central shows where the interface moved.

hibernate-core jarPackage of SessionFactoryImplementor
hibernate 3.2.7.ga, hibernate-core 3.3.2.GA, 3.5.6-Final, 3.6.10.Finalorg.hibernate.engine
4.0.0.Final, 4.3.11.Finalorg.hibernate.engine.spi
5.0.12.Final, 5.6.15.Finalorg.hibernate.engine.spi
6.6.36.Final, 7.4.11.Final (group id org.hibernate.orm)org.hibernate.engine.spi

The abbreviation SPI stands for service provider interface (a contract for libraries that plug into Hibernate, not for application code). A class file stores the full name of every type it uses, and the JVM looks only for that exact name. So the interface still exists, but under a name the old class file does not know.

Spring 3.2 orm.hibernate3 HibernateTransactionManager calls SessionFactoryUtils.getDataSource, which checks instanceof org.hibernate.engine.SessionFactoryImplementor; hibernate-core 7.4.11 has no class in org/hibernate/engine, only org/hibernate/engine/spi/SessionFactoryImplementor; the result is the NoClassDefFoundError with its stack trace
The old Spring class asks for org.hibernate.engine.SessionFactoryImplementor. Hibernate 4 and newer only have the spi one.

1.1. Spring’s orm.hibernate3 Classes on Hibernate 4 or Newer

Spring 3.2 and 4.3 ship one integration package per Hibernate generation, namely orm.hibernate3, orm.hibernate4 and (in 4.3) orm.hibernate5. The hibernate3 classes use the old interface. Because the JVM resolves a missing type only when the code first uses it, creating the transaction manager works, and the error comes later at startup.

HibernateTransactionManager txManager = new HibernateTransactionManager();   // orm.hibernate3
txManager.setSessionFactory(emf.unwrap(SessionFactory.class));                // works
txManager.afterPropertiesSet();       // NoClassDefFoundError: org/hibernate/engine/SessionFactoryImplementor

We saw the same trace at the top of the article. It happens when a project upgrades its Hibernate jars but keeps the hibernate3 class names in its Spring XML or Java configuration. The version of spring-orm is often fine, whereas the package in the class name is wrong.

1.2. An Old Hibernate Module Next to a Newer hibernate-core

Up to Hibernate 5, all modules used the group id org.hibernate and had to share one version. When a project declares hibernate-core 4.3 but keeps hibernate-entitymanager 3.6, Maven keeps only the newer core. The verbose dependency tree of the project shows the conflict.

[INFO] com.example:league-app:jar:1.0
[INFO] +- org.hibernate:hibernate-core:jar:4.3.11.Final:compile
[INFO] \- org.hibernate:hibernate-entitymanager:jar:3.6.10.Final:compile
[INFO]    \- (org.hibernate:hibernate-core:jar:3.6.10.Final:compile - omitted for conflict with 4.3.11.Final)

The 3.6.10 hibernate-entitymanager jar has classes such as AbstractEntityManagerImpl and QueryImpl that refer to org.hibernate.engine.SessionFactoryImplementor. Other Hibernate 3 classes are missing too, so the class name in the error depends on what the code touches first. In a test project with these two jars, the call Persistence.createEntityManagerFactory() failed with NoClassDefFoundError: org/hibernate/util/xml/Origin, which is the same problem with a different missing class.

2. How to Find the Jar That Uses the Old Class

The stack trace already names the guilty class in the first at line under the error. The package of that class tells us which library it comes from, and the class itself tells us the jar.

Three steps: the Caused by line names org/hibernate/engine/SessionFactoryImplementor without spi, so the caller was built for Hibernate 3; the first at line names org.springframework.orm.hibernate3.SessionFactoryUtils; getCodeSource().getLocation() or mvn dependency:tree gives spring-orm-3.2.18.RELEASE.jar; then the fix is chosen for that jar
Read the missing name, then the first at line, then ask the JVM which jar holds that class.

The JVM can print the jar of any loaded class.

URL jar = SessionFactoryUtils.class.getProtectionDomain().getCodeSource().getLocation();
// file:/root/.m2/repository/org/springframework/spring-orm/3.2.18.RELEASE/spring-orm-3.2.18.RELEASE.jar

The Maven dependency tree shows how that jar got into the build, and -Dincludes filters the tree to Hibernate and Spring ORM.

mvn dependency:tree -Dincludes=org.hibernate,org.hibernate.orm,org.springframework:spring-orm
[INFO] com.howtodoinjava:hibernate-sessionfactoryimplementor-error:jar:1.0.0
[INFO] +- org.hibernate.orm:hibernate-core:jar:7.4.11.Final:compile
[INFO] \- org.springframework:spring-orm:jar:3.2.18.RELEASE:test

When the stack trace is not at hand, we can search every jar for the old name. The class name with slashes is stored as plain text in each class file that uses it, so a small scanner opens each jar on the class path and looks for those bytes.

ClasspathScanner.findReferences("org/hibernate/engine/SessionFactoryImplementor")
    .forEach(System.out::println);
// spring-orm-3.2.18.RELEASE.jar: org.springframework.orm.hibernate3.SessionFactoryUtils
// spring-orm-3.2.18.RELEASE.jar: org.springframework.orm.hibernate3.LocalSessionFactoryBean
// spring-orm-3.2.18.RELEASE.jar: org.springframework.orm.hibernate3.SpringSessionContext

The scanner is under 80 lines of plain Java in the example project and needs no library.

3. How to Fix It

No jar brings back org.hibernate.engine.SessionFactoryImplementor for Hibernate 4 or newer. Putting a Hibernate 3 core back on the class path mixes two Hibernate generations and fails elsewhere, so we replace the old piece instead.

3.1. Use the Spring Package That Matches Hibernate

Each Spring integration package is compiled against one Hibernate generation, so the package decides which SessionFactoryImplementor the Spring classes look for.

spring-ormPackageUses SessionFactoryImplementor fromFor Hibernate
3.2, 4.3orm.hibernate3org.hibernate.engine3.x
3.2, 4.3orm.hibernate4org.hibernate.engine.spi4.x
4.3, 5.3, 6.2orm.hibernate5org.hibernate.engine.spi5.x, and 6.x in Spring 6.2 for migration
7.0orm.jpa.hibernateorg.hibernate.engine.spi7.x

In a legacy XML configuration, the fix is the package of both beans.

<!-- Before: Hibernate 3 classes, fail with Hibernate 4 and newer -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">...</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">...</bean>

<!-- After: Hibernate 5.x with Spring 4.3 or 5.x (orm.hibernate4 for Hibernate 4.x) -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">...</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">...</bean>

With spring-orm 4.3.30 and a Hibernate 5.6.15 SessionFactory, the hibernate3 transaction manager fails with the same error (SessionFactoryUtils.java:131), whereas the hibernate5 one starts and finds the DataSource.

3.2. Align or Exclude Old Hibernate Modules

Every Hibernate module in a build must have the same version as hibernate-core. In Hibernate 6 and 7, the JPA support is part of hibernate-core, so we remove hibernate-entitymanager instead of upgrading it. Its last final release, 5.6.15, is marked “deprecated – use hibernate-core instead”. For example, an internal reporting library may still pull in hibernate-entitymanager 3.6, and an exclusion removes it.

<dependency>
  <groupId>com.example</groupId>
  <artifactId>legacy-reports</artifactId>
  <version>2.1</version>
  <exclusions>
    <exclusion>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
    </exclusion>
  </exclusions>
</dependency>

A library whose own classes were compiled against Hibernate 3 cannot be fixed with an exclusion, so it needs a newer version of that library. To keep all org.hibernate.orm artifacts on one version, we import the hibernate-platform BOM (bill of materials, a pom that only lists versions) and omit the versions of the modules.

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.hibernate.orm</groupId>
      <artifactId>hibernate-platform</artifactId>
      <version>7.4.11.Final</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependency>
  <groupId>org.hibernate.orm</groupId>
  <artifactId>hibernate-core</artifactId>
</dependency>

In a Spring Boot application, the Spring Boot dependency management already aligns the Hibernate versions, so the pom does not set a Hibernate version of its own.

3.3. The Hibernate 7 and Spring 7 Setup

Spring Framework 7 replaced orm.hibernate5 with org.springframework.orm.jpa.hibernate, built for Hibernate 7. A native Hibernate setup moves to that package.

SessionFactory sessionFactory = new LocalSessionFactoryBuilder(dataSource).buildSessionFactory();
HibernateTransactionManager txManager = new HibernateTransactionManager(sessionFactory);   // orm.jpa.hibernate
txManager.afterPropertiesSet();                  // starts, txManager.getDataSource() = dataSource

The setup works with spring-orm 7.0.9 and Hibernate 7.4.11. Apps that use JPA with an EntityManagerFactory and Spring Data JPA do not need this package at all.

4. SessionFactoryImplementor FAQs

4.1. How Do I Get SessionFactoryImplementor in Hibernate 7?

We unwrap it from the EntityManagerFactory, or get it from a session. The following example stores sports fixtures, where a Fixture has a homeTeam (“Rovers”), an awayTeam (“United”), a kickoff time and a venue (“Riverside”), in an in-memory H2 database.

SessionFactoryImplementor sfi = emf.unwrap(SessionFactoryImplementor.class);
boolean same = sfi == emf.unwrap(SessionFactory.class);       // true

SessionFactoryImplementor fromSession =
    emf.callInTransaction(em -> em.unwrap(SessionImplementor.class).getFactory());   // sfi

The import is org.hibernate.engine.spi.SessionFactoryImplementor. Copying the old import from a Hibernate 3 example gives a compile error instead of the runtime error, which is easier to fix.

4.2. When Should Application Code Use SessionFactoryImplementor?

Application code rarely needs it, because SessionFactoryImplementor is “the internal contract between the SessionFactory and the internal implementation of Hibernate”, and its methods can change between minor versions. The interface is meant for integration code such as custom types, event listeners or interceptors that need Hibernate internals. Most needs have a public API.

NeedPublic APISPI call
StatisticssessionFactory.getStatistics()Not needed
Entity names and attributesemf.getMetamodel()sfi.getMappingMetamodel().getEntityDescriptor(Fixture.class)
The SQL dialect in useNonesfi.getJdbcServices().getDialect()
Dialect dialect = sfi.getJdbcServices().getDialect();                       // H2Dialect
String entityName = sfi.getMappingMetamodel().getEntityDescriptor(Fixture.class).getEntityName();
// com.howtodoinjava.hibernate.sfimplementor.Fixture

When a public API exists, we use it, so the code survives the next Hibernate upgrade.

4.3. Is This the Same Problem as NoClassDefFoundError for CacheProvider?

Yes, it has the same cause, a Hibernate 3 class on Hibernate 4 or newer. Hibernate 4.0 removed CacheProvider instead of moving it, and Spring’s orm.hibernate3.LocalSessionFactoryBean refers to both types. The fix for the cache side is the Hibernate 7 JCache setup that replaces CacheProvider.

The complete project on GitHub reproduces the error with the real Spring 3.2.18 jars in test scope and finds the jar, with 9 JUnit tests that check the error, the jar lookup and the SPI calls (mvn -q test-compile exec:java, mvn test).

5. Conclusion

The error NoClassDefFoundError: org/hibernate/engine/SessionFactoryImplementor comes from a version mix. A class compiled for Hibernate 3 runs with Hibernate 4 or newer, where the interface lives in org.hibernate.engine.spi. We read the class from the first at line and look up its jar. After that, we either switch to the Spring package for our Hibernate version or align the old Hibernate modules. On Hibernate 7, we unwrap SessionFactoryImplementor from the EntityManagerFactory only when no public API gives the same information.

6. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.