07.關於解決保存關聯對象時產生的nTon問題

文前碎言:數據庫

其實原本昨天中午就抽時間把思路寫好了,結果一直太忙了,到今天如今才忙完,便想着必定要寫了,言歸正文哈!

原理介紹:

當咱們添加或者修改數據的時候,若是操做的數據對象Employee有關聯對象,而此時並未添加關聯對象的值,也就是如圖所示不選擇添加部門
image.png
image.png
image.png
也就是爲空或者沒有設置值進去,那麼此時將對象傳入後臺進行保存的時候,就會同時傳入一個沒有id的關聯對象Department,
image.png
而在發送保存的請求到達後臺以前,對應的Controller會執行@ModelAttribute(「beforeupdate」)方法,會根據須要修改的對象Employee的id去數據庫中查詢,app

/\*\*  
 \* 這個方法會在請求到達Controller以前執行,會將要修改(有id)的對象從數據庫中查詢出來  
  \* 由於懶加載的緣故,不會將關聯對象查詢出來。  
  \* @param id  
  \* @return  
  \*/  
@ModelAttribute("beforeupdate")  
public Employee before(Long id){  
    Employee employee = null;  
 if (id != null){  
        employee = employeeService.findById(id);  
  }  
    return employee;  
}

查詢出來以後,這裏的employee對象是一個持久化對象咱們別名爲EmployeePersist(之因此爲持久化對象,是由於和entitymanager發生了關係),而後在進入修改的Controller以後,會自動將EmployeePersist和須要保存的傳入的Employee對象進行對比,this

/\*\*  
 \* 這裏再把查詢出來的對象和前臺傳入的對象進行比較,不同的,就直接更新了,而後再把新的對象傳入到保存的方法中去保存  
  \* @param employee  
  \* @return  
  \*/  
@RequestMapping("/update")  
@ResponseBody  
public AjaxResult update(@ModelAttribute("beforeupdate")Employee employee, MultipartFile fileImage, HttpServletRequest req){  
    return saveOrUpdate(employee,fileImage, req);  
}

若是前臺傳來的Employee有和EmployeePersist不同的地方,那麼EmployeePersist就會自動進行更新其數據,而此時,也會將沒有id的關聯對象Department設置進EmployeePersist這個對象中,而後在Controller中調用對應的保存的方法,在service執行保存的時候,就會直接執行保存這一個EmployeePersist,也就是上面的saveOrUpdate(employee,fileImage, req)中的employee === EmployeePersist,當進入到SimpleJpaRepository Entitymanager對象執行persist方法的時候,spa

public class SimpleJpaRepository<T, ID extends Serializable> implements JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
    @Transactional  
    public <S extends T> S save(S entity) {  
        if (this.entityInformation.isNew(entity)) {  
            this.em.persist(entity);  
     return entity;  
      } else {  
            return this.em.merge(entity);  
      }  
    }
}
...
...
...

此時由於懶加載的緣故,會將EmployeePersist在數據庫中相關聯的對象Department查詢出來,而後進行修改/保存,結果發現,查詢出來的關聯對象Department的id不爲null,而此時由於是作的修改動做,又要將該Department的id設置爲null,可是持久化對象的id不能設置爲null,因此最後就形成了nTon問題(之因此nTon,是有可能前臺作修改的時候選擇了部門也會形成這種狀況,即修改前的部門id爲2,修改後的部門id爲3,就會形成2);
image.pngcode

解決思路:

情形一:在沒有選擇Department而傳入的對象id爲null的狀況,此時咱們能夠在Service裏面保存以前,判斷Employee對象的部門id是否爲null,若是是,則直接將部門對象設置爲null,orm

// 這裏之因此要判斷部門對象不爲空而部門id爲空的狀況是由於在新增的時候若是不選擇部門會傳一個department對象過來,  
// 而又沒有id,於是須要把則個對象設置爲null  
if (employee.getDepartment() != null && employee.getDepartment().getId() == null) {  
    employee.setDepartment(null);  
}

那麼此時咱們再去保存的時候,實際就是保存的部門爲null的Employee對象;
情形二:傳入的部門對象有id,而咱們在保存的時候,不可以直接更改關聯對象Department對象的id,即nTon,此時咱們能夠在執行
@ModelAttribute(「beforeupdate」)方法的時候,將查詢出來的Employee對象的關聯對象Deparmtment設置爲null,對象

@ModelAttribute("beforeupdate")  
public Employee before(Long id){  
    Employee employee = null;  
 if (id != null){  
        employee = employeeService.findById(id);  
        employee.setDepartment(null);  
  }  
    return employee;  
}

這樣,咱們在進行到SimpleJpaSpecification內部調用Entitymanager對象執行persist方法的時候,此時的Employee查村出來的關聯對象已經爲null了,就能夠直接進行保存從而完成修改。
注:上面一個是在service裏面的保存方法設置關聯對象Department爲null,這個前提是傳入的Department對象id爲null的狀況,而咱們不能設置一個關聯對象id爲null,另外一個實在Controller裏面設置根據id查詢出來的Employee的關聯對象Department爲null,這一個是爲了解決前臺傳入的關聯對象id有值的狀況blog

相關文章
相關標籤/搜索