Spring Boot 3.5.0中JPA查询返回实体的枚举字段为null,原生与非原生查询均异常
我来帮你梳理下这个问题的核心原因和解决方案,你遇到的情况大概率是依赖版本冲突+多数据源配置不匹配的双重问题,再结合Spring Boot从2.x升级到3.x的兼容性细节,我们一步步解决:
一、先搞定最紧急的:Hibernate依赖冲突
你当前的Gradle依赖里藏着一个致命问题——手动引入了和Spring Boot 3.5.0不兼容的Hibernate版本:
- Spring Boot 3.5.0属于Jakarta EE 9+生态,默认依赖Hibernate ORM 6.4.x
- 你手动加的
hibernate-entitymanager:5.6.14.Final是Hibernate 5.x的组件,在Hibernate 6.x里这个模块已经被合并到hibernate-core里了,完全不需要单独引入 - 同时你还指定了
hibernate-core:6.1.6.Final,比Spring Boot默认的版本旧,这会导致类加载混乱(比如你之前碰到的ClassNotFoundException),直接破坏JPA的枚举映射、实体转换逻辑
立刻修正的方法:删掉手动添加的Hibernate依赖,让Spring Boot的自动依赖管理来处理:
// 移除这两行旧依赖 // implementation 'org.hibernate:hibernate-core:6.1.6.Final' // implementation 'org.hibernate:hibernate-entitymanager:5.6.14.Final' // 只保留Spring Boot提供的starter即可,它会自动引入兼容的Hibernate版本 implementation "org.springframework.boot:spring-boot-starter-data-jpa:$springbootVersion"
二、修正JPA查询的写法(解决查不到/枚举null的问题)
1. 优先用非原生JPQL或派生查询
你之前的非原生查询写法有个小问题,like %:name%的JPQL语法需要用CONCAT来拼接,或者直接用Spring Data JPA的派生查询(更省心,还不容易错):
public interface BrandRepo extends JpaRepository<Brand, String> { // 方案1:正确的JPQL查询 @Query("select i from Brand i where i.name like CONCAT('%', :name, '%') and i.reviewStage = :stage") List<Brand> findByNameContainingAndReviewStage(@Param("name") String name, @Param("stage") ReviewStage stage, Pageable pageable); // 方案2:更简洁的派生查询(推荐,Spring Data自动生成SQL) List<Brand> findByNameContainingAndReviewStage(String name, ReviewStage stage, Pageable pageable); }
派生查询完全不需要自己写SQL/JPQL,Spring Data会自动处理枚举的参数绑定,不会出现查不到数据的情况。
2. 原生查询的枚举映射修正
如果你必须用原生查询,要注意JPA不会自动帮你把数据库的字符串转成枚举(非原生查询会自动处理),所以需要手动传入枚举的name(),同时确保查询结果的枚举字段能被正确映射:
@Query(value = "select * from brand where name like CONCAT('%', :name, '%') and review_stage = :stage", nativeQuery = true) List<Brand> findByNameLikeAndReviewStageNative(@Param("name") String name, @Param("stage") String stage, Pageable pageable); // 调用的时候传入枚举的name(): // brandRepo.findByNameLikeAndReviewStageNative("test", stage.name(), pageable);
三、多数据源配置的关键检查
你提到项目是多数据源,这很可能是枚举字段返回null的核心原因——Brand实体和Repo可能没有绑定到正确的EntityManagerFactory:
- 实体与Repo的包扫描:确保
@EnableJpaRepositories的basePackages包含BrandRepo的路径,@EntityScan包含Brand实体的路径,并且和对应数据源绑定:
@Configuration @EnableJpaRepositories( basePackages = "com.your.project.repository.brand", // 你的BrandRepo所在包 entityManagerFactoryRef = "brandEntityManagerFactory", // 对应数据源的EntityManagerFactory transactionManagerRef = "brandTransactionManager" ) @EntityScan(basePackages = "com.your.project.entity.brand") // Brand实体所在包 public class BrandDataSourceConfig { // 数据源、EntityManagerFactory、TransactionManager的配置... }
- EntityManager的枚举映射配置:在构建EntityManagerFactory时,明确指定枚举的编码策略,确保和实体的
@Enumerated(EnumType.STRING)一致:
@Bean public LocalContainerEntityManagerFactoryBean brandEntityManagerFactory(EntityManagerFactoryBuilder builder, DataSource brandDataSource) { Map<String, Object> jpaProps = new HashMap<>(); // 强制枚举按字符串映射,和实体注解匹配 jpaProps.put("hibernate.type.enum.encoding", "STRING"); return builder .dataSource(brandDataSource) .packages("com.your.project.entity.brand") .persistenceUnit("brandPU") .properties(jpaProps) .build(); }
四、最后验证数据库枚举值的一致性
虽然你说数据库里的字段有值,但还是要确认:数据库中resource_type和review_stage的字符串值,和Java枚举的name()完全一致(大小写、拼写、下划线都不能错)。比如Java里的ReviewStage.CONFIRM_APPEALED,数据库里必须是CONFIRM_APPEALED,不能有空格或者拼写错误。
总结
按这个顺序排查:
- 先修复依赖冲突,这是所有诡异问题的根源
- 换成派生查询或正确的JPQL查询,避免原生查询的映射坑
- 检查多数据源的绑定,确保Brand实体和Repo用的是正确的EntityManager
- 验证数据库枚举值和Java枚举的一致性
内容来源于stack exchange

