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