繁体   English   中英

Spring MVC复杂对象数据绑定

[英]Spring MVC complex object data binding

我仍然在为Spring MVC奋斗,这应该是一个相当简单的问题,但是在Spring MVC文档中似乎很少记录。

我的项目使用Spring MVC和Thymeleaf作为视图,但是视图渲染引擎与该问题并不真正相关。

我的应用程序以一个Activity类为中心,该Activity类对一个(室内或室外)活动进行建模,该活动由成员组织并且其他成员可以订阅。 一个Activity有一个Category字段和Region字段,它们是下拉字段,由Hibernate建模为包含ID和description字段的DB查找表的多对一实体。

Activity实体类的代码如下,省略了不相关的字段以缩短代码:

package nl.drsklaus.activiteitensite.model;

//imports

@Entity
@Table(name="activity")
public class Activity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) 
private Integer id;

@ManyToOne(cascade=CascadeType.ALL)
@JoinColumn(name="organizer_id")
private Member organizer;

@Size(min=5, max=50)
@Column(name = "title", nullable = false)
private String title;

@Size(min=5, max=500)
@Column(name = "description", nullable = false)
private String description;

@ManyToOne
@JoinColumn(name="category_id")
private ActivityCategory category;

@ManyToOne
@JoinColumn(name="region_id")
private ActivityRegion region;


@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name="member_activity_subscription", 
           joinColumns = {@JoinColumn(name="activity_id")}, 
           inverseJoinColumns={@JoinColumn(name="member_id")})
private List<Member> participants = new ArrayList<Member>();

//getters and setters   

@Override
public int hashCode() {
  ...
}

@Override
public boolean equals(Object obj) {
  ...
}
}

在视图中,用户应该能够从选择框中选择地区和类别。 选项在类级别上使用@ModelAttribute注解方法放入模型中。

问题是框​​绑定到查找属性字段。

例如,类别字段是ActivityCategory类型,它是一个包含id和description属性的实体类。

在视图中,选择框填充有可能的选项列表(all类别包含ActivityCategory实例),Thymeleaf负责通过将“ value”属性值与列表进行匹配来选择当前值:

<label>Categorie</label>
<select th:field="*{category}">
  <option th:each="cat : ${allCategories}"
          th:value="${cat}"
          th:text="${cat.description}">
  </option>
</select>

生成的HTML如下所示:

<select id="category" name="category">
      <option value="nl.drsklaus.activiteitensite.model.lookup.ActivityCategory@20">Actief en sportief</option>
      <option value="nl.drsklaus.activiteitensite.model.lookup.ActivityCategory@21">Uitgaan en nachtleven</option>
      <option value="nl.drsklaus.activiteitensite.model.lookup.ActivityCategory@22" selected="selected">Kunst en cultuur</option>
      <option value="nl.drsklaus.activiteitensite.model.lookup.ActivityCategory@23">Eten en drinken</option>
      <option value="nl.drsklaus.activiteitensite.model.lookup.ActivityCategory@24" selected="selected">Ontspanning en gezelligheid</option>
</select>

如我们所见,value属性包含对象本身的字符串表示形式,这显然是不希望的,为了显示id值,我们可以使用$ {cat.id}代替$ {cat},但是可以选择当前值(设置'selected =“ selected”'属性)不再起作用。 在此之前,我实现了一个Converter,该Converter将ActivityCategory对象转换为int(id值)。 在Thymeleaf中,通过使用双赞{{}}来调用转换器:

th:value="${{cat}}"

转换器已创建并添加到Spring:

public class LookupConverter implements Converter<LookupEntity, String> {
  public String convert(LookupEntity source) {
     return String.valueOf(source.getId());
  }
}

//在MvcConfig类中

@Override
public void addFormatters(FormatterRegistry registry) {
  registry.addConverter(new LookupConverter());
}

现在,HTML显示了选项的id值,这更加合乎逻辑:

<select id="category" name="category">
      <option value="1">Actief en sportief</option>
      <option value="2">Uitgaan en nachtleven</option>
      <option value="3" selected="selected">Kunst en cultuur</option>
      <option value="4">Eten en drinken</option>
      <option value="5">Ontspanning en gezelligheid</option>
</select>

但是在提交后仍然错误,ID值不能绑定到期望使用ActivityCategory的Activity对象(如果是整数值),因此会生成typeMismatch验证错误。

我的处理程序方法如下所示:

@RequestMapping(value = "/{id}/submit", method = RequestMethod.POST)
public String submitForm(@ModelAttribute("activity") Activity activity, BindingResult result, ModelMap model) {

    if (result.hasErrors()) {
        return "activityform";
    } else {

        if (activity.getId() == null) {
            this.service.saveActivity(activity);
        } else {
            this.service.mergeWithExistingAndUpdate(activity);
        }

        return "redirect:/activity/" + activity.getId() + "/detail";
    }
}

我看过很多帖子,但仍然找不到针对恕我直言的琐碎问题的解决方案。 包含id的String值如何被处理程序方法接受并正确转换? 还是我们不能为此目的使用id值? 寻找一些提示...

我认为您不能使用实体模型将数据从表单提交到MVC控制器。 尝试创建一个与表单数据匹配的单独的表单对象,并编写服务方法以将其转换为可以在数据库中保留的实体。

在另一个论坛的帮助下,我找到了最优雅的解决方案! 代替转换器,我们使用格式化程序,该格式化程序可以将特定对象类型转换为字符串,反之亦然。 格式化程序已注册到Spring,并从Thymeleaf自动调用,并将id字段转换为仅设置了id值的ActivityCategory实例。 因此,我们无需从数据库中查找实际实例,因为在这里我们不需要描述,因为Hober吃了这个ID足以创建查询。

我的格式化程序看起来像:

public class ActivityCategoryFormatter implements Formatter<ActivityCategory> {

@Override
public String print(ActivityCategory ac, Locale locale) {
    // TODO Auto-generated method stub
    return Integer.toString(ac.getId());
}

@Override
public ActivityCategory parse(final String text, Locale locale) throws ParseException {
    // TODO Auto-generated method stub
    int id = Integer.parseInt(text);
    ActivityCategory ac = new ActivityCategory(id);

    return ac;
}
} 

并通过以下方式注册到Spring(与ActivityRegionFormatter一起用于其他查找字段):

@Override
public void addFormatters(FormatterRegistry registry) {
//registry.addConverter(new LookupConverter());
registry.addFormatter(new ActivityCategoryFormatter());
registry.addFormatter(new ActivityRegionFormatter());
}

现在,它可以正常工作了!

唯一剩下的问题是我们有一些代码重复,因为两个Formatter类几乎相同,它们只是在传入的通用类中有所不同。我试图通过使用由两个实现的通用接口LookupEntity来解决此问题。查找实体类(ActivityCategory和RegionCategory),并使用此公共接口定义格式化程序,但不幸的是,这没有用...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM