Unable to Locate Persister in Hibernate: Causes and Fixes

“Unable to locate persister” (Unknown entity type in Hibernate 7) means Hibernate has no mapping for the class it received. See each cause reproduced on Hibernate 7.4 and Spring Boot 4.1, from javax imports and missing registration to DTOs, proxies and entity scanning, with a tested fix for each.

exceptions-notes

The Hibernate error “Unable to locate persister” means that we passed a class to Hibernate that is not a mapped entity in the current persistence unit. A persister is the object Hibernate builds at startup for each entity, and it knows the table, the columns, the id and the SQL. When em.persist() or em.find() gets a class with no persister, the call fails.

We meet the error when we add a new entity class and forget to register it, or right after an upgrade from Hibernate 5 to Hibernate 6 or 7.

The following example saves a FerryRoute object that Hibernate does not know as an entity. Hibernate 5 and 6 print “Unable to locate persister”, whereas Hibernate 7 reports the same problem as “Unknown entity type”.

java.lang.IllegalArgumentException: Unable to locate persister: com.howtodoinjava.hibernate.persister.FerryRoute
	at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:764)
	at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:745)
Caused by: org.hibernate.UnknownEntityTypeException: Unable to locate persister: com.howtodoinjava.hibernate.persister.FerryRoute
java.lang.IllegalArgumentException: Unknown entity type 'com.howtodoinjava.hibernate.persister.FerryRoute' ('FerryRoute' does not belong to this persistence unit)
Caused by: org.hibernate.UnknownEntityTypeException: Unknown entity type 'com.howtodoinjava.hibernate.persister.FerryRoute'

Notice that both versions throw an IllegalArgumentException that wraps org.hibernate.UnknownEntityTypeException, so the cause and the fix are the same.

Next, we go through the five causes of the error, from a class that is not registered to a Spring Boot app that does not scan the entity package, and fix each one.

1. Why Hibernate Cannot Find the Entity Persister

Hibernate builds its persisters once, when the EntityManagerFactory starts. It takes the classes we registered, keeps those annotated with jakarta.persistence.Entity and creates one persister per entity. Later, every persist() or find() looks up the persister by the name of the class it receives. The error comes from one of three places in that flow.

Flow diagram: at startup Hibernate collects classes from managedClass, persistence.xml class elements, classpath scanning or Spring Boot entity scanning, keeps only those with jakarta.persistence.Entity and stores one EntityPersister per entity; on every call it uses the class name of the argument as the key, finds the persister and builds the insert, or throws UnknownEntityTypeException wrapped in IllegalArgumentException; red markers show the failure points: class not registered, no jakarta @Entity, wrong class passed
Hibernate finds a persister by class name. The class must be registered, carry the jakarta @Entity, and be the entity class itself.

The wording changed between versions, but the cause did not. Hibernate 5.6 and 6.6 even print different messages for persist() and find().

Hibernateem.persist(route)em.find(FerryRoute.class, 1L)
5.6Unknown entity: …FerryRouteUnable to locate persister: …FerryRoute
6.6Unable to locate persister: …FerryRouteUnable to locate entity descriptor: …FerryRoute
7.0Unknown entity type: …FerryRouteUnknown entity type: …FerryRoute
7.4Unknown entity type ‘…FerryRoute’ plus the reason in bracketsUnknown entity type ‘…FerryRoute’

The reason in Hibernate 7.4 tells us which cause we hit, either ‘FerryRoute’ does not belong to this persistence unit or ‘FerryRoute’ is not annotated ‘@Entity’.

Our example is a ferry timetable with one entity, FerryRoute (fromPort, toPort, durationMinutes), on Hibernate 7.4 and H2.

1.1. The Entity Is Not Registered

With HibernatePersistenceConfiguration, Hibernate knows only the classes we pass to managedClass() or managedClasses(). If we forget one, the factory still starts without errors, and the first call fails. For example, a team adds FerryRoute to a ferry booking app and writes the booking code, but nobody adds the class to the configuration, so the first booking request throws the exception.

EntityManagerFactory emf = new HibernatePersistenceConfiguration("ferry")
    .managedClasses()                           // FerryRoute not listed
    .jdbcUrl("jdbc:h2:mem:ferry;DB_CLOSE_DELAY=-1")
    .createEntityManagerFactory();

emf.runInTransaction(em -> em.persist(new FerryRoute("Helsinki", "Tallinn", 120)));
// IllegalArgumentException: Unknown entity type '...FerryRoute' ('FerryRoute' does not belong to this persistence unit)

A persistence.xml unit fails the same way when the class is not in a <class> element and unlisted classes are excluded.

<persistence-unit name="ferry-unlisted">
  <exclude-unlisted-classes>true</exclude-unlisted-classes>
  ...
