Я все еще борется с Spring MVC с тем, что должно быть довольно простой проблемой, но то, что, как представляется, разрешено в документах Spring MVC.Сложная привязка данных объекта Spring MVC
Мой проект использует Spring MVC и Thymeleaf для представлений, но механизм рендеринга представления не имеет отношения к проблеме.
Мое приложение сосредоточено вокруг класса деятельности, который моделирует (внутреннюю или наружную) деятельность, которая организована членом и где могут быть подписаны участники. У Activity есть, помимо прочего, поле «Категория» и поле «Регион», которые представляют собой раскрывающиеся поля, которые моделируются Hibernate как многие-к-одному для таблиц поиска DB, которые содержат поле id и description.
Код класса активности объекта выглядит следующим образом, не входящих в соответствующие поля опущены, чтобы сократить код:
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, который представляет собой класс сущности, содержащий идентификатор и свойство описания.
В представлении, то выберите поле заполняется со списком возможных вариантов (allCategories, который содержит ActivityCategory экземпляров), Thymeleaf заботится о выборе текущего значения путем сопоставления «значение» значение атрибута со списком:
<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} вместо $ {cat }, но затем выбор текущего значения (установка атрибута 'selected = "selected"') больше не работает. Поэтому я реализовал конвертер, который преобразует объект 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 не может быть привязано к объекту Activity, который ожидает значение ActivityCategory вместо целочисленного значения, поэтому генерируется ошибка проверки типаMismatch.
Моего метод обработчик выглядит следующим образом:
@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";
}
}
Я смотрел много постов, но до сих пор не найдено не имеет решения для этого ИМХО довольно тривиального вопроса. Как значение String, содержащее идентификатор, принимается методом обработчика и правильно преобразовывается? Или мы не можем использовать значение id для этой цели? Ищете несколько советов ...
Спасибо за ваш ответ, большинство, если не все примеры на самом деле используют модель сущности объект как объект формы. На самом деле документация Spring MVC советует не использовать отдельные объекты поддержки формы: _ «Таким образом, вам не нужно дублировать свойства ваших бизнес-объектов как простые, нетипизированные строки в объектах формы, просто обрабатывать недопустимые представления или преобразовывать строки Вместо этого часто предпочтительнее напрямую связываться с вашими бизнес-объектами ». _ Но большинство примеров не используют поля поиска из отдельных объектов, поэтому мне остается неясным, как с этим справиться. – klausch