Spring项目BeanCreationException求助:Round实体被判定非Entity
我是Java和Spring新手,跟着一份Spring Security教程操作一切正常,但添加自定义代码后出现BeanCreationException,提示Round不是Entity类,但我已经给Round标注了@Entity注解。其他实体都能在MySQL中正常创建,唯一区别是Round是唯一与User关联的实体。
User实体代码
@Getter @Setter @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; @Column(nullable = false, unique = true) private String email; @Column(nullable = false) private String password; @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) @JoinTable( name = "users_roles", joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")} ) private List<Role> roles = new ArrayList<>(); @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "user_id", referencedColumnName = "id") private List<Round> rounds = new ArrayList<>(); public User(String name, String email, String password, List<Role> roles) { this.name = name; this.email = email; this.password = password; this.roles = roles; } public User(String name, String email, String password, List<Role> roles, List<Round> rounds) { this.name = name; this.email = email; this.password = password; this.roles = roles; this.rounds = rounds; } public void addRole(Role role) { this.roles.add(role); } public void addRound(Round round) { this.rounds.add(round); } }
Round实体代码
@Getter @Setter @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "round") public class Round { @Id @Column(name = "round_id") @GeneratedValue(strategy = GenerationType.IDENTITY) private Long roundId; @JsonIgnore @ManyToOne @JoinColumn(name = "course_id") private Course course; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "round_id", referencedColumnName = "round_id") private List<Score> scores = new ArrayList<>(); @JsonDeserialize(using = LocalDateTimeDeserializer.class) @Column(name = "round_date") @DateTimeFormat(pattern = "dd/MM/yyyy") private Date roundDate; @Column(name = "round_total") private int total; public void addScoreToRound(Score score) { this.scores.add(score); } }
错误日志
2023-03-05T20:08:34.058+01:00 ERROR 34998 --- [ restartedMain] j.LocalContainerEntityManagerFactoryBean : Failed to initialize JPA EntityManagerFactory: Association 'com.springSecurityLogin.login.model.User.rounds' targets the type 'com.springSecurityLogin.discgolf.model.Round' which is not an '@Entity' type 2023-03-05T20:08:34.059+01:00 WARN 34998 --- [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Association 'com.springSecurityLogin.login.model.User.rounds' targets the type 'com.springSecurityLogin.discgolf.model.Round' which is not an '@Entity' type 2023-03-05T20:08:34.061+01:00 INFO 34998 --- [ restartedMain] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated... 2023-03-05T20:08:34.107+01:00 INFO 34998 --- [ restartedMain] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed. 2023-03-05T20:08:34.113+01:00 INFO 34998 --- [ restartedMain] o.apache.catalina.core.StandardService : Stopping service [Tomcat] 2023-03-05T20:08:34.155+01:00 INFO 34998 --- [ restartedMain] .s.b.a.l.ConditionEvaluationReportLogger : Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. 2023-03-05T20:08:34.211+01:00 ERROR 34998 --- [ restartedMain] o.s.boot.SpringApplication : Application run failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Association 'com.springSecurityLogin.login.model.User.rounds' targets the type 'com.springSecurityLogin.discgolf.model.Round' which is not an '@Entity' type at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1752) ~[spring-beans-6.0.5.jar:6.0.5] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:600) ~[spring-beans-6.0.5.jar:6.0.5] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522) ~[spring-beans-6.0.5.jar:6.0.5] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.5.jar:6.0.5] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.5.jar:6.0.5] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.5.jar:6.0.5] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.0.5.jar:6.0.5] at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1132) ~[spring-context-6.0.5.jar:6.0.5] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:907) ~[spring-context-6.0.5.jar:6.0.5] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584) ~[spring-context-6.0.5.jar:6.0.5] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.0.3.jar:3.0.3] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) ~[spring-boot-3.0.3.jar:3.0.3] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) ~[spring-boot-3.0.3.jar:3.0.3] at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[spring-boot-3.0.3.jar:3.0.3] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1304) ~[spring-boot-3.0.3.jar:3.0.3] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1293) ~[spring-boot-3.0.3.jar:3.0.3] at com.springSecurityLogin.login.SpringSecurityLoginApplication.main(SpringSecurityLoginApplication.java:12) ~[classes/:na] at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:577) ~[na:na] at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-3.0.3.jar:3.0.3] Caused by: org.hibernate.AnnotationException: Association 'com.springSecurityLogin.login.model.User.rounds' targets the type 'com.springSecurityLogin.discgolf.model.Round' which is not an '@Entity' type at org.hibernate.cfg.annotations.CollectionBinder.detectManyToManyProblems(CollectionBinder.java:2544) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final] at org.hibernate.cfg.annotations.CollectionBinder.bindManyToManySecondPass(CollectionBinder.java:2097) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final] at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:1593) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final] at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:1524) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final] at org.hibernate.cfg.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:47) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final] at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1782) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final] at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1739) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final] at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:300) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final] at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1348) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final] at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1419) ~[hibernate-core-6.1.7.Final.jar:6.1.7.Final] at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:66) ~[spring-orm-6.0.5.jar:6.0.5] at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:376) ~[spring-orm-6.0.5.jar:6.0.5] at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.0.5.jar:6.0.5] at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-6.0.5.jar:6.0.5] at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:352) ~[spring-orm-6.0.5.jar:6.0.5] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1798) ~[spring-beans-6.0.5.jar:6.0.5] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1748) ~[spring-beans-6.0.5.jar:6.0.5] ... 19 common frames omitted
1. 修正JPA实体扫描范围
从错误日志可见,User在com.springSecurityLogin.login.model包,Round在com.springSecurityLogin.discgolf.model包。Spring Boot默认只会扫描主启动类所在包及其子包下的实体类,而主启动类SpringSecurityLoginApplication在com.springSecurityLogin.login包,因此discgolf.model包下的Round不会被扫描到。
在主启动类上添加@EntityScan注解,指定所有实体所在的包:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; @SpringBootApplication @EntityScan(basePackages = {"com.springSecurityLogin.login.model", "com.springSecurityLogin.discgolf.model"}) // 或者用更上层的包简化:@EntityScan(basePackages = "com.springSecurityLogin") public class SpringSecurityLoginApplication { public static void main(String[] args) { SpringApplication.run(SpringSecurityLoginApplication.class, args); } }
2. 确认JPA注解的导入(Spring Boot 3.x注意)
Spring Boot 3.x基于Jakarta EE,所有JPA注解都从jakarta.persistence包导入,而非旧的javax.persistence包。检查Round类的注解导入:
// 正确导入 import jakarta.persistence.Entity; import jakarta.persistence.Table; import jakarta.persistence.Id; import jakarta.persistence.Column; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.ManyToOne; import jakarta.persistence.JoinColumn; import jakarta.persistence.OneToMany;
如果导入的是javax.persistence下的注解,Hibernate会无法识别该类为实体。
3. 完善双向关联(可选但推荐)
当前User到Round的单向关联虽然能工作,但双向关联更符合JPA规范,也能避免潜在问题。修改如下:
在Round类中添加对User的关联:
@JsonIgnore @ManyToOne @JoinColumn(name = "user_id") private User user;
同时修改User类中的@OneToMany注解,添加mappedBy属性指定关联的反向字段:
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) private List<Round> rounds = new ArrayList<>();
这样JPA会明确关联关系,无需额外的中间表。
内容的提问来源于stack exchange,提问作者Kayd Anderson