</persistence-unit>

1.2. The Class Has No @Entity

A class with @Id fields but without @Entity is not an entity. Hibernate ignores a class without @Entity and logs no warning, even when we register it, so the error shows up only at the first call.

public class FerryRoute {          // @Entity is missing
  @Id @GeneratedValue
  private Long id;
  private String fromPort;
  ...
}

int entityCount = emf.getMetamodel().getEntities().size();   // 0
em.persist(new FerryRoute("Helsinki", "Tallinn", 120));
// IllegalArgumentException: Unknown entity type '...FerryRoute' ('FerryRoute' is not annotated '@Entity')

1.3. The Entity Uses javax.persistence Instead of jakarta.persistence

The old javax import shows up right after an upgrade, with no change in our own code, because Hibernate 6 moved from Java Persistence (javax.persistence) to Jakarta Persistence (jakarta.persistence). An entity copied from a Hibernate 5 or Spring Boot 2 project still compiles when an old javax.persistence-api jar is on the classpath, for example through a library. Hibernate 6 and 7 do not read those annotations, so they treat the class as a plain class.

import javax.persistence.Entity;      // Hibernate 5 and earlier
import javax.persistence.Id;

@Entity
public class FerryRoute { ... }

em.persist(new FerryRoute("Helsinki", "Tallinn", 120));
// Hibernate 6.6: Unable to locate persister: com.howtodoinjava.hibernate.persister.FerryRoute
// Hibernate 7.4: Unknown entity type '...FerryRoute' ('FerryRoute' is not annotated '@Entity')

1.4. A DTO or Proxy Class Is Passed Instead of the Entity

Hibernate looks up the persister by the exact class we pass. A DTO (a plain class or record that only carries data) has the same fields as the entity but no persister.

public record FerryRouteDto(String fromPort, String toPort, int durationMinutes) { }

em.persist(new FerryRouteDto("Turku", "Stockholm", 660));
// Unknown entity type '...dto.FerryRouteDto' ('FerryRouteDto' is not annotated '@Entity')
FerryRouteDto found = em.find(FerryRouteDto.class, 1L);
// IllegalArgumentException: Unknown entity type '...dto.FerryRouteDto'

The same happens with a proxy. Both em.getReference() and lazy associations return a subclass generated at runtime, so generic code that calls getClass() gets the proxy class. For example, a generic audit helper that reloads any entity with em.find(entity.getClass(), id) fails as soon as it gets a proxy.

FerryRoute ref = em.getReference(FerryRoute.class, id);
String className = ref.getClass().getName();   // ...FerryRoute$HibernateProxy
FerryRoute found = em.find(ref.getClass(), id);
// IllegalArgumentException: Unknown entity type '...FerryRoute$HibernateProxy'

1.5. Spring Boot Does Not Scan the Entity Package

Spring Boot looks for entities only in the package of the @SpringBootApplication class and its sub-packages. An entity in a parent or sibling package is never registered. For example, a team moves the application class into a boot sub-package during a refactoring, and the entities in the parent package drop out of the scan.

Two package trees: on the left the application class sits in persister.boot.scanmissing, only that package is scanned, FerryRoute in the parent package is not, so persist fails with Unknown entity type and a JpaRepository fails at startup with Not a managed type; on the right FixedFerryApp has @EntityScan(basePackageClasses = FerryRoute.class), the whole persister package is scanned and repository.save works
Spring Boot scans from the application class downward. FerryRoute sits one level above it, so it is outside the scan.

With an EntityManager, the error appears at the first call, with the same message as in section 1.1. With a Spring Data repository for that entity, the application does not start.

BeanCreationException: Error creating bean with name 'ferryRouteRepository' ...: Not a managed type: class com.howtodoinjava.hibernate.persister.FerryRoute

2. How to Fix It

Every cause has a short fix. The order follows section 1, so we can jump to the cause that the reason in brackets names.

2.1. Register the Entity

We add the class to the configuration, either in code with managedClasses() or as a <class> element in persistence.xml. A missing class is the usual problem after we add a new entity to a project that lists its classes.

.managedClasses()                                   // before
.managedClasses(FerryRoute.class)                   // after: insert into FerryRoute (...) values (?,?,?,?)
<persistence-unit name="ferry-listed">
  <class>com.howtodoinjava.hibernate.persister.FerryRoute</class>
  <exclude-unlisted-classes>true</exclude-unlisted-classes>
  ...
</persistence-unit>

2.2. Add @Entity From jakarta.persistence

The fix for sections 1.2 and 1.3 is the same, so we annotate the class with jakarta.persistence.Entity. The import decides which annotation the class gets, so we search the project for javax.persistence and replace every one.

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;

@Entity
public class FerryRoute { ... }

