0

I have a Company entity that has a ManyToMany association with an Acknowledgement entity.

@Entity
public class Company {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private int id;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "company_acknowledgement", joinColumns = @JoinColumn(name = "company_id"), inverseJoinColumns = @JoinColumn(name = "acknowledgement_id"))
    @OrderBy("name")
    private Set<Acknowledgement> acknowledgements = new HashSet<>();

    ...
}

@Entity
public class Acknowledgement {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private int id;

    @Column(length = 50, nullable = false)
    private String name;

    ...
}

Within a form, I want to be able to choose a number of acknowledgements that should be bound to the Company entity (with checkboxes). So when I submit the form with POST to my controller action, my Company object contains a number of Acknowledgement objects.

I tried to change the HashSet to TreeSet as discussed in this question, but without luck. The checked Acknowledgement objects will always exist in my database, so basically I only need to populate the id field for each object.

Here is my controller code:

@Controller
public class CompanyController {
    @Autowired
    private CompanyService companyService;

    @RequestMapping("/company/edit/{companyId}")
    public String edit(Model model, @PathVariable int companyId) {
        Company company = this.companyService.get(companyId);
        model.addAttribute("company", company);

        return "company-edit";
    }

    @RequestMapping(value = "/company/edit", method = RequestMethod.POST)
    public String doEdit(Model model, Company company) {
        return ""; // TODO: persist company
    }
}

And my JSP file (view)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<c:url var="actionUrl" value="/company/edit" />

<form:form action="${actionUrl}" commandName="company">
    <form:label path="name">Name:</form:label>
    <form:input path="name" />

    <%-- Other inputs here --%>

    <input type="checkbox" name="acknowledgements[]" value="1" />
    <input type="checkbox" name="acknowledgements[]" value="2" />

    <form:button>Save Changes</form:button>
</form:form>

I tried various things, the above being one of them, but I cannot seem to figure out how to do this. So, how do I map the selected acknowledgements to the the association in my Company entity when submitting the form?

Thank you very much in advance!

Community
  • 1
  • 1
ba0708
  • 10,180
  • 13
  • 67
  • 99

4 Answers4

0

In your jsp, you need to iterate the loop as below.

<c:forEach items="${company.acknowledgements}" var="ack" varStatus="count">
    <input type="checkbox" name="acknowledgements[${count.index}].name" value="${ack.name}" />
 </c:forEach> 

EDIT: Can you try checkboxes

<form:checkboxes path="acknowledgements" items="${company.acknowledgements}" />
javafan
  • 1,525
  • 3
  • 21
  • 40
  • This looks good until I submit the POST request where the following exception occurs when binding the object: `org.springframework.beans.InvalidPropertyException: Invalid property 'acknowledgements[0]' of bean class [com.example.company.entity.Company]: Cannot get element with index 0 from Set of size 0, accessed using property path 'acknowledgements[0]'` – ba0708 Jan 27 '15 at 18:03
  • You will have to change set to list for acknowledgement as Set does not have index. Also it seems that Set is of size 0. Please make sure acknowledgement is loaded from companyService.get(companyId); method as I see it is lazyly loaded in the entity class. – javafan Jan 27 '15 at 18:36
  • Sorry for not getting back to you sooner. I changed the field to be of type `java.util.List` (`org.hibernate.collection.internal.PersistentBag` at runtime), but with the same result. The collection is indeed fetched from the database from calling my service. The error occurs when hitting the `doEdit` controller action, at which point the collection has not been initialized when binding to the object. – ba0708 Jan 29 '15 at 17:51
  • I didnt get you. Do you mean to say it is lazy loading issue? – javafan Jan 29 '15 at 18:10
  • I meant that the problem does not occur when rendering the edit page (the `edit` controller action). On there, the acknowledgements are loaded fine, because they are already loaded by calling the `get` method in my service. The problem occurs when POSTing to the `doEdit` controller action - more specifically when binding the POST data to the `company` parameter. I am not entirely sure, but my guess would be that during the object binding, the indexes that I have chosen with checkboxes are attempted to be looked up on the collection, but at that point the collection is empty. I hope this helps. – ba0708 Jan 29 '15 at 18:17
  • I edited ans with another approach. can you try that – javafan Jan 29 '15 at 18:37
  • Thank you. I actually attempted that before, and I get the following exception when rendering the edit page: `org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type @javax.persistence.ManyToMany @javax.persistence.JoinTable @javax.persistence.OrderBy java.util.List> to type java.lang.String` – ba0708 Jan 29 '15 at 18:43
  • Generally in my every project, I do not use entity classes directly in jsp. I have another layer which will be simple list so that I do not have such issue. If possible for you to change the design, then you can try that. This happens because your list is of type Acknowledgement. – javafan Jan 29 '15 at 18:48
  • Yes, ideally my view layer would not interfere with entities directly, but right now this is how the application is designed. Could I exclude the `acknowledgements` collection from the object binding and manually bind it myself after the other data has been bound by Spring? That would be a workaround, but I could live with it. I am not 100% sure what you mean by what you wrote, so if you could give me a simple example, then I would really appreciate that. – ba0708 Jan 29 '15 at 19:03
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/69843/discussion-between-javafan-and-andy0708). – javafan Jan 29 '15 at 19:04
  • You can create list of String with acknowledgment names and set it in the request. and use – javafan Jan 29 '15 at 19:06
0

Why don't you write your custom request argument resolver ? For example implementing interface HandlerMethodArgumentResolver (more: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/method/support/HandlerMethodArgumentResolver.html).

In 'resolverArgument' method (based on request with all arguments) you could build Company entity with selected acknowledgements.

Markko
  • 26
  • 4
0

First, you need to load the full list of Acknowledgement in your controller and put it in your model (let's say acknowledgementList). Simply loading the Company will only load that campany's acknowledgements, not the complete list.

Then in the jsp :

<form:checkboxes path="acknowledgements" items="${acknowledgementList}" itemValue="id" itemLabel="name"  />
wesker317
  • 2,172
  • 1
  • 16
  • 11
0

It seems you are missing implementation equals and hashCode method on your Company and Acknowledgement entities.

In your jsp you just write code like :

<form:checkboxes path="acknowledgements" items="${acknowledgementList}" itemValue="id" itemLabel="name"/>

and these guys have full answer : https://stackoverflow.com/a/8718318/3846688

Community
  • 1
  • 1
Arip H
  • 67
  • 1
  • 10