hibernate 批量保存数据时存在唯一键unique值重复时报错的解决方式

hibernate 批量保存数据时存在唯一键unique值重复时报错的解决方式 ( 主键策略为indentity时可用)

测试的数据库:mysql
其中Teacher的主键策略分别为以下几种
//Teacher1  -- native
//Teacher2  -- hilo
//Teacher3  -- indentity
//Teacher4  -- increment
4种主键策略的测试结果:主键方式仅native或indentity可以完成该操作

测试的项目文件:

点击进入项目资源链接页

在进行保存具有重复的唯一键值时出现以下错误:

错误代码:org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions WARN: SQL Error: 1062, SQLState: 23000

an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: null id in xxx entry (don’t flush the Session after an exception occurs)

本来以为解决不了了,通过后台程序实现这个功能:就是通过从数据库中取得已存在的数据对象集合与excel表获得的对象集合进行比较,如果存在则移除excel表取得的数据集合对象中的该对象(只保存未存在的数据场景,根据教工号判断)或者对该对象进行Oid赋值(保存未存在的数据、已存在的数据进行更新的场景),然后在for遍历这个excel的集合对象进行使用session.save()或者session.saveOrUpdate()方法。

后来发现junit中未报错,数据库表中却多出来一条记录,使用debug发现有一个实体类对象为null,进行了保存,然后发现自己模拟的对象集合的引用对象写错了,更正之后可以进行正确的保存操作了。经测试:在try代码块中未使用session.flush()两种方案均能解决错误问题,且数据能进行保存。

如果批量插入数据较多在一定数量时可以进行if(index % 20 == 0){session.flush(); session.clear();} ,只有方案一能够使用,方案二将会仍出现该错误。 以下就是我的解决方案,可以正常使用,不过目前博主对session.clear();有所疑惑,我觉得它当前场景(catch中)的作用是将该重复的实体对象从session中清除了。如果有正确的观点或其他见解,欢迎各位看官不吝指教~

解决方案一: 可以在操作一定数量时进行调用session.flush(); session.clear();

使用session.clear();

//session获取、事务开启
for(Entity e: es){
        try{  //新数据进行保存操作
            session.save(e);

        }catch(ConstraintViolationException e){  //唯一键重复时
            session.clear();

        }

    }
//最后session关闭、事务提交

解决方案二:不适合使用在一定数量时调用session.flush(); session.clear(); 的场景,否则仍会报出该错误

设置session.setFlushMode(FlushMode.NEVER);

//session获取、事务开启
session.setFlushMode(FlushMode.NEVER);
for(Entity e: es){
    try{  //新数据进行保存操作
        session.save(e);
    }catch(ConstraintViolationException e){  //唯一键重复时           
    }
}
    //最后session关闭、事务提交

其中可在catch中进行重复数据的处理操作,比如更新。Entity e 是指实体类对象,es是指该实体类的集合。假设e指teacher,该实体类包括主键id,教工号是唯一键,备注等。

下面使用第一种进行介绍更新操作:


    Session session = xxx.openSession();        //获取session,具体session怎么获取就不介绍了

    Transaction transaction = session.beginTransaction();  //开启事务

    for(Entity e: es){   //遍历该对象数据集合,具体数据就不模拟了

            try{
                session.save(e);

            }catch(ConstraintViolationException e){  //已存在的数据
                session.clear();  
                Integer id = (Integer) session.createQuery("select 
                id from Teacher where teacherNo = ?")
                .setString(0, e.getTeacherNo()).uniqueResult();  //首先获取该重复对象的oid
                e.setId(id);  //设置游离对象
                e.setMemo("重复唯一键");    
                session.update(e);  
                session.flush();  //刷新缓存,同步更新数据库
            }

        }


        transaction.commit();   //提交事务  

        session.close();    // 关闭Session

以上只是我找到的解决办法,且仅凭自己初步理解难免有错误,如果有更好见解或有所指教,欢迎各位指出。

你可能感兴趣的:(hibernate)