記一次hibernate 的saveOrUpdate方法異常

報錯異常如下:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章