解决表单提交的数据丢失问题:
一、问题描述:
当我们在给前台页面设置修改功能的时候,因为有些信息是不允许进行修改的,所以在修改表单中没有相应的修改输入框,但是在修改表单的数据提交的时候,那些不允许修改的信息在数据库中却不见了。
二、原因分析:
提交表单的时候,因为在表单中没有那些不允许修改的信息相对应的数据,所以在执行SQL修改语句的时候,这些信息就被默认的null值替换了,相当于这些信息被修改为了null值(我的Dao层实现类是通过SpringDataJpa自动生成的)。
三、解决方案:
方案一:在修改表单中添加那些不允许修改信息的隐藏输入框。
优点:容易理解。
缺点:如果不允许修改的信息太多,代码量会比较大,另外这种方案的安全性确实是有一些低!
适用场景:字段较少的时候可取。
方案二:在domain实体类的不许修改的字段上加上@Column(updatable = false)这个标签。
优点:比较简单。
缺点:当真正需要对这些信息进行修改的时候就无法修改。
适用场景:永久不需要对该字段进行修改。
方案三:使用SpringMVC专门为我们提供的数据丢失解决方案(使@ModelAttribute注解)【推荐使用】
优点:当不能修改的信息较多时,减少代码量,更为安全。
缺点:较难理解。
操作:1、先在前台添加一个请求参数:obj.method="updateEmployee";
//添加或者修改用户 saveEmployee:function (){ //serializeObject:序列化表单中的数据,使之能完整提交 var obj = $("#updateFrom").serializeObject(); //id的那个隐藏输入框有值,就表示修改 var url = "/employee/addEmployee"; if (obj.id && obj.id > 0) { url = "/employee/updateEmployee"; //添加一个请求参数 obj.method="updateEmployee"; } //如果表单验证失败,就阻止表单提交【当表单内所有输入控件都验证通过的时候,这个方法返回true;validate:确认,使生效】 var valid = $("#updateFrom").form("validate"); if (!valid) return; $.post(url, obj, function (data) { //提示一下 $.messager.alert('消息', data.msg + " " + (data.exception ? data.exception : ""), "info"); if (data.status == 200) { //调用搜索页面数据的方法 window.method.search(); //关闭window窗口 $("#win").window("close"); } }, "json"); }
2、在Controller控制层添加如下一个方法(方法上加上注解@ModelAttribute("updateEmployee") )。
/** * 修改员工之前,通过id查询员工【解决数据丢失问题】 * @ModelAttribute("updateEmployee") * SpringMVC框架专门提供来解决数据丢失问题 * 加了@ModelAttribute注解的方法会在当前类的所有处理请求的方法之前都会先执行此方法 * */ @ModelAttribute("updateEmployee") public Employee findEmployeeToEdit(Long id, String method){ //只有修改员工的方法执行之前先去查询员工(因为每个方法执行前都会执行这个方法) if(id != null && "updateEmployee".equals(method)){ Employee employee = employeeService.findOneById(id); //员工对象和它关联的部门对象,断开关系,部门就变成临时对象(注意:如果查询的表有相关联的对象,必须要先解除关联,不然会报 n to n错误) employee.setDepartment(null);//持久对象的主键值不能被修改,所以必须要断开联系 return employee; } return null; }
3、在修改方法的参数列表上添加@ModelAttribute("updateEmployee")注解。
/** * 修改员工 * @ModelAttribute("updateEmployee")写在参数列表前 * 先执行findEmployeeToEdit方法得到一个持久状态的对象(懒加载的特点) * 再从表单请求中获取employee对象 * 合并非空属性值,最后以数据库查询出来的持久状态的对象为准,再传入当前方法的参数列表中 * 下面形参列表的employee其实就是前台传过来的数据和上面方法传过来的数据的合并(主体是上面方法查到的数据, * 如果前台传来有数据,就把其中对应的数据覆盖,前台对应数据为空就继续用查询到的数据) * * 加了@ResponseBody注解,表示处理异步请求,SpringMVC内部默认使用jackson工具转化为JSON进行返回,不跳转页面 */ @ResponseBody @RequestMapping("/updateEmployee") public ResultJson updateEmployee(@ModelAttribute("updateEmployee")Employee employee){ try { employeeService.save(employee); } catch (Exception e) { e.printStackTrace(); return new ResultJson(500,"员工修改失败!",e.getClass().getName() + ": " + e.getMessage()); } return new ResultJson(200,"员工修改成功!",null); }
SpringMVC的@ModelAttribute("updateEmployee") 注解执行原理分析:
1、添加有该注解的(findEmployeeToEdit)方法会在当前类的所有处理请求的方法之前执行,因此必须写先在前台页面添加一个请求参数:obj.method="updateEmployee"; 然后在 if(id != null && "updateEmployee".equals(method)) 语句中通过请求参数确保该方法只对修改方法(updateEmployee)起作用;
2、findEmployeeToEdit执行完毕后,会或得一个持久状态的employee对象(懒加载特性),然后注解@ModelAttribute通过名字updateEmployee找到修改方法的参数列表的同名注解updateEmployee(@ModelAttribute("updateEmployee")Employee employee);
3、然后以findEmployeeToEdit方法查询到的employee对象作为主体,用前台传过来的employee对象中有值的数据替换掉主体对象中相对应的数据,这样新得到的employee对象就既有了前台的修改数据,又保存了不可被修改的信息的数据。
注意:如果出现如图的 n to n 错误,就是因为持久对象的主键值不能被修改,所以必须要断开联系;例如:employee.setDepartment(null);