【问题标题】:Spring Formatter for input field输入字段的 Spring Formatter
【发布时间】:2014-12-10 12:48:08
【问题描述】:

我对 Spring 中的格式化程序有疑问。

我的选择框有一个格式化程序,例如:

public class SportTypeFormatter implements Formatter<SportType> {

    @Autowired
    private SportTypeRepository sportTypeRepository;

    @Override
    public String print(SportType sportType, Locale locale) {
        return String.valueOf(sportType.getTypeId());
    }

    @Override
    public SportType parse(String sportTypeId, Locale locale) throws ParseException {
        return sportTypeRepository.findSportTypeByTypeId(Long.valueOf(sportTypeId));
    }
}

在百里香中是这样的:

<select class="form-control" name="sportTypeId" th:field="*{person.sport.sportType}">
    <option th:each="spoType : ${allSportTypes}" th:value="${spoType.typeId}" th:selected="${spoType.typeId == person.sport.sportType}" th:text="#{${'login.sport.sportType.' + spo.typeId}}" >Sporttype</option>
</select>

这很简单,因为我只需要一个值(id)并且我将使用选择框。

但是如果我需要两个值怎么办?

建议我有电子邮件,我需要 id 和值(邮件地址)。我可以为电子邮件构建一个格式化程序,但我没有机会同时传输电子邮件和 ID。

在打印方法中我可以这样:

@Override
public String print(Email email, Locale locale) {
    return email.getId() + email.getEmail();
}

在百里香中:

<input id="email" type="text" class="form-control" th:field="*{{person.email}}" th:placeholder="#{login.email}" />

但是有了这个用户可以看到id。

如果我使用“标准”Spring 方式进行绑定,则会出现以下异常(这就是我使用格式化程序的原因):

{timestamp=Wed Dec 10 11:14:47 CET 2014, status=400, error=Bad Request, exception=org.springframework.validation.BindException, 
 errors=[Field error in object 'person' on field 'email': rejected value [com.sample.persistence.user.model.Email@0]; 
 codes [typeMismatch.person.email,typeMismatch.person.institutionEmployees.email,
        typeMismatch.email,typeMismatch.email,
        typeMismatch.email,typeMismatch.com.sample.persistence.user.model.Email,typeMismatch]; 
 arguments [org.springframework.context.support.DefaultMessageSourceResolvable: 
            codes [person.email,email]; 
            arguments []; 
            default message [email]]; 
            default message [Failed to convert property value of type 'com.sample.persistence.user.model.Email' 
                             to required type 'com.sample.persistence.user.model.Email' 
                             for property 'email'; 
                             nested exception is org.springframework.core.convert.ConversionFailedException: 
                             Failed to convert from type com.sample.persistence.user.model.Email 
                             to type @javax.persistence.OneToOne @javax.persistence.JoinColumn @com.google.gson.annotations.Expose 
                                     com.sample.persistence.user.model.Email for value 
                                     'com.sample.persistence.user.model.Email@0'; 
                            nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: 
                                     Provided id of the wrong type for class com.sample.persistence.user.model.Email. 
                                     Expected: class java.lang.Long, got class java.lang.String; 
                            nested exception is java.lang.IllegalArgumentException: 
                                     Provided id of the wrong type for class com.sample.persistence.user.model.Email. 
                                     Expected: class java.lang.Long, got class java.lang.String]], 
                                     message=Validation failed for object='person'. Error count: 1, path=/manageUsers/Ab-Soul/edit}

欢迎提出任何建议。

提前致谢。

1.编辑:

控制器方法

@RequestMapping(value = "/{login}/edit", method = RequestMethod.GET)
public ModelAndView editUserByLogin(@PathVariable("login") final String login) {
    final User currentUser = UserRepository.findPersonByLogin(login);

    ModelAndView mav = new ModelAndView(URL_EDIT_USER);
    mav.addObject(MODEL_USER, currentUser);

    return mav;
}

场景中,'admin' 获取所有当前用户的列表,如果他点击表格,requestmapping-method 将使用他点击的用户名被调用。

电子邮件类:

@Entity(name="Email")
public class Email implements Serializable
{
    private static final long serialVersionUID = 6891079722082340011L;

    @Id()
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Expose
    protected Long emailId;
    @Expose
    protected String value;

    //getter/setter

    @Override
    public boolean equals(Object obj)
    {
        if(obj instanceof Email){
            return value.equals(((Email) obj).getValue());
        }
        return false;
    }

    @Override
    public int hashCode()
    {
        return value.hashCode();
    }
}

2。编辑:

现在我已经将孩子的电子邮件字段更改为 emailChild

org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'person' on field 'child[0].emailChild': 
rejected value [com.sample.persistence.user.model.Email@0]; 
codes [typeMismatch.person.child[0].emailChild,
       typeMismatch.person.child.emailChild,
       typeMismatch.child[0].emailChild,
       typeMismatch.child.emailChild,
       typeMismatch.emailChild,
       typeMismatch.com.sample.persistence.user.model.Email,typeMismatch];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable: 
    codes [person.child[0].emailChild,child[0].emailChild]; 
arguments []; 
default message [child[0].emailChild]]; 
default message [Failed to convert property value of type 'com.sample.persistence.user.model.Email' to required type 'com.sample.persistence.user.model.Email' 
       for property 'child[0].emailChild'; 
nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type com.sample.persistence.user.model.Email
to type @javax.persistence.OneToOne @javax.persistence.JoinColumn @com.google.gson.annotations.Expose com.sample.persistence.user.model.Email 
for value 'com.sample.persistence.user.model.Email@0'; 
nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: Provided id of the wrong type for class com.sample.persistence.user.model.Email. Expected: class java.lang.Long, got class java.lang.String; 
nested exception is java.lang.IllegalArgumentException: Provided id of the wrong type for class com.sample.persistence.user.model.Email. Expected: class java.lang.Long, got class java.lang.String]

3.编辑:

为帖子添加控制器方法:

@RequestMapping(value = "/{login}/edit", method = RequestMethod.POST)
public ModelAndView updateUser(@PathVariable("login") final String login, @ModelAttribute(MODEL_USER) final Person person, BindingResult bindingResult,  final Model model) {
    Person repositoryPerson = personRepository.findPersonByLogin(login);

    repositoryPerson = repositoryPerson.updateWith(person);
    manageUserService.updatePerson(repositoryPerson);

    model.asMap().clear();
    return new ModelAndView("redirect:" + URL_USERS_OVERVIEW, MODEL, model);
}

【问题讨论】:

  • “标准”Spring 方式是最好的方式,能否请您提供您的电子邮件对象详细信息、控制器和 html 页面?您是否仅在执行编辑或创建新用户时遇到此问题?
  • @PatrickLC 抱歉回复晚了,但我家里没有数据。如果我创建一个新用户,我有另一个结构,通过注册有以下结构:Person -> Child -> email and in edit(发生错误的地方):Person -> List -> email
  • 如果您尝试编辑一个只有一个孩子的人并且关联的电子邮件工作正常?
  • 不,然后我得到错误。如果我编辑来自该人的电子邮件,它将适用于该人。我混淆的一点是,人和孩子的电子邮件对象是一样的。
  • 好消息! :) 我认为这是一个完美的解决方案,有人也有类似的复杂对象绑定问题(仅供参考:stackoverflow.com/questions/16895406/…

标签: java spring spring-boot thymeleaf formatter


【解决方案1】:

我已经回答了我自己的问题。

我们应该记住这一点

  • 注册过程正常,绑定正常

只有编辑过程会出现问题,所以我在数据库中有一个有效的电子邮件对象,与一个有效的子对象绑定,这与一个有效的用户对象绑定。

我决定使用以下实现构建一个EmailFormatter

public class EmailFormatter implements Formatter<Email> {

    @Override
    public String print(Email email, Locale locale) {
        return email.getValue();
    }

    @Override
    public Email parse(String mailAddress, Locale locale) throws ParseException {
        return new Email(mailAddress);
    }

}

有了这个,我在我的子实例中获得了一个新的电子邮件对象(没有 id)。现在我在有效的子对象中有一个有效的电子邮件对象。看看post-Method:

@RequestMapping(value = "/{login}/edit", method = RequestMethod.POST)
public ModelAndView updateUser(@PathVariable("login") final String login, @ModelAttribute(MODEL_USER) final Person person, BindingResult bindingResult,  final Model model) {
    Person repositoryPerson = personRepository.findPersonByLogin(login);

    repositoryPerson = repositoryPerson.updateWith(person);
    manageUserService.updatePerson(repositoryPerson);

    model.asMap().clear();
    return new ModelAndView("redirect:" + URL_USERS_OVERVIEW, MODEL, model);
}

此时我从用户那里获得了登录名,我从数据库中获取了“旧”(有效)用户对象。我写了一个“合并”方法,我给“旧”用户对象修改后的用户对象。

在这里我可以获取带有 id 的旧电子邮件对象并像这样设置电子邮件地址:

this.emailChild.setValue(modifiedChild.getemailChild().getValue());

在此之后,我查看了这个解决方案,我注意到这真的很简单。

但是,如果绑定问题是由“复杂器”(超过一个相关领域)对象发生的,那么我认为,您最好选择 PatrickLC 建议的解决方案:

@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
    dataBinder.setDisallowedFields("emailId");
}

【讨论】:

    猜你喜欢
    • 2018-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-01
    • 2011-10-14
    • 2019-06-20
    相关资源
    最近更新 更多