[英]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.