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

来源:互联网 发布:中国新歌声网络主播 编辑:程序博客网 时间:2024/06/06 10:45

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

测试的数据库:mysql其中Teacher的主键策略分别为以下几种//Teacher1  -- native//Teacher2  -- hilo//Teacher3  -- indentity//Teacher4  -- increment4种主键策略的测试结果:主键方式仅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

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

0 0