Hibernate循环批量插入仅一条成功并抛NonUniqueObjectException问题求助
解决Hibernate批量插入时的
NonUniqueObjectException异常问题 咱们先拆解下你遇到的问题:循环批量插入多条FeeTermDates记录时,只插入了一条还抛出了NonUniqueObjectException,异常提示里的[com.rasvek.cg.entity.FeeTermDates#0]已经给出了关键线索——所有新建的FeeTermDates实例主键都是0,Hibernate的Session会把主键相同的对象判定为同一个,自然就会报错。下面一步步给你解决:
一、核心问题分析
- 主键生成策略错误:你的
FeeTermDates实体主键没有配置自动生成规则,导致所有新实例的主键默认都是0,Session认为这是同一个对象,触发非唯一异常。 - 关联对象处理不当:你手动新建了
FeeTerms对象并设置ID,这个对象是脱管状态(不在Session管理范围内),Hibernate无法正确处理关联关系,也可能间接导致主键冲突。 - 批量插入逻辑未优化:循环中直接
save虽然能运行,但没有做批量处理,既影响性能也容易引发Session缓存溢出。
二、分步解决方案
1. 修正实体类的主键生成策略
打开FeeTermDates实体类,给主键字段配置自动生成策略,比如针对MySQL等支持自增的数据库:
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) // 自动使用数据库自增主键 private Long id; // 主键类型建议用Long,避免溢出
如果是用XML映射文件,配置如下:
<id name="id" type="java.lang.Long"> <generator class="identity"/> <!-- 对应数据库自增策略 --> </id>
这样Hibernate会自动为每个新实例生成唯一的主键值,不会再出现全是0的情况。
2. 修正关联对象的获取方式
不要手动新建FeeTerms对象,而是从Session中获取持久态的实例,这样Hibernate能正确管理关联关系:
// 替换原来的新建FeeTerms代码 FeeTerms feeTerms = currentSession.get(FeeTerms.class, termid); if (feeTerms == null) { // 处理termId不存在的情况,比如抛出异常或者返回失败 System.err.println("不存在ID为" + termid + "的FeeTerms记录"); return false; }
3. 优化批量插入逻辑(可选但推荐)
为了提升批量插入的性能,避免Session缓存溢出,建议开启Hibernate批量支持,并在循环中分批flush:
第一步:添加Hibernate批量配置
在你的Hibernate配置文件(比如hibernate.cfg.xml或者application.properties)中添加:
hibernate.jdbc.batch_size = 50 hibernate.order_inserts = true hibernate.order_updates = true
第二步:修改DAO层的循环逻辑
@Override public boolean addTermdates(FeeTermDates feetermdates) { int termid=feetermdates.getFeeTerms().getTermId(); // 获取持久态的FeeTerms对象 FeeTerms feeTerms = currentSession.get(FeeTerms.class, termid); if(feeTerms == null) { System.err.println("不存在ID为" + termid + "的FeeTerms记录"); return false; } boolean success = false; try { currentSession=sessionFactory.getCurrentSession(); String stringDate=feetermdates.getDate(); String val[]=stringDate.split(","); int batchSize = 50; // 每50条批量提交一次 for(int i=0; i<val.length; i++) { String date = val[i]; FeeTermDates feeDate=new FeeTermDates(); feeDate.setFeeTerms(feeTerms); feeDate.setDate(date); currentSession.save(feeDate); // 每batchSize条记录flush一次,清空Session缓存 if ((i + 1) % batchSize == 0) { currentSession.flush(); currentSession.clear(); } } // 处理最后一批不足batchSize的记录 currentSession.flush(); success=true; } catch (Exception e) { e.printStackTrace(); } return success; }
三、额外注意点
- 确保
FeeTermDates的equals和hashCode方法是基于主键或唯一业务字段实现的,避免Hibernate判断对象重复时出错。 - 检查Service层的
@Transactional注解是否生效,确保事务覆盖了整个批量插入的过程。
内容的提问来源于stack exchange,提问作者bharath
相关产品推荐
相关产品推荐

