如何将复选框正确绑定到百里香中的对象列表?


问题内容

我的领域模型由Employee和组成Certificate。一名员工可以引用/拥有许多证书(一对多关系)。证书的完整列表可以从中获取certificateService

为了给员工分配一些特殊证书,我使用th:checkbox了百里香的元素,如下所示:

<form action="#" th:action="@{/employee/add}" th:object="${employee}" method="post">
    <table>
        <tr>
            <td>Name</td>
            <td><input type="text" th:field="*{name}"></td>
        </tr>
        <tr>
            <td>Certificate</td>
            <td>
                <th:block th:each="certificate , stat : ${certificates}">
                    <input type="checkbox" th:field="*{certificates}" name="certificates" th:value="${certificate.id]}"/>
                    <label th:text="${certificate.name}" ></label>
                </th:block>
            </td>
        </tr>
        <tr>
            <td colspan="2"><input type="submit" value="Add"/></td>
        </tr>
    </table>
</form>

现在,当我尝试提交HTML表单时,总是出现以下错误:

400-客户端发送的请求在语法上不正确。

我的问题是:如何用百里香将复选框元素正确绑定到对象列表?

控制者

@RequestMapping(value = "/add" , method = RequestMethod.GET)
public String add(Model model) {
    model.addAttribute("employee",new Employee());
    model.addAttribute("certificates",certificateService.getList());
    return "add";
}

@RequestMapping(value = "/add" , method = RequestMethod.POST)
public String addSave(@ModelAttribute("employee")Employee employee) {
    System.out.println(employee);
    return "list";
}

员工实体

@Entity
@Table(name = "employee")
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID")
    private int id;

    @Column(name = "Name")
    private String name;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "emp_cert",
            joinColumns = {@JoinColumn(name = "employee_id")},
            inverseJoinColumns = {@JoinColumn(name = "certificate_id")})
    private List<Certificate> certificates;

    public Employee() {
        if (certificates == null)
            certificates = new ArrayList<>();
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Certificate> getCertificates() {
        return certificates;
    }

    public void setCertificates(List<Certificate> certificates) {
        this.certificates = certificates;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + "certificates size = " + certificates.size() + " ]";
    }
}

证书实体

@Entity
@Table(name = "certificate")
public class Certificate {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "Id")
    private int id;

    @Column(name = "name")
    private String name;

    @ManyToMany(mappedBy = "certificates")
    private List<Employee> employees;

    public Certificate() {
        if (employees == null)
            employees = new ArrayList<>();
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Employee> getEmployees() {
        return employees;
    }

    public void setEmployees(List<Employee> employees) {
        this.employees = employees;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Certificate other = (Certificate) obj;
        if (id != other.id)
            return false;
        return true;
    }
}

问题答案:

我用自定义解决方案来解决这个问题,我实现它通过发送一个 阵列证书 ID到控制器和接收它作为 requestParam
。需求更改定义如下。

视图

         <tr>
             <td>Certificate</td>
             <td>
               <th:block th:each="certificate : ${certificates}">
                <input type="checkbox" name="cers" th:value="${certificate.id}"/>
                 <label th:text="${certificate.name}"></label>
               </th:block>
             </td>
           </tr>

控制者

@RequestMapping(value = "/add" , method = RequestMethod.POST)
public String addSave(
         @ModelAttribute("employee")Employee employee , 
         @RequestParam(value = "cers" , required = false) int[] cers ,
         BindingResult bindingResult , Model model) {

if(cers != null) {
    Certificate certificate = null ;
    for (int i = 0; i < cers.length; i++) {

        if(certificateService.isFound(cers[i])) {
            certificate = new  Certificate();
            certificate.setId(cers[i]);
          employee.getCertificates().add(certificate);  
        }
    }
        for (int i = 0; i < employee.getCertificates().size(); i++) {
            System.out.println(employee.getCertificates().get(i));
        }
}