Spring MVC множественный выбор для списка в таблице
У меня проблема с сохранением данных из множественного выбора в таблице HTML. Вот подробности:
Используемые технологии:
Spring 4.0.5, Hibernate 4.3.5, MySQL 5.5
У меня есть две таблицы базы данных: "патент" и "ФТС". "fts" - это таблица поиска, которая содержит атрибуты "id" и "val". Соотношение между "патентом" и "fts" является многим-многим и реализуется таблицей "atent_fts", которая содержит внешние ключи "atent_id" и "fts_id". Классы сущностей, сгенерированные Hibernate для "патент" и "fts" выглядит следующим образом:
патент:
@Entity
@Table(name = "patent", catalog = "patscoredb")
public class Patent implements java.io.Serializable {
private Set<Fts> ftses = new HashSet<Fts>(0);
// other attributes...
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(name = "patent_fts", catalog = "patscoredb", joinColumns = {@JoinColumn(name = "patent_id", nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "fts_id", nullable = false, updatable = false) })
public Set<Fts> getFtses() {
return this.ftses;
}
// other methods...
}
FTS:
@Entity
@Table(name = "fts", catalog = "patscoredb")
public class Fts implements java.io.Serializable {
private Integer ftsId;
private String val;
private Set<Patent> patents = new HashSet<Patent>(0);
// other attributes...
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "ftses")
public Set<Patent> getPatents() {
return this.patents;
}
// other methods...
}
Я хочу создать форму отправки, содержащую таблицу HTML, в которой перечислены все патенты. Каждая строка в таблице должна иметь множественный выбор, содержащий все возможные значения fts. Пользователь будет использовать этот выпадающий список, чтобы назначить один (или несколько футов) каждому патенту. Когда пользователь отправляет форму, должны быть соответствующие записи, относящиеся к патентам в ftses, в таблице "многие-ко-многим".
Вот мой взгляд:
<html>
<head>
<link rel="stylesheet" href="../resources/css/bootstrap-multiselect.css" type="text/css" />
</head>
<body>
<form:form method="POST" action="/patscore/maintaindata/editpatentscorrelation" id="submit-form" modelAttribute="patentWrapper">
<div id="page-wrapper">
<div class="row">
<div class="col-lg-12">
<div class="table-responsive">
<table id="patents" class="table table-hover table-striped">
<thead>
<tr>
<th class="header">Internal Number<i class="fa "></i></th>
<th class="header">Correlating Field of Technical Solution <i class="fa "></i></th>
<th class="header">Title <i class="fa "></i></th>
<th class="header">Short Description <i class="fa "></i></th>
<th class="header">Picture <i class="fa "></i></th>
</tr>
</thead>
<tbody>
<c:forEach var="patent" items="${patentWrapper.patentList}" varStatus="i">
<tr>
<td>${patent.caseReference}</td>
<td>
<form:select id="multiselect_id_${i.index}" multiple="true" path="patentList[${i.index}].ftses">
<c:forEach items="${ftsList}" var="fts">
<c:set var="isSelected" value="false" />
<c:forEach items="${patentWrapper.patentList[i.index].ftses}" var="patentFts">
<c:if test="${patentFts.ftsId==fts.ftsId}">
<c:set var="isSelected" value="true" />
</c:if>
</c:forEach>
<c:choose>
<c:when test="${isSelected}">
<option value="${fts.ftsId}" label="${fts.val}" selected="selected"></option>
</c:when>
<c:otherwise>
<option value="${fts.ftsId}" label="${fts.val}"></option>
</c:otherwise>
</c:choose>
</c:forEach>
</form:select>
</td>
<td>${patent.internalTitle}</td>
<td>${patent.abstract_}</td>
<td></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<button type="submit" class="btn btn-default" id="submit">Save Changes</button>
</div>
</div>
</div>
</form:form>
<script src="../resources/js/jquery-1.10.2.js"></script>
<script src="../resources/js/bootstrap.js"></script>
<script type="text/javascript" src="../resources/js/bootstrap-multiselect.js"></script>
<script>
$(document).ready(function() {
$("select[id^='multiselect_id_']").multiselect({
includeSelectAllOption: true
});
});
</script>
</body>
</html>
Вы не можете использовать список в качестве атрибута модели для формы, поэтому я создал класс-оболочку для списка патентов с именем PatentWrapper:
public class PatentWrapper {
private List<Patent> patentList;
public PatentWrapper(){
}
public PatentWrapper(List<Patent> patentList){
this.patentList = patentList;
}
public void addPatent(Patent p){
patentList.add(p);
}
public void removePatent(Patent p){
patentList.add(p);
}
public void removePatent(int index){
patentList.remove(index);
}
public List<Patent> getPatentList() {
return patentList;
}
public void setPatentList(List<Patent> patentList) {
this.patentList = patentList;
}
}
Мой контроллер выглядит следующим образом:
@Controller
@SessionAttributes({ "patentWrapper" })
public class CorrelationController {
private static final Logger logger = LoggerFactory
.getLogger(CorrelationController.class);
@Autowired
private PatentHome patentDao;
@Autowired
private FtsHome ftsDao;
/* Supplying the view with the list of patents and the list of ftses */
@RequestMapping(value = "/maintaindata/correlationpatent", method = RequestMethod.GET)
public String createCorrelationPatent(Locale locale, Model model) {
List<Patent> patList = patentDao.findAllWithFtses();
List<Fts> ftsList = ftsDao.findAll();
PatentWrapper patentWrapper = new PatentWrapper(patList);
model.addAttribute("patentWrapper", patentWrapper);
model.addAttribute("ftsList", ftsList);
return "maintaindata/correlation-patent";
}
@RequestMapping(value = "/maintaindata/editpatentscorrelation", method = RequestMethod.POST)
public String editPatents(
@ModelAttribute("patentWrapper") PatentWrapper patentWrapper,
Locale locale) {
List<Patent> patentList = patentWrapper.getPatentList();
for (Patent patent : patentList) {
patentDao.attachDirty(patent);
}
return "redirect:../";
}
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(List.class, "ftses",
new CustomCollectionEditor(List.class) {
@Override
protected Object convertElement(Object element) {
String ftsId = (String) element;
Fts fts = ftsDao.findById(Integer.parseInt(ftsId));
return fts;
}
});
}
}
Данные правильно загружены в HTML-таблицу, но отправка не работает должным образом.
Первая проблема:
Допустим, у нас есть список из 14 футов в таблице fts, и пользователь назначил патент 1 с fts 5. В результате создается новый fts в таблице fts с идентификатором 15 и значением 5 (значение является правильным идентификатором fts!!). затем создается запись в таблице atent_fts с патент_ид = 1 и fts_id = 15.
Вторая проблема:
если пользователь перезагружает таблицу и назначает другой fts (скажем, fts 6) для того же патента 5, старая запись в таблице atent_fts (atent_id 1 fts_id 15) перезаписывается с помощью fts_id 16! Я уверен, что я делаю это неправильно, но так как я довольно новичок в весне, я действительно не понимаю, что вызывает такое поведение.
Я буду признателен за вашу помощь.