如何基于JpaRepository实现仅增改不删数据、不改创建日期的saveAll方法
问题根因
Spring Data CrudRepository自带的saveAll()方法本身没有全表删除逻辑,你遇到的原有2500条数据被清空、仅剩余传入的154条数据,基本是两个原因导致:
- 自定义的
domServices.saveAll()业务逻辑存在错误,在执行保存前误调用了deleteAll()、表truncate这类全量删数操作,这是最高发的场景 - 实体关联配置错误:如果Dom和其他实体存在级联映射,配置了
cascade = CascadeType.ALL+orphanRemoval = true,且本次保存是从父实体侧触发,框架会自动把不在本次传入列表里的关联数据判定为孤儿数据执行删除
另外你贴的代码里存在一个显性配置错误:DomRepository的主键泛型写的是String,但Dom实体的主键是int类型,类型不匹配会触发很多意料之外的持久化异常,必须先修正。
高性能批量新增/更新实现方案(不删旧数据、不修改创建时间)
1. 先加固创建时间不被篡改的配置
你当前给dateCreation加的@Column(updatable = false)已经可以让JPA生成更新语句时自动忽略该字段,再做两处调整彻底锁死创建时间:
@Entity @DynamicUpdate @EntityListeners(AuditingEntityListener.class) public class Dom { @Id @GeneratedValue(strategy=GenerationType.AUTO) // 把基础类型int改成包装类Integer,避免int默认值0导致的主键识别异常 private Integer id; // 其余业务字段省略 @CreatedDate @Column(updatable = false, insertable = true) private Date dateCreation; // 注意:不要给dateCreation写setter方法,从代码层面禁止手动修改创建时间 // getter、构造方法省略 } // 修正Repository主键泛型,和实体主键类型保持一致 public interface DomRepository extends CrudRepository<Dom, Integer> { }
核心原则:所有更新场景下,既不要在代码里手动给dateCreation赋值,也不要在自定义SQL里写dateCreation的更新逻辑,创建时间完全交给JPA审计功能在数据插入时自动填充
2. 替换默认saveAll,实现高性能批量upsert
采用数据库原生upsert语法实现批量新增/更新,性能比JPA默认逐行merge的saveAll高10~100倍,且不会触碰表内不在本次传入列表里的原有数据:
在DomRepository中添加自定义批量操作方法,以MySQL为例:
@Modifying @Transactional @Query(value = """ INSERT INTO dom (id, 这里替换成你所有需要更新的业务字段名, date_creation) VALUES :domList ON DUPLICATE KEY UPDATE -- 这里只写需要更新的业务字段,绝对不要加date_creation的更新逻辑 业务字段1 = VALUES(业务字段1), 业务字段2 = VALUES(业务字段2) """, nativeQuery = true) void batchUpsert(List<Dom> domList);
如果使用PostgreSQL数据库,把ON DUPLICATE KEY UPDATE语法替换为PostgreSQL原生的ON CONFLICT (id) DO UPDATE SET语法即可。
3. 性能优化注意事项
- 单次批量提交的列表长度控制在200~500条,超过的话分批次提交,避免触发数据库单包大小限制
- 不要在批量保存逻辑里添加任何全表删除、条件清空数据的逻辑
- 给主键id加唯一索引,保证upsert语法的冲突判断正常生效
内容的提问来源于stack exchange,提问作者kiki keke
相关产品推荐
相关产品推荐