String entityName = emf.getMetamodel().entity(FerryRoute.class).getName();   // "FerryRoute"

If javax.persistence-api still shows up in mvn dependency:tree, we exclude it, so the old import no longer compiles.

2.3. Pass the Entity Class, Not a DTO or Proxy

We map the DTO to an entity before saving, and read DTOs with a query instead of find().

FerryRoute route = new FerryRoute(dto.fromPort(), dto.toPort(), dto.durationMinutes());
em.persist(route);                  // insert into FerryRoute (...) values (?,?,?,?)

FerryRouteDto loaded = em.createQuery(
        "select r.fromPort, r.toPort, r.durationMinutes from FerryRoute r where r.id = :id",
        FerryRouteDto.class)
    .setParameter("id", id)
    .getSingleResult();             // FerryRouteDto[fromPort=Turku, toPort=Stockholm, durationMinutes=660]

For a proxy, Hibernate.getClass() returns the real entity class. It initializes the proxy first, so it runs a SELECT when the proxy is not loaded yet.

Class<?> type = Hibernate.getClass(ref);   // ...FerryRoute (select ... from FerryRoute fr1_0 where fr1_0.id=?)
Object route = em.find(type, id);          // Turku -> Stockholm (660 min)

2.4. Point Spring Boot to the Entity Package

We add @EntityScan to the application class, or move the application class to a package above all entities. In Spring Boot 4 the annotation is in org.springframework.boot.persistence.autoconfigure; Spring Boot 3 has it in org.springframework.boot.autoconfigure.domain.

import org.springframework.boot.persistence.autoconfigure.EntityScan;

@SpringBootApplication
@EntityScan(basePackageClasses = FerryRoute.class)
public class FixedFerryApp { }

repository.save(new FerryRoute("Helsinki", "Tallinn", 120));
long count = repository.count();           // 1

The complete project on GitHub covers each cause and each fix with JUnit tests (20 in total), including Spring Boot 4.1.1 and the old javax annotations (mvn -q compile exec:java, mvn test).

3. Unknown Entity Error FAQs

3.1. Why Do persist(), find() and Queries Show Different Messages?

Each API checks the class in its own place, so one missing entity produces several messages, from persist() and find() to an HQL query and the Criteria API. All of them mean “this class is not an entity in this persistence unit”.

Call (Hibernate 7.4)Message
em.persist(route)Unknown entity type ‘…FerryRoute’ (‘FerryRoute’ does not belong to this persistence unit)
em.find(FerryRoute.class, 1L)Unknown entity type ‘…FerryRoute’
em.createQuery(“from FerryRoute”)UnknownEntityException: Could not resolve root entity ‘FerryRoute’
cb.createQuery(…).from(FerryRoute.class)Not an entity: …FerryRoute
Spring Data repositoryNot a managed type: class …FerryRoute

The JPA specification requires the IllegalArgumentException, because EntityManager.persist() throws it “if the instance is not an entity”.

3.2. Why Did My persistence.xml Stop Finding Entities in Hibernate 7?

Hibernate 7 moved classpath scanning out of hibernate-core into the hibernate-scan-jandex module. A unit without <class> elements that relied on scanning finds no entities after the upgrade.

int entityCount = emf.getMetamodel().getEntities().size();   // 0
em.persist(new FerryRoute("Helsinki", "Tallinn", 120));
// Unknown entity type '...FerryRoute' ('FerryRoute' does not belong to this persistence unit)

Adding hibernate-scan-jandex to the dependencies brings scanning back.

<dependency>
  <groupId>org.hibernate.orm</groupId>
  <artifactId>hibernate-scan-jandex</artifactId>
  <version>7.4.11.Final</version>
</dependency>

Hibernate scans the root of the persistence unit, which is the jar or directory that holds META-INF/persistence.xml. The Jakarta Persistence specification calls scanning optional in Java SE, so listing each class is the portable choice.

3.3. Is “Unknown entity” in Hibernate 5 the Same Error?

Yes. Hibernate 5.6 reports an unmapped class as Unknown entity from persist() and as org.hibernate.MappingException: Unknown entity: … from the old Session.save(). Old answers that fix “Unknown entity” (register the class, add @Entity) apply here too, as long as we use jakarta imports on Hibernate 6 and 7. Session.save() no longer exists in Hibernate 7; persist() replaces it.

4. Conclusion

“Unable to locate persister” and “Unknown entity type” both say that Hibernate has no entity mapping for the class it received. To fix it, we check that the class is registered or scanned and carries jakarta.persistence.Entity. We also check that the code passes the entity class itself, not a DTO or a proxy. In Hibernate 7.4, the text in brackets at the end of the message points to the right cause.

5. References

Happy Learning !!

Source Code on Github

Leave a Comment

Comments are closed.

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.