Spring Boot从Oracle数据库取数异常:重复/缺失数据但计数正确
这个问题我之前排查过好几次,大概率是你的Inventory实体类的主键映射出了问题!
问题原因分析
JPA在处理查询结果时,是通过实体类上的@Id注解来识别唯一实体的。如果出现以下情况,就会导致你遇到的现象:
Inventory类的@Id注解没有标注在对应ORACLE_DATA1表主键的字段上- 表使用复合主键,但你只给单个字段加了
@Id,没有正确配置复合主键映射 @Id对应的字段在ORACLE_DATA1表中存在重复值
这种情况下,JPA会把具有相同主键值的行当成同一个实体对象,最终返回的列表里就会大量重复,而那些被重复主键覆盖的行就看起来像是“未获取到”,但因为SQL本身的计数是正确的,所以getCount()返回的数字和SQL Developer一致。
具体排查和解决步骤
检查实体类的主键映射
打开你的Inventory实体类,确保@Id注解准确标注在对应数据库表主键的字段上。比如如果ORACLE_DATA1的主键是DATA_ID,那实体类里的对应字段应该是:@Id @Column(name = "DATA_ID") private String dataId; // 类型要和数据库字段匹配处理复合主键场景
如果ORACLE_DATA1是复合主键(比如由ID和TYPE两个字段共同作为主键),你需要用@EmbeddedId或者@IdClass来定义复合主键:- 使用
@EmbeddedId的示例:// 定义复合主键类 @Embeddable public class InventoryPK implements Serializable { @Column(name = "ID") private String id; @Column(name = "TYPE") private String type; // 必须重写equals和hashCode方法 } // 在实体类中使用 @Entity public class Inventory { @EmbeddedId private InventoryPK pk; // 其他字段... }
- 使用
验证主键字段的唯一性
登录SQL Developer,执行select 主键字段, count(*) from ORACLE_DATA1 group by 主键字段 having count(*) > 1,检查主键字段是否存在重复值。如果有,要么修正数据,要么重新确认表的主键定义。临时排查:查看原始查询结果
可以在Repository中添加一个返回原始数组的方法,直接查看SQL查询的原始结果是否正确:@Query(nativeQuery = true, value = "select * from ORACLE_DATA1") List<Object[]> findAllRawData();在Service中调用这个方法并打印结果,如果原始数据和SQL Developer一致,那就100%是实体映射的问题。
顺便提下代码里的小问题
你的GetInventoryService中getCount()方法的@Autowired注解加错位置了,应该放在字段上,而不是方法上(而且你已经在字段上注入过searchInventoryRepository了,不需要重复注入):
// 错误写法 @Autowired public int getCount() { return searchInventoryRepository.getCount(); } // 正确写法 public int getCount() { return searchInventoryRepository.getCount(); }
按照上面的步骤排查,应该就能解决你的问题啦!
内容的提问来源于stack exchange,提问作者shasha0607

