报错异常如下:
Exception in thread"main"org.hibernate.NonUniqueObjectException:
a different object with the same identifier value was already exit。
报错原因:
异常解释 :不是唯一对象异常,即hibernate的session中存在不同的但是主键相同的对象。所以在执行session.saveOrUpdate方法时不知道去保存那个对象。
出现概率:我遇到的这个问题,有的时候不会报错,但是绝大部分是会报错的。
解决方案:
先上代码,更加直观。
#controller层代码#
@Action("saveUser")
public void saveUser() {
try {
/*名字不能为空**/
if (StringUtils.isEmpty(this.user.getUserName())) {
Logger.error("userName can not be empty");
renderState(false);
return;
}
/*手机号码不能为空*/
if (StringUtils.isEmpty(this.user.getMobile())) {
Logger.error("mobile can not be empty");
renderState(false);
return;
}
if (!MobileUtils.isMobile(this.user.getMobile())) {
Logger.error("Wrong format of mobile phone ");
renderState(false);
return;
}
User exitUser = channelUserManager.getChannelUser(this.user.getId());
if (exitUser.getStatus != 1) {
userManager.saveUser(this.user);
renderState(true);
}else{
renderState("操作失败,该用户状态被禁用");
}
} catch (Exception e) {
e.printStackTrace();
}
renderState(false);
#service层代码#
public void saveUser(User user) {
if (user == null) {
return;
}
channelUserDao.save(channelUser);
}
#dao层代码(通用save方法)
public void save(T data) {
Assert.notNull(data);
getSession().saveOrUpdate(data);
}
上面代码执行saveUser()方法时会报这个错误。它最终调用的是getSession().saveOrUpdate()这个方法。顺便说一下session是hibernate的session,是用来操作数据库的,不是servlet中的session,cookie。
分析:controller层执行保存user这个方法时,他首先接受来自前端编辑后的user对象(new)。根据其id查询出原来的对象(old),判断他的status是否等于1。也许是其他逻辑,反正就是你需要根据主键去查出原来的对象(old),进行一些逻辑判断或者操作,然后再把这个新的对象(new)保存进去。然而你就会发现在session中存在了两个主键相同的对象,第一个对象是查出来的,第二个是你要保存的,由于主键相同,hibernate无法识别保存那个对象,这里应该可以理解为,saveOrUpdate方法是根据主键来更新的,session去执行更新操作时,存在两个主键一样的对象,让它感到矛盾,就抛出了异常。
如何解决:
- 使用getSession().merge(Object object)
在dao层做处理,由于上面的那个是通用的,重新写一个save方法。
public void save(User user) {
Assert.notNull(user);
Object merge = getSession().merge(user);
User mergeUser=(User) merge;
getSession().saveOrUpdate(mergeUser);
}
getSession().merge方法它会在 session 缓存中找到持久化对象(old),把新对象(new)的属性赋过去,再保存原session中的持久化对象。
merge是合并的意思,将新对象的属性值赋值给之前查出来的那个,再将old保存进去。
- 这个方法其实和第一个方法的原理相同,
exitUser.setUserName(this.user.getUserName());
exit.setStatus(this.user.getStatus());
exit.setMobile(this.user.getMobile());
exit.setRemark(thisuser.getRemark());
userManager.saveUser(exitUser);
这次我保存的还是我查出来的那个对象,但是某些属性值给改成了新对象的值,这样session中就不会有NonUniqueObject异常了。
拓展:
想要真正了解核心,就要了解hibernate对象的三种状态。
可参考博客:https://blog.csdn.net/pangqiandou/article/details/53386589