Hibernate原生查询批量插入OneToMany关联数据及返回ID问题
问题1:插入后获取自增ID报错解决
你使用的是SQL Server数据库,默认INSERT语句不会返回结果集,所以调用getSingleResult会抛出异常。要返回自增主键需要在INSERT语句中添加OUTPUT INSERTED.主键列名子句,同时禁止直接拼接SQL防止注入风险,正确写法如下:
// pack_group表本身不存在packArticles字段,该关联关系是通过子表的外键维护的,所以插入语句不需要加该字段 String query = "insert into pack_group(packing_nr, description, qty, delivered_time) " + "OUTPUT INSERTED.id " + "values (?, ?, ?, ?)"; Query q = entityManager.createNativeQuery(query); // 按顺序绑定参数,不要用String.format拼接 q.setParameter(1, packGroup.getPackingNr()); q.setParameter(2, packGroup.getDescription()); q.setParameter(3, packGroup.getQty()); q.setParameter(4, packGroup.getDeliveredTime()); Long id = ((Number) q.getSingleResult()).longValue();
问题2:批量保存关联数据的实现方案
推荐优先使用JPA自带批量配置,实现成本低性能达标,如有特殊要求再用原生SQL方案。
方案1:JPA批量插入配置(推荐)
无需修改现有业务逻辑,仅需添加配置即可开启批量插入,性能提升非常明显:
- 新增JPA配置(application.yml为例)
spring: jpa: properties: hibernate: jdbc: batch_size: 500 # 每累积500条语句批量执行一次,可根据实际情况调整 order_inserts: true # 按实体类型排序插入语句,满足批量执行要求 order_updates: true generate_statistics: false # 调试时可开启,能在日志中查看批量执行效果
- 业务逻辑调整为批量保存
// 先组装所有父实体和关联的子实体 List<PackGroupEntity> allGroups = new ArrayList<>(); for (Map.Entry<Integer, List<RecivedArticlesDTO>> entry : groupedArticles.entrySet()) { PackGroupEntity packGroup = new PackGroupEntity(); packGroup.setPackingNr(entry.getKey()); // 其他父类字段赋值逻辑 List<PackArticlesEntity> listWithArticles = new ArrayList<>(); entry.getValue().forEach(pack -> { PackArticlesEntity packArticle = new PackArticlesEntity(); packArticle.setQty(pack.getQty()); packArticle.setPackGroup(packGroup); // 手动绑定父实体,避免外键为空 // 其他子类字段赋值逻辑 listWithArticles.add(packArticle); }); packGroup.setPackArticles(listWithArticles); allGroups.add(packGroup); } // 一次性保存所有父实体,因为配置了级联保存,子实体会自动批量插入 packGroupRepository.saveAll(allGroups);
方案2:原生SQL批量实现
如果必须用原生SQL实现,步骤如下:
- 先批量插入所有
PackGroupEntity,通过OUTPUT子句一次性返回所有生成的主键ID,建立packingNr和主键ID的映射关系 - 给所有
PackArticlesEntity绑定对应的父实体ID,再批量插入所有子实体
SQL Server批量插入支持多值写法,性能更高:
-- 批量插入父实体并返回主键 INSERT INTO pack_group(packing_nr, description, qty, delivered_time) OUTPUT INSERTED.id, INSERTED.packing_nr VALUES (?,?,?,?), (?,?,?,?), (?,?,?,?) -- 按batch_size拼接多组值
内容的提问来源于stack exchange,提问作者xarqt
相关产品推荐
相关产品推荐

