1、概览 {#1概览}
使用 Spring Data JPA 时,应用启动出现异常。大致如下:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerAdapter'
...
Caused by: java.lang.IllegalArgumentException: Not a managed type: ...OurEntity
at org.hibernate.metamodel.internal.MetamodelImpl.managedType(MetamodelImpl.java:583)
at org.hibernate.metamodel.internal.MetamodelImpl.managedType(MetamodelImpl.java:85)
...
大意是说,一些 Bean 创建失败了,导致应用启动失败。
根异常是 IllegalArgumentException:"Not a managed type",本文将带你了解出现这个异常的原因,以及如何解决该异常。
2、缺少 @Entity 注解 {#2缺少-entity-注解}
出现这种异常的一个可能原因是,忘记使用 @Entity
注解来标记实体。
2.1、重现问题 {#21重现问题}
假设有以下实体类:
public class EntityWithoutAnnotation {
@Id
private Long id;
}
及其对应的 Spring Data JPA repository:
public interface EntityWithoutAnnotationRepository
extends JpaRepository<EntityWithoutAnnotation, Long> {
}
最后是 Application 启动类,它会扫描上面定义的所有类:
@SpringBootApplication
public class EntityWithoutAnnotationApplication {
}
尝试使用此 Application 来启动 Spring Context:
@Test
void givenEntityWithoutAnnotationApplication_whenBootstrap_thenExpectedExceptionThrown() {
Exception exception = assertThrows(Exception.class,
() -> SpringApplication.run(EntityWithoutAnnotationApplication.class));
assertThat(exception)
.getRootCause()
.hasMessageContaining("Not a managed type");
}
运行测试,不出所料,出现了 "Not a managed type" 异常。
2.2、修复问题 {#22修复问题}
将 @Entity
注解添加到实体的 "修复版" 中:
@Entity
public class EntityWithoutAnnotationFixed {
@Id
private Long id;
}
Application 和 Repository 类保持不变,再次尝试启动应用:
@Test
void givenEntityWithoutAnnotationApplicationFixed_whenBootstrap_thenRepositoryBeanShouldBePresentInContext() {
ConfigurableApplicationContext context = run(EntityWithoutAnnotationFixedApplication.class);
EntityWithoutAnnotationFixedRepository repository = context
.getBean(EntityWithoutAnnotationFixedRepository.class);
assertThat(repository).isNotNull();
}
测试通过,成功获取到了了 ConfigurableApplicationContext
实例,并从中获取了 Repository
实例。
3、从 javax.persistance 迁移到 jakarta.persistance {#3从-javaxpersistance-迁移到-jakartapersistance}
在将应用迁移到 Jakarta Persistence API 时,也可能还会遇到这种异常情况。
3.1、重现问题 {#31重现问题}
假设有如下实体:
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
public class EntityWithJakartaAnnotation {
@Id
private Long id;
}
如上,使用了 jakarta.persistence
包,但我们仍在使用 Spring Boot 2,再以与上一节类似的方式创建 Repository 和 Application 类。
现在,尝试启动应用:
@Test
void givenEntityWithJakartaAnnotationApplication_whenBootstrap_thenExpectedExceptionThrown() {
Exception exception = assertThrows(Exception.class,
() -> run(EntityWithJakartaAnnotationApplication.class));
assertThat(exception)
.getRootCause()
.hasMessageContaining("Not a managed type");
}
"Not a managed type" 异常再次出现。JPA 实体扫描器(Entity Scanner )希望我们使用 javax.persistence.Entity
注解,而不是 jakarta.persistence.Entity
注解。
3.2、修复问题 {#32修复问题}
在这种情况下,有两种可能的解决方案。
- 升级到 Spring Boot 3 ,然后使用
jakarta.persistence
。 - 如果还没准备好升级,就继续使用
javax.persistence.Entity
。
4、缺少或错误配置了 @EntityScan {#4缺少或错误配置了-entityscan}
另一种可能遇到 "Not a managed type" 异常的常见情况是,JPA 实体扫描器(Entity Scanner)无法在预期路径中找到实体。
4.1、重现问题 {#41重现问题}
首先,创建另一个实体:
package com.baeldung.spring.entity;
@Entity
public class CorrectEntity {
@Id
private Long id;
}
它带有 @Entity
注解,并被置于 entity
包中。现在,创建一个 Repository:
package com.baeldung.spring.repository;
public interface CorrectEntityRepository extends JpaRepository<CorrectEntity, Long> {
}
Repository
接口放在 repository
包中。最后,创建一个 Application 类:
package com.baeldung.spring.app;
@SpringBootApplication
@EnableJpaRepositories(basePackages = "com.baeldung.spring.repository")
public class WrongEntityScanApplication {
}
它在 app
包中。默认情况下,Spring Data 会扫描 main 类所在包及其子包下的 Repository
接口。因此,我们需要使用 @EnableJpaRepositories
的 basePackages
属性指定 Repository
接口所在的包。
现在,尝试启动应用:
@Test
void givenWrongEntityScanApplication_whenBootstrap_thenExpectedExceptionThrown() {
Exception exception = assertThrows(Exception.class,
() -> run(WrongEntityScanApplication.class));
assertThat(exception)
.getRootCause()
.hasMessageContaining("Not a managed type");
}
再次遇到了 "Not a managed type" 异常。原因是实体扫描的逻辑与 Repository 扫描的逻辑相同。我们扫描了 app
包下的组件,但没有发现任何实体,因此在构建 CorrectEntityRepository
时出现了异常。
4.2、修复问题 {#42修复问题}
要解决这个问题,可以使用 @EntityScan
注解。
再创建一个 Application 类:
@SpringBootApplication
@EnableJpaRepositories(basePackages =
"com.baeldung.spring.repository")
@EntityScan("com.baeldung.spring.entity")
public class WrongEntityScanFixedApplication {
}
如上,使用 @EnableJpaRepositories
注解指定 Repository
所在包,并使用 @EntityScan
注解指定实体包。
执行测试:
@Test
void givenWrongEntityScanApplicationFixed_whenBootstrap_thenRepositoryBeanShouldBePresentInContext() {
ConfigurableApplicationContext context = run(WrongEntityScanFixedApplication.class);
CorrectEntityRepository repository = context
.getBean(CorrectEntityRepository.class);
assertThat(repository).isNotNull();
}
测试通过。我们从 Context 中成功获取到了 CorrectEntityRepository
,并且 CorrectEntity
已被成功识别为 JPA 实体。
5、总结 {#5总结}
本文介绍了在使用 Spring Data JPA 时出现 "Not a managed type" 异常的各种原因,以及对应的解决办法。
Ref:https://www.baeldung.com/spring-data-jpa-not-managed-type-exception