Spring 实体正在修改,但什么都没有改变

Spring entity is being modified when nothing has changed

我正在使用 Spring Roo 创建一个带有用户管理的简单 Web 应用程序。我有一个 AppUser 实体,其中(除其他外)有一个 password 字段,在更新实体的其他属性时,该字段被设置为空,即使我根本没有更改它。

为了实现更新 AppUser 实例的用例,Roo 生成了两个方法 updateFormupdate

前一种方法为编辑实体准备表单:

@RequestMapping(value = "/{id}", params = "form", produces = "text/html")
public String updateForm(@PathVariable("id") Long id, Model uiModel) {
    AppUser appUser = AppUser.findAppUser(id);
    populateEditForm(uiModel, appUser);
    System.out.println("On Update Form - " + appUser);
    return "users/update";
}

第二种方法检查验证错误并将实体合并到 JPA 上下文中:

@RequestMapping(method = RequestMethod.PUT, produces = "text/html")
public String update(@Valid AppUser appUser, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
    System.out.println("On Update - " + appUser);
    if (bindingResult.hasErrors()) {
        populateEditForm(uiModel, appUser);
        return "users/update";
    }
    uiModel.asMap().clear();
    appUser.merge();
    return "redirect:/users/" + encodeUrlPathSegment(appUser.getId().toString(), httpServletRequest);
}

编辑表单中 password 字段的默认值为空,但在它的 mutator 方法上 setPassword,当发送 null 或空白时我不会更新密码。

public void setPassword(String password) {
    // Don't update password if null or blank is sent
    if (password != null && !password.isEmpty()) {
        String encodedPassword = passwordEncoder.encodePassword(password, null);
        this.password = encodedPassword;
    }
}

标准输出中的结果如下:

On Update Form - AppUser[username=user,password=04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb,enabled=true,appRole=AppRole[rolename=ROLE_USER,id=2,version=1],customer=Customer[code=<null>,description=default,address=<null>,cap=<null>,locality=<null>,province=<null>,telephone=<null>,fax=<null>,vat=<null>,fiscalCode=<null>,sex=<null>,customerType=<null>,insertionDate=<null>,lastVisit=<null>,id=0,version=<null>],id=2,version=0]

On Update - AppUser[username=user,password=<null>,enabled=true,appRole=AppRole[rolename=ROLE_USER,id=2,version=1],customer=Customer[code=<null>,description=default,address=<null>,cap=<null>,locality=<null>,province=<null>,telephone=<null>,fax=<null>,vat=<null>,fiscalCode=<null>,sex=<null>,customerType=<null>,insertionDate=<null>,lastVisit=<null>,id=0,version=<null>],id=2,version=0]

明明是改密码了,但我不明白在哪里

编辑:

我用来更新 AppUser 信息的 jspx 视图如下:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:field="urn:jsptagdir:/WEB-INF/tags/form/fields" xmlns:form="urn:jsptagdir:/WEB-INF/tags/form" xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <form:update id="fu_it_digitelematica_leovince_domain_AppUser" modelAttribute="appUser" path="/users" versionField="Version" z="9/aOTZ+wTF1ijLMgiWkx1JcbpG8=">
        <field:input field="username" id="c_it_digitelematica_leovince_domain_AppUser_username" required="true" z="H3JOKuXes6/RLOnAd4x1v0j3Njo="/>
        <field:input field="password" id="c_it_digitelematica_leovince_domain_AppUser_password" required="false" type="password" z="user-managed"/>
        <field:checkbox field="enabled" id="c_it_digitelematica_leovince_domain_AppUser_enabled" z="4WIlYpFXs46FLp4l8koJ2DXmWz8="/>
        <field:select field="appRole" id="c_it_digitelematica_leovince_domain_AppUser_appRole" itemValue="id" items="${approles}" path="/approles" required="true" z="KDeZRlxip/VWLtb/kN58hn2zZdg="/>
        <field:select field="customer" id="c_it_digitelematica_leovince_domain_AppUser_customer" itemValue="id" items="${customers}" path="/customers" required="true" z="rEMR1CzjRLuSkoIKTHmq4N+qZoI="/>
    </form:update>
</div>

您是否尝试从 field:input 中删除 "type=password" 属性?

也许,input.tagx 上有错误...您可以试试吗?

如果您检测到错误,您可以在 http://jira.spring.io/browse/ROO

上创建问题

这是标准方法:当您合并一个实体时,您覆盖它的所有值。让我解释一下:

  1. RequestHandler 创建一个新的 user 实例并用请求值填充它
  2. 附加 实例到 entityManager 并将其存储在数据库中。

关于第 2 点 entityManager 对 Request 及其值一无所知,因此,当您 合并 用户,如果password 字段是null,那将是数据库上字段的新值。这就是为什么 Spring Roo 生成的视图包括更新表单中的所有字段。

要解决此问题,您必须加载实体的 attached 实例并填充 binding 实例的缺失值(警告,未测试代码):

@RequestMapping(method = RequestMethod.PUT, produces = "text/html")
public String update(@Valid AppUser appUser, BindingResult bindingResult, Model uiModel, HttpServletRequest httpServletRequest) {
    System.out.println("On Update - " + appUser);
    if (bindingResult.hasErrors()) {
        populateEditForm(uiModel, appUser);
        return "users/update";
    }
    uiModel.asMap().clear();
    User attached = User.findUserById(user.getId());
    appUser.setPassword_encoded(attached..getPassword());
    appUser.merge();
    return "redirect:/users/" + encodeUrlPathSegment(appUser.getId().toString(), httpServletRequest);
}

您需要一个设置密码而不对其进行编码的修改器。

祝你好运!