2015-09-01 95 views
1

我仍然在与Spring MVC苦苦挣扎,这应该是一个相当直接的问题,但Spring MVC文档中似乎更少。Spring MVC复杂对象数据绑定

我的项目使用Spring MVC和Thymeleaf的意见,但视图渲染引擎是不是真正的问题有关。

我的申请是围绕活动类模型是由一个部件,并且其中其他成员可以订阅组织的(室内或室外)活性居中。一个Activity包含一个Category字段和一个Region字段,它们是由Hibernate建模为多对一实体的下拉字段,包含一个id和description字段的DB查找表。

为活动实体类的代码如下,非相关字段被省略,以缩短的代码:

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和一个描述性的实体类的。

在视图中,选择框填充有可能的选项(allCategories含有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="[email protected]">Actief en sportief</option> 
     <option value="[email protected]">Uitgaan en nachtleven</option> 
     <option value="[email protected]" selected="selected">Kunst en cultuur</option> 
     <option value="[email protected]">Eten en drinken</option> 
     <option value="[email protected]" selected="selected">Ontspanning en gezelligheid</option> 
</select> 

正如我们看到的,值属性包含这显然是不希望对象本身的字符串表示,以显示我们可以用$ {猫的ID值。 id}而不是$ {cat }但是然后选择当前值(设置'selected =“selected''属性)不再有效。因此,我实现了一个Converter,它将ActivityCategory对象转换为int(id值)。在Thymeleaf,转换器通过使用双赞誉称为{{}}:

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

转换器创建并添加到春:

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的字符串值如何被处理程序方法接受并正确转换?或者我们不能使用id值来达到这个目的吗? 寻找一些提示...

回答

0

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

+1

谢谢您的回答,如果不是所有的例子实际上确实使用了实体模型对象作为表单对象。事实上反对使用单独的表单支持对象的Spring MVC的文件建议: _“因此,你不必复制你的业务对象的属性,如您的表单对象简单的无类型字符串简单地处理无效的意见,或者对字符串转换正确的。相反,它往往是直接绑定到你的业务对象。“_ 但事实如此仍不明朗我如何处理这个最例子并不从单独的实体使用查找字段。 – klausch

0

从另一个论坛的帮助,我已经找到了最完美的解决方案!我们使用格式器来代替转换器,该格式器可以从specfiec对象类型转换为字符串,反之亦然。格式化程序向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; 
} 
} 

,并注册到春季(与ActivityRegionFormatter为其他查找字段一起)由:

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

而现在它按预期工作!

唯一剩下的问题是,我们有一些重复代码,因为这两个格式化类几乎是相同的,他们只在传入的泛型类不同。 我尝试用一​​个通用的接口LookupEntity解决这一哪些由两个查找实体类(ActivityCategory和RegionCategory)实现,并使用这个通用接口来定义格式化但不幸的是,没有工作......