Spring 3 MVC: one-to-many in una forma dynamic (aggiungi / rimuovi su crea / aggiorna)

Sto cercando una soluzione per gestire una relazione uno-a-molti all’interno di un modulo HTML usando jQuery . Sto sviluppando con Spring , Spring MVC e Hibernate . Ho trovato molte tracce sul web, ma nessun esempio completo funzionante.

Lo sfondo

Ho tre entity framework JPA:

Consult.java (1)

 @Entity @Table(name = "consult") public class Consult private Integer id; private String label; private Set consultTechnos; /* getters & setters */ } 

ConsultTechno.java (2)

 @Entity @Table(name = "consult_techno") public class ConsultTechno { private Integer id; private Techno techno; private Consult consult; private String level; /* getters & setters */ } 

Techno.java (3)

 @Entity @Table(name="techno") public class Techno { private Integer id; private String label; private Set consultTechnos; /* getters & setters */ } 

Come mostrato, un Consult (1) contiene n ConsultTechnos (2), che sono caratterizzati da un livello e un Techno (3).

I bisogni

Utilizzando un modulo HTML, mi piacerebbe avere un pulsante Add a techno che aggiunge dynamicmente due campi nel DOM:

   

Ovviamente, ogni volta che l’utente fa clic sul pulsante, questi due campi devono essere aggiunti nuovamente, ecc. Ho scelto input type="text" per l’esempio, ma alla fine i campi saranno due select .

Dovrebbero essere coperti quattro tipi di operazioni:

  1. Aggiungi un’entity framework figlio durante la creazione di una nuova entity framework principale
  2. Rimuovere un’entity framework figlio durante la creazione di una nuova quadro principale
  3. Aggiungi un’entity framework figlio durante l’ aggiornamento di una nuova quadro principale
  4. Rimuovere un’ quadro figlio durante l’ aggiornamento di una nuova quadro principale

Il problema

Quella parte di layout funziona già, ma quando si invia il modulo, non riesco a bind i campi aggiunti dynamicmente alla mia @ModelAttribute consult .

Hai qualche idea di come fare quel tipo di lavoro? Spero di essere stato abbastanza chiaro …

Grazie in anticipo :)

Questo punto è ancora abbastanza confuso e poco chiaro sul web, quindi ecco come ho risolto il mio problema. Questa soluzione probabilmente non è la più ottimizzata, ma funziona quando si crea e si aggiorna un’ quadro master.

Teoria

  1. Usa una List invece di un Set per le tue relazioni uno-a-molti che dovrebbero essere gestite in modo dinamico.

  2. Inizializza la tua List come una List di AutoPopulatingList . È una lista pigra che consente di aggiungere elementi dinamici.

  3. Aggiungi un attributo remove of int all’ quadro figlio. Questo giocherà la parte di un flag booleano e sarà utile quando si rimuove dynamicmente un elemento.

  4. Quando si pubblica il modulo, persistono solo gli elementi che hanno il flag remove su 0 (es. false ).

Pratica

Un esempio pieno di lavoro: un datore di lavoro ha molti dipendenti, un dipendente ha un datore di lavoro.

Entità:

Employer.java

 @Entity @Table(name = "employer") public class Employer private Integer id; private String firstname; private String lastname; private String company; private List employees; // one-to-many /* getters & setters */ } 

Employee.java

 @Entity @Table(name = "employee") public class Employee { private Integer id; @Transient // means "not a DB field" private Integer remove; // boolean flag private String firstname; private String lastname; private Employer employer; // many-to-one /* getters & setters */ } 

controller:

EmployerController.java

 @Controller @RequestMapping("employer") public class EmployerController { // Manage dynamically added or removed employees private List manageEmployees(Employer employer) { // Store the employees which shouldn't be persisted List employees2remove = new ArrayList(); if (employer.getEmployees() != null) { for (Iterator i = employer.getEmployees().iterator(); i.hasNext();) { Employee employee = i.next(); // If the remove flag is true, remove the employee from the list if (employee.getRemove() == 1) { employees2remove.add(employee); i.remove(); // Otherwise, perform the links } else { employee.setEmployer(employer); } } } return employees2remove; } // -- Creating a new employer ---------- @RequestMapping(value = "create", method = RequestMethod.GET) public String create(@ModelAttribute Employer employer, Model model) { // Should init the AutoPopulatingList return create(employer, model, true); } private String create(Employer employer, Model model, boolean init) { if (init) { // Init the AutoPopulatingList employer.setEmployees(new AutoPopulatingList(Employee.class)); } model.addAttribute("type", "create"); return "employer/edit"; } @RequestMapping(value = "create", method = RequestMethod.POST) public String create(@Valid @ModelAttribute Employer employer, BindingResult bindingResult, Model model) { if (bindingResult.hasErrors()) { // Should not re-init the AutoPopulatingList return create(employer, model, false); } // Call the private method manageEmployees(employer); // Persist the employer employerService.save(employer); return "redirect:employer/show/" + employer.getId(); } // -- Updating an existing employer ---------- @RequestMapping(value = "update/{pk}", method = RequestMethod.GET) public String update(@PathVariable Integer pk, @ModelAttribute Employer employer, Model model) { // Add your own getEmployerById(pk) model.addAttribute("type", "update"); return "employer/edit"; } @RequestMapping(value = "update/{pk}", method = RequestMethod.POST) public String update(@PathVariable Integer pk, @Valid @ModelAttribute Employer employer, BindingResult bindingResult, Model model) { // Add your own getEmployerById(pk) if (bindingResult.hasErrors()) { return update(pk, employer, model); } List employees2remove = manageEmployees(employer); // First, save the employer employerService.update(employer); // Then, delete the previously linked employees which should be now removed for (Employee employee : employees2remove) { if (employee.getId() != null) { employeeService.delete(employee); } } return "redirect:employer/show/" + employer.getId(); } // -- Show an existing employer ---------- @RequestMapping(value = "show/{pk}", method = RequestMethod.GET) public String show(@PathVariable Integer pk, @ModelAttribute Employer employer) { // Add your own getEmployerById(pk) return "employer/show"; } } 

Vista:

employer/edit.jsp

 < %@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>< %@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>< %@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>< %@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>< !DOCTYPE HTML>   Edit            
Firstname
Lastname
company
Employees
remove

Spero che potrebbe aiutare :)

perché stai usando un tag di input HTML invece di forms taglib di spring

      

e creare una class EmployeeDto come, e aggiungere il modelMap.addAttribute("employeeDto", new Employee); nel tuo controller

Bene, ho appena avuto il problema, il sorgente della vista HTML non mostrerà l’html dinamico aggiunto. Se ispezionerai gli elementi html, l’albero DOM mostrerà tutti gli elementi dinamici aggiunti ma sul modulo Invia se vedi che tutti gli elementi saranno inviati al server compresi gli elementi dinamici creati.

Un modo per riprodurlo è il modulo ob submit chiama il metodo javascript e inserisci un punto di debug nel metodo javascript e controlla che il modulo invii i valori nel documento> elementi del modulo ispezionando l’albero DOM