1. 概述
在使用 Spring Data JPA 的过程中,我们可能会在启动时遇到问题。部分bean可能无法创建,导致应用无法启动。虽然堆栈跟踪可能因情况而异,但通常看起来像这样:
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)
...
根本原因是一个“非受管理类型”异常。在这篇文章中,我们将深入探讨这种异常的可能原因,并探索解决方案。
2. 忘记添加@Entity
注解
遇到此异常的一个可能原因是我们在实体类上忘记使用 @Entity
注解。
2.1. 复现问题
假设我们有以下实体类:
public class EntityWithoutAnnotation {
@Id
private Long id;
}
我们还有其Spring Data JPA的仓库:
public interface EntityWithoutAnnotationRepository
extends JpaRepository<EntityWithoutAnnotation, Long> {
}
最后,我们有一个扫描上述所有类的应用类:
@SpringBootApplication
public class EntityWithoutAnnotationApplication {
}
现在尝试使用这个应用启动Spring上下文:
@Test
void givenEntityWithoutAnnotationApplication_whenBootstrap_thenExpectedExceptionThrown() {
Exception exception = assertThrows(Exception.class,
() -> SpringApplication.run(EntityWithoutAnnotationApplication.class));
assertThat(exception)
.getRootCause()
.hasMessageContaining("Not a managed type");
}
正如预期,我们遇到了与我们的实体相关的“非受管理类型”异常。
2.2. 解决问题
让我们将@Entity
注解添加到修复后的实体版本:
@Entity
public class EntityWithoutAnnotationFixed {
@Id
private Long id;
}
应用和仓库类保持不变。再次尝试启动应用:
@Test
void givenEntityWithoutAnnotationApplicationFixed_whenBootstrap_thenRepositoryBeanShouldBePresentInContext() {
ConfigurableApplicationContext context = run(EntityWithoutAnnotationFixedApplication.class);
EntityWithoutAnnotationFixedRepository repository = context
.getBean(EntityWithoutAnnotationFixedRepository.class);
assertThat(repository).isNotNull();
}
我们成功获取了ConfigurableApplicationContext
实例,并从此处获得了仓库实例。
3. 从javax.persistence
迁移到jakarta.persistence
当我们的应用迁移至Jakarta 持久化API时,也可能遇到这种异常。
3.1. 复现问题
假设我们有如下实体:
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@Entity
public class EntityWithJakartaAnnotation {
@Id
private Long id;
}
我们开始使用jakarta.persistence
包,但仍在使用Spring Boot 2。我们将按照上一节创建仓库和应用类的方式进行创建。
现在尝试启动应用:
@Test
void givenEntityWithJakartaAnnotationApplication_whenBootstrap_thenExpectedExceptionThrown() {
Exception exception = assertThrows(Exception.class,
() -> run(EntityWithJakartaAnnotationApplication.class));
assertThat(exception)
.getRootCause()
.hasMessageContaining("Not a managed type");
}
我们再次遇到了“非受管理类型”异常。我们的JPA实体扫描器期望我们使用javax.persistence.Entity
注解,而不是 jakarta.persistence.Entity
。
3.2. 解决问题
在这种情况下,我们有两种潜在的解决方案。我们可以迁移到Spring Boot 3,Spring Data JPA开始使用jakarta.persistence
。或者,如果我们尚未准备好迁移,可以继续使用javax.persistence.Entity
。
4. 忽略或配置错误的@EntityScan
另一个可能导致“非受管理类型”异常的常见情况是当我们的JPA实体扫描器无法在预期路径中找到实体时。
4.1. 复现问题
首先,我们创建另一个实体:
package com.baeldung.spring.entity;
@Entity
public class CorrectEntity {
@Id
private Long id;
}
它带有@Entity
注解,并放在entity
包中。现在,我们创建一个仓库:
package com.baeldung.spring.repository;
public interface CorrectEntityRepository extends JpaRepository<CorrectEntity, Long> {
}
我们的仓库位于repository
包中。最后,我们创建一个应用类:
package com.baeldung.spring.app;
@SpringBootApplication
@EnableJpaRepositories(basePackages = "com.baeldung.spring.repository")
public class WrongEntityScanApplication {
}
它位于app
包中。默认情况下,Spring Data会在主类包及其子包下查找仓库。因此,我们需要使用@EnableJpaRepositories
注解指定基础仓库包。现在尝试启动应用:
@Test
void givenWrongEntityScanApplication_whenBootstrap_thenExpectedExceptionThrown() {
Exception exception = assertThrows(Exception.class,
() -> run(WrongEntityScanApplication.class));
assertThat(exception)
.getRootCause()
.hasMessageContaining("Not a managed type");
}
我们再次遇到了“非受管理类型”异常。原因在于实体扫描逻辑与仓库扫描相同。在我们的app
包下的包中进行了扫描,但没有在那里找到任何实体,从而在构建CorrectEntityRepository
时出现了异常。
4.2. 解决问题
要解决这个问题,我们可以使用**@EntityScan
注解**。
让我们创建另一个应用类:
@SpringBootApplication
@EnableJpaRepositories(basePackages =
"com.baeldung.spring.repository")
@EntityScan("com.baeldung.spring.entity")
public class WrongEntityScanFixedApplication {
}
现在,我们使用@EnableJpaRepositories
注解指定仓库包,并使用@EntityScan
注解指定实体包。看它是如何工作的:
@Test
void givenWrongEntityScanApplicationFixed_whenBootstrap_thenRepositoryBeanShouldBePresentInContext() {
SpringApplication app = new SpringApplication(WrongEntityScanFixedApplication.class);
app.setAdditionalProfiles("test");
ConfigurableApplicationContext context = app.run();
CorrectEntityRepository repository = context
.getBean(CorrectEntityRepository.class);
assertThat(repository).isNotNull();
}
我们成功启动了应用。我们从上下文中获取了CorrectEntityRepository
,这意味着它已构建并且CorrectEntity
成功被识别为JPA实体。
5. 总结
在这篇教程中,我们探讨了在使用Spring Data JPA时为何可能会遇到“非受管理类型”异常,以及如何避免它。解决方案的选择取决于具体的情况,但理解可能的原因有助于我们识别我们所面对的具体变种。
如往常一样,完整的源代码可以在GitHub上找到。