解決表單提交的數據丟失問題:
一、問題描述:
當我們在給前台頁面設置修改功能的時候,因為有些信息是不允許進行修改的,所以在修改表單中沒有相應的修改輸入框,但是在修改表單的數據提交的時候,那些不允許修改的信息在數據庫中卻不見了。
二、原因分析:
提交表單的時候,因為在表單中沒有那些不允許修改的信息相對應的數據,所以在執行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 + "\r\n" + (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);