Native SQL在Spring Boot与SQL Workbench执行结果不一致问题排查
问题原因及解决方案
核心原因
你的ExperimentDetails类用@Id标注了externalId字段,但SQL查询结果中所有记录的external_id(对应externalId)都是同一个值(查询条件明确指定了ce.external_id = 'someExperimentID')。
JPA的EntityManager会通过@Id字段识别唯一实体,当所有结果行的Id值相同时,它会认为这些都是同一个实体,只会创建第一个实体对象并缓存,后续行直接复用该缓存对象,最终导致所有记录显示为第一条的数据内容。
解决方案
方案一:移除JPA实体注解(推荐,符合你原本的需求)
既然你不想用@Entity注解,直接将ExperimentDetails作为普通DTO类使用,通过调整SQL列别名或配置命名策略实现字段映射:
- 修改POJO类:移除
@Entity和@Id注解
package com.pdb.testdbconn.dto; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class ExperimentDetails { private String externalId; private String isid; private String name; private String controlledStatus; private String jurisdictionName; private String resultComments; private String codeName; private String corporateId; }
- 调整SQL查询:给列添加别名,匹配POJO的驼峰字段名
@Query(value = "Select ce.external_id as externalId, ce.isid as isid, ji.name as name, ji.controlled_status as controlledStatus, " + "ji.jurisdiction_name as jurisdictionName, ji.result_comments as resultComments," + "ji.code_name as codeName, s.corporate_id as corporateId " + "from cs_jurisdiction_information ji " + "Join substance s on ji.substance_id = s.id " + "Join controlled_event ce on ce.id = s.controlled_event_id " + "and ce.calling_system_id = 402 and ji.controlled_status = 'CONTROLLED' " + "and ce.external_id = :externalId", nativeQuery = true) List<ExperimentDetails> getExperimentDetails(@Param("externalId") String externalId);
或者也可以通过配置Spring Boot的JPA命名策略自动处理下划线转驼峰,无需手动加别名:
在application.properties中添加:
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
方案二:使用复合主键(若必须保留@Entity)
如果需要将ExperimentDetails作为JPA实体,需定义复合主键来唯一标识每条记录:
- 创建复合主键类(实现
Serializable接口)
package com.pdb.testdbconn.dto; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @Data @AllArgsConstructor @NoArgsConstructor public class ExperimentDetailsId implements Serializable { private String externalId; private String jurisdictionName; // 与externalId组合唯一标识记录 }
- 修改实体类:使用
@IdClass指定复合主键
package com.pdb.testdbconn.dto; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.IdClass; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor @Entity @IdClass(ExperimentDetailsId.class) public class ExperimentDetails { @Id private String externalId; @Id private String jurisdictionName; private String isid; private String name; private String controlledStatus; private String resultComments; private String codeName; private String corporateId; }
这样JPA会通过复合主键识别不同实体,不会复用缓存对象。
内容的提问来源于stack exchange,提问作者Altaf
相关产品推荐
相关产品推荐

