【问题标题】:Spring entity is being modified when nothing has changed没有任何变化时正在修改 Spring 实体
【发布时间】:2015-01-24 23:24:03
【问题描述】:

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

为了实现更新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>

【问题讨论】:

  • 您使用哪个 jspx 视图来更新实体信息?你能提供给我们那个jspx视图代码吗?
  • 请在问题的最后找到更新AppUser信息的jspx视图的代码。
  • 这似乎与使用未配置的 Spring 的 DataBinder support.springsource.com/security/spring-mvc 的已知问题有关,但它仍然没有解释为什么要更改密码属性。
  • 您是否尝试从您的字段中删除“type=password”属性:输入?或许,input.tagx 有错误……你能试试吗?

标签: java spring spring-mvc spring-roo


【解决方案1】:

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

  1. RequestHandler 创建一个新的 user 实例并用请求值填充它
  2. 您将实例附加entityManager,然后将其存储在 DB 中。

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

要解决这个问题,您必须加载一个附加实体实例并填充绑定实例的缺失值(警告,未测试代码 ):

@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);
}

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

祝你好运!

【讨论】:

  • 感谢您的回答,确实是使用请求参数初始化的全新 AppUser 实例。出于某种原因,我认为这将是同一个实例,但更改了绑定到它的属性。现在我知道我需要一些自定义逻辑来处理我需要的部分更新。
【解决方案2】:

您是否尝试从您的字段中删除“type=password”属性:输入?

也许,input.tagx 有错误...你能试试吗?

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

【讨论】:

  • 我试过你的建议,但结果是一样的。检查 input.tagx 源代码我发现主要区别在于,在一种情况下它使用 Spring 的输入标签,而在另一种情况下它使用密码标签。使用最新的,输入是一个 HTML 密码字段,没有提供默认值。
猜你喜欢
  • 2015-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-04
  • 1970-01-01
  • 1970-01-01
  • 2012-10-12
  • 2021-06-29
相关资源
最近更新 更多