sirve requestmapping que para mvc modelandview formulario ejemplo arquitectura java hibernate spring-mvc one-to-many dynamicform

java - requestmapping - spring mvc



Spring 3 MVC: uno-a-muchos dentro de una forma dinĂ¡mica(agregar/eliminar en crear/actualizar) (3)

Estoy buscando una solución para administrar una relación de uno a muchos dentro de un formulario HTML usando jQuery . Estoy desarrollando Spring , Spring MVC e Hibernate . Encontré muchas pistas en la web, pero no hay ningún ejemplo completo que funcione.

El fondo

Tengo tres entidades JPA:

Consult.java (1)

@Entity @Table(name = "consult") public class Consult private Integer id; private String label; private Set<ConsultTechno> 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<ConsultTechno> consultTechnos; /* getters & setters */ }

Como se muestra, un Consult (1) contiene n ConsultTechnos (2), que se caracterizan por un nivel y un Techno (3).

Las necesidades

Usando un formulario HTML, me gustaría tener un botón Add a techno que agrega dinámicamente dos campos en el DOM:

<input type="text" name="consult.consultTechnos[].techno.id" /> <input type="text" name="consult.consultTechnos[].level" />

Por supuesto, cada vez que el usuario haga clic en el botón, esos dos campos se deben volver a agregar, etc. Elegí input type="text" para el ejemplo, pero al final, los campos serán dos select .

Se deben cubrir cuatro tipos de operaciones:

  1. Agregue una entidad secundaria al crear una nueva entidad maestra
  2. Eliminar una entidad secundaria al crear una nueva entidad maestra
  3. Agregue una entidad secundaria al actualizar una nueva entidad maestra
  4. Eliminar una entidad hijo al actualizar una nueva entidad maestra

El problema

Esa parte de diseño ya funciona, pero al publicar el formulario, no puedo vincular los campos agregados dinámicamente a mi @ModelAttribute consult .

¿Tienes alguna idea de cómo hacer ese tipo de trabajos? Espero haber sido lo suficientemente claro ...

Gracias de antemano :)


¿Por qué está usando la etiqueta de entrada HTML en lugar de las formas de taglib de primavera?

<form:form action="yourURL.htm" command="employeeDto"> <form:input type="text" name="consult.consultTechnos[].techno.id" /> <form:input type="text" name="consult.consultTechnos[].level" /> <form:form>

y crear una clase EmployeeDto como, y agregar el modelMap.addAttribute("employeeDto", new Employee); en tu controlador


Bueno, acabo de llegar al problema, la fuente de vista html no mostrará el html dinámico agregado. Si va a inspeccionar los elementos html, el árbol DOM le mostrará todos los elementos dinámicos agregados, pero en el formulario Enviar, si ve todos los elementos se enviarán al servidor, incluidos los elementos dinámicos creados también.

Una forma de reproducir es ob enviar llamar al método javascript y poner un punto de depuración en el método javascript y verificar los valores de envío de formulario en el documento> elementos de formulario inspeccionando el árbol DOM


Este punto todavía es bastante confuso y poco claro en la web, así que aquí está la manera en que resolví mi problema. Esta solución probablemente no sea la más optimizada, pero funciona al crear y actualizar una entidad maestra.

Teoría

  1. Utilice una List lugar de un Set para sus relaciones de uno a muchos, que deben administrarse dinámicamente.

  2. Inicialice su List como AutoPopulatingList . Es una lista perezosa que permite agregar elementos dinámicamente.

  3. Agregue un atributo remove of int a su entidad hijo. Esto jugará la parte de una bandera booleana y será útil al eliminar dinámicamente un elemento.

  4. Al publicar el formulario, persista solo los elementos que tienen el indicador remove en 0 (es decir, false ).

Práctica

Un ejemplo completo de trabajo: un empleador tiene muchos empleados, un empleado tiene un solo empleador.

Entidades:

Employer.java

@Entity @Table(name = "employer") public class Employer private Integer id; private String firstname; private String lastname; private String company; private List<Employee> 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 */ }

Controlador:

EmployerController.java

@Controller @RequestMapping("employer") public class EmployerController { // Manage dynamically added or removed employees private List<Employee> manageEmployees(Employer employer) { // Store the employees which shouldn''t be persisted List<Employee> employees2remove = new ArrayList<Employee>(); if (employer.getEmployees() != null) { for (Iterator<Employee> 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>(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<Employee> 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"; } }

Ver:

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> <html> <head> <title>Edit</title> <style type="text/css">.hidden {display: none;}</style> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script> <script type="text/javascript"> $(function() { // Start indexing at the size of the current list var index = ${fn:length(employer.employees)}; // Add a new Employee $("#add").off("click").on("click", function() { $(this).before(function() { var html = ''<div id="employees'' + index + ''.wrapper" class="hidden">''; html += ''<input type="text" id="employees'' + index + ''.firstname" name="employees['' + index + ''].firstname" />''; html += ''<input type="text" id="employees'' + index + ''.lastname" name="employees['' + index + ''].lastname" />''; html += ''<input type="hidden" id="employees'' + index + ''.remove" name="employees['' + index + ''].remove" value="0" />''; html += ''<a href="#" class="employees.remove" data-index="'' + index + ''">remove</a>''; html += "</div>"; return html; }); $("#employees" + index + "//.wrapper").show(); index++; return false; }); // Remove an Employee $("a.employees//.remove").off("click").on("click", function() { var index2remove = $(this).data("index"); $("#employees" + index2remove + "//.wrapper").hide(); $("#employees" + index2remove + "//.remove").val("1"); return false; }); }); </script> </head> <body> <c:choose> <c:when test="${type eq ''create''}"><c:set var="actionUrl" value="employer/create" /></c:when> <c:otherwise><c:set var="actionUrl" value="employer/update/${employer.id}" /></c:otherwise> </c:choose> <form:form action="${actionUrl}" modelAttribute="employer" method="POST" name="employer"> <form:hidden path="id" /> <table> <tr> <td><form:label path="firstname">Firstname</form:label></td> <td><form:input path="firstname" /><form:errors path="firstname" /></td> </tr> <tr> <td><form:label path="lastname">Lastname</form:label></td> <td><form:input path="lastname" /><form:errors path="lastname" /></td> </tr> <tr> <td><form:label path="company">company</form:label></td> <td><form:input path="company" /><form:errors path="company" /></td> </tr> <tr> <td>Employees</td> <td> <c:forEach items="${employer.employees}" varStatus="loop"> <!-- Add a wrapping div --> <c:choose> <c:when test="${employer.employees[loop.index].remove eq 1}"> <div id="employees${loop.index}.wrapper" class="hidden"> </c:when> <c:otherwise> <div id="employees${loop.index}.wrapper"> </c:otherwise> </c:choose> <!-- Generate the fields --> <form:input path="employees[${loop.index}].firstname" /> <form:input path="employees[${loop.index}].lastname" /> <!-- Add the remove flag --> <c:choose> <c:when test="${employees[loop.index].remove eq 1}"><c:set var="hiddenValue" value="1" /></c:when> <c:otherwise><c:set var="hiddenValue" value="0" /></c:otherwise> </c:choose> <form:hidden path="employees[${loop.index}].remove" value="${hiddenValue}" /> <!-- Add a link to remove the Employee --> <a href="#" class="employees.remove" data-index="${loop.index}">remove</a> </div> </c:forEach> <button id="add" type="button">add</button> </td> </tr> </table> <button type="submit">OK</button> </form:form> </body> </html>

Espero que pueda ayudar :)