我仍在努力使用Spring MVC,这应该是一个相当简单的问题,但在Spring MVC文档中似乎有些细节记录。
我的项目使用Spring MVC和Thymeleaf作为视图,但视图渲染引擎与问题并不真正相关。
我的应用程序以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注释方法将选项放入模型中。
问题在于框与查找属性字段的绑定。
例如,Category字段属于ActivityCategory类型,它是一个包含id和description属性的实体类。
在视图中,选择框中填充了可能选项列表(包含ActivityCategory实例的allCategories),Thymeleaf负责通过匹配&#34;值&#34;来选择当前值。属性值与列表:
<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属性包含对象本身的字符串表示,显然不需要,以显示我们可以使用$ {cat.id}代替$ {cat}的id值,但随后选择当前值(设置&#39; selected =&#34;已选择&#34;&#39;属性)不再有效。因此,我实现了一个将ActivityCategory对象转换为int(id值)的Converter。在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值吗? 寻找一些提示......
答案 0 :(得分:0)
我认为您无法使用实体模型将表单中的数据提交给MVC控制器。尝试创建一个与表单数据匹配的单独表单对象,并编写一个服务方法将其转换为可以保留在数据库中的实体。
答案 1 :(得分:0)
在另一个论坛的帮助下,我找到了最优雅的解决方案!我们使用Formatter而不是Converter,它可以从specfiec Object类型转换为String,反之亦然。格式化程序已注册到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类几乎相同,它们只在传入的泛型类中有所不同。 我试图通过使用由两个查找实体类(ActivityCategory和RegionCategory)实现的公共接口LookupEntity来解决这个问题,并使用这个公共接口来定义格式化程序,但遗憾的是这不起作用...