2015-09-01 2 views
1

Я все еще борется с 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 для этой цели? Ищете несколько советов ...

ответ

0

Я думаю, вы не можете использовать свою модель сущности для отправки данных из вашей формы в контроллер MVC. Попробуйте создать отдельный объект формы, соответствующий данным формы, и напишите метод службы, чтобы перевести его на объекты, которые могут сохраняться в базе данных.

+1

Спасибо за ваш ответ, большинство, если не все примеры на самом деле используют модель сущности объект как объект формы. На самом деле документация Spring MVC советует не использовать отдельные объекты поддержки формы: _ «Таким образом, вам не нужно дублировать свойства ваших бизнес-объектов как простые, нетипизированные строки в объектах формы, просто обрабатывать недопустимые представления или преобразовывать строки Вместо этого часто предпочтительнее напрямую связываться с вашими бизнес-объектами ». _ Но большинство примеров не используют поля поиска из отдельных объектов, поэтому мне остается неясным, как с этим справиться. – klausch

0

С помощью другого форума я нашел самое элегантное решение! Вместо конвертера мы используем Formatter, который может конвертировать из specfiec Object type в String и наоборот. Форматировщик зарегистрирован на Spring и автоматически вызывается из Thymeleaf и преобразует поле id в экземпляр ActivityCategory только с установленным значением 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()); 
} 

И теперь он работает, как и ожидалось!

Единственная оставшаяся проблема заключается в том, что у нас есть некоторое дублирование кода, потому что два класса Formatter почти одинаковы, они отличаются только от общего класса, который передается. Я попытался решить это, используя общий интерфейс LookupEntity, который реализуется двумя классами сущностей поиска (ActivityCategory и RegionCategory) и использует этот общий интерфейс для определения форматирования, но, к сожалению, это не сработало ...

Смежные вопросы