文前碎言:
其实本来昨天中午就抽时间把思路写好了,结果一直太忙了,到今天现在才忙完,便想着一定要写了,言归正文哈!
原理介绍:
当我们添加或者修改数据的时候,如果操作的数据对象Employee有关联对象,而此时并未添加关联对象的值,也就是如图所示不选择添加部门
也就是为空或者没有设置值进去,那么此时将对象传入后台进行保存的时候,就会同时传入一个没有id的关联对象Department,
而在发送保存的请求到达后台之前,对应的Controller会执行@ModelAttribute(“beforeupdate”)方法,会根据需要修改的对象Employee的id去数据库中查询,
/\*\*
\* 这个方法会在请求到达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对象进行对比,
/\*\*
\* 这里再把查询出来的对象和前台传入的对象进行比较,不一样的,就直接更新了,然后再把新的对象传入到保存的方法中去保存
\* @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方法的时候,
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);
解决思路:
情形一:在没有选择Department而传入的对象id为null的情况,此时我们可以在Service里面保存之前,判断Employee对象的部门id是否为null,如果是,则直接将部门对象设置为null,
// 这里之所以要判断部门对象不为空而部门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有值的情况
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。