Spring Boot + JPA复合主键批量插入时消除冗余SELECT的Persistable接口实现问题
Spring Boot + JPA复合主键批量插入时消除冗余SELECT的Persistable接口实现问题
嘿,这个问题我之前批量导数据的时候也踩过一模一样的坑!确实,用复合主键的实体实现Persistable时,会卡在getId()的实现上,毕竟不像单一主键那样直接返回单个字段就行。咱们来一步步把它搞定:
首先得说清为什么要折腾这个:你现在用saveAll()的时候,Hibernate会对每个实体先跑一遍SELECT检查是否存在,这在批量插入几十万条数据时简直是灾难——冗余查询量直接翻倍。而实现Persistable接口,通过isNew()告诉Hibernate“这是新数据,直接插不用查”,就能解决这个问题,但必须实现getId()方法,复合主键的情况需要我们稍微调整下实现方式。
正确的实现方式
因为你的主键是复合类型PlayerCompositeId,所以实现Persistable的时候要指定泛型为这个复合主键类,然后在getId()方法里直接用实体已有的主键字段构造复合主键对象返回就行,不需要额外存主键实例。
修改后的完整Player实体代码如下:
@Entity @Table(name="player") @Setter @Getter @ToString @NoArgsConstructor @AllArgsConstructor @IdClass(PlayerCompositeId.class) public class Player implements Persistable<PlayerCompositeId> { @Id Long leagueId; @Id Long playerId; Integer numGames; @Override public boolean isNew() { // 因为是批量插入已知的全新数据,固定返回true即可 return true; } @Override public PlayerCompositeId getId() { // 用实体本身的两个主键字段构造复合主键对象返回 return new PlayerCompositeId(leagueId, playerId); } }
关键细节解释
- 泛型指定:实现
Persistable<PlayerCompositeId>,这样getId()的返回类型就和接口要求的抽象方法完全匹配了,不会再报编译错误 - getId()实现:不需要额外在实体里加一个PlayerCompositeId类型的字段,直接用已有的
leagueId和playerId构造实例返回,完全符合复合主键的要求 - isNew()逻辑:因为你确定是批量插入全新数据,没有重复主键的情况,所以固定返回
true即可,这样Hibernate就会跳过预插入的SELECT检查
验证效果
改完之后再执行saveAll()批量插入,配合你配置的hibernate.jdbc.batch_size=1000,你会发现之前那50k冗余SELECT语句完全消失了,所有操作都是批量插入,性能会提升一大截。
如果后续你有需要同时支持插入和更新的场景,再调整isNew()的逻辑就行(比如根据某个字段判断是否是新实体),但纯批量插入的场景下,这个实现就足够用了。
内容来源于stack exchange
相关产品推荐
相关产品推荐

