Spring Boot 3迁移报错:无法定位AbstractEntity的id属性
问题现象
原本运行正常的Spring Boot 2.5应用迁移到3.0 RC1后,调用仓库方法translationRepository.findTranslationsByClassifierId(1L)时触发如下错误:
Caused by: java.lang.IllegalArgumentException: Unable to locate Attribute with the given name [id] on this ManagedType [com.example.demo.domain.AbstractEntity]
at org.hibernate.metamodel.model.domain.AbstractManagedType.checkNotNull(AbstractManagedType.java:281) ~[hibernate-core-6.1.4.Final.jar:6.1.4.Final]
...
经排查,该错误与泛型限定<T extends AbstractEntity & WithTranslationKey>直接相关——移除WithTranslationKey后错误消失。Spring Boot 2.5版本无此问题,疑问这是否属于Spring Boot 3的Bug。
简化代码示例
1. 抽象实体基类
@MappedSuperclass @Data @NoArgsConstructor @AllArgsConstructor public abstract class AbstractEntity { @Id @GeneratedValue private Long id; }
2. 用户实体类
@Entity(name = "USERS") @Data @NoArgsConstructor public class User extends AbstractEntity implements WithTranslationKey { private String name; @Override public String getTranslationKey() { return name; } @Builder User(Long id, String name) { super(id); this.name = name; } }
3. 抽象翻译实体类
@MappedSuperclass @Data @ToString(callSuper = true) @EqualsAndHashCode(callSuper = true) @NoArgsConstructor public abstract class AbstractTranslationEntity<T extends AbstractEntity & WithTranslationKey> extends AbstractEntity { @JoinColumn(name = "CLASSIFIER_ID", nullable = false) @ManyToOne(fetch = FetchType.LAZY, optional = false) private T classifier; @Column(name = "LOCALE_CODE", length = 15, nullable = false) private String localeCode; AbstractTranslationEntity(Long id, T classifier, String localeCode) { super(id); this.classifier = classifier; this.localeCode = localeCode; } }
4. 用户翻译实体类
@Data @NoArgsConstructor @ToString(callSuper = true) @EqualsAndHashCode(callSuper = false) @Entity @Table(name = "USER_TRANSLATION") public class UserTranslation extends AbstractTranslationEntity<User> { private String translation; @Builder UserTranslation(Long id, User classifier, String localeCode, String translation) { super(id, classifier, localeCode); this.translation = translation; } }
5. 仓库接口定义
@NoRepositoryBean public interface AbstractTranslationRepository<C extends AbstractEntity & WithTranslationKey, T extends AbstractTranslationEntity<C>> extends CrudRepository<T, Long> { List<T> findTranslationsByClassifierId(Long classifierId); } public interface UserTranslationRepository extends AbstractTranslationRepository<User, UserTranslation> { }
问题分析与解决方案
这并非Spring Boot 3本身的Bug,而是Hibernate 6.x(Spring Boot 3默认依赖版本)在处理带多重边界的泛型类型时,元模型解析逻辑发生变化导致的问题。Hibernate 5.x(Spring Boot 2.5默认版本)对泛型边界解析更宽松,能正确识别AbstractEntity上的id属性;但Hibernate 6.x在处理T extends AbstractEntity & WithTranslationKey这类多重边界时,可能优先将T解析为WithTranslationKey接口类型,进而找不到id属性。
可行的解决方案:
- 明确属性路径:修改仓库方法签名,用下划线明确指定属性层级:
List<T> findTranslationsByClassifier_Id(Long classifierId); - 手动编写JPQL:绕过Spring Data的方法名自动解析,直接定义查询语句:
@Query("SELECT t FROM #{#entityName} t WHERE t.classifier.id = :classifierId") List<T> findTranslationsByClassifierId(@Param("classifierId") Long classifierId); - 调整泛型约束结构:如果业务允许,让
WithTranslationKey接口继承AbstractEntity,或者拆分多重边界的泛型定义,避免同时使用类和接口作为泛型边界。
内容的提问来源于stack exchange,提问作者never

