2015-09-01 114 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)實現,並使用這個通用接口來定義格式化但不幸的是,沒有工作......