You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Hibernate循环批量插入仅一条成功并抛NonUniqueObjectException问题求助

解决Hibernate批量插入时的NonUniqueObjectException异常问题

咱们先拆解下你遇到的问题:循环批量插入多条FeeTermDates记录时,只插入了一条还抛出了NonUniqueObjectException,异常提示里的[com.rasvek.cg.entity.FeeTermDates#0]已经给出了关键线索——所有新建的FeeTermDates实例主键都是0,Hibernate的Session会把主键相同的对象判定为同一个,自然就会报错。下面一步步给你解决:

一、核心问题分析

  1. 主键生成策略错误:你的FeeTermDates实体主键没有配置自动生成规则,导致所有新实例的主键默认都是0,Session认为这是同一个对象,触发非唯一异常。
  2. 关联对象处理不当:你手动新建了FeeTerms对象并设置ID,这个对象是脱管状态(不在Session管理范围内),Hibernate无法正确处理关联关系,也可能间接导致主键冲突。
  3. 批量插入逻辑未优化:循环中直接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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:06:55