1

I know questions on this matter have been asked already, but none of the answers I found solved my problem.

I have a many-to-many relantionship on my database. I'm using JPA and Hibernate to create and alter my tables. Here are my model classes:

Book.java

@Entity
@Table(name="tb_books")
public class Book implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", updatable = false, nullable = false, insertable = false)
    private Integer id;

    @Column(name = "title", nullable = false, length = 255)
    private String title;

    @Column(name = "author", nullable = false, length = 255)
    private String author;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)
    @JoinTable(name = "book_tag",
               joinColumns = { @JoinColumn(name = "fk_book") },
               inverseJoinColumns = { @JoinColumn(name = "fk_tag") })
    private List<Tag> tags;

    //getters, setters, equals and hash methods...

}

Tag.java

@Entity
@Table(name="tb_tags")
public class Tag implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", updatable = false, nullable = false)
    private Integer id;

    @Column(name = "description", nullable = false, length = 255)
    private String description;

    //getters, setters, equals and hash methods...
}

I'm trying to make insertions and updates on my book_tag table with the data comming from a JSP using Spring MVC. Here is my controller and insertion page:

BookController.java

@Controller
@RequestMapping(value = "/book")
public class BookController {

    @Autowired
    private BookService bookService;

    /*
        Controller method that gets us to the adding book page. 
    */    
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public ModelAndView addBookPage() {

        List<Tag> tags = tagService.getTags(); //all tags from the database

        ModelAndView modelAndView = new ModelAndView("form-book");
        modelAndView.addObject("book", new Book());
        modelAndView.addObject("tags", tags);

        return modelAndView;
    }

    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public ModelAndView addBookProcess(@ModelAttribute Book book) {

        bookService.addBook(book);
        return new ModelAndView("redirect:/book/add");
    }

    //...

}

form-book.jsp

<form:form method="POST" modelAttribute="book" action="${pageContext.request.contextPath}/book/add.html">
<table>
    <tbody>
        <tr>
            <td>Title:</td>
            <td><form:input path="title" autocomplete="off" /></td>
        </tr>
        <tr>
            <td>Author:</td>
            <td><form:input path="author" autocomplete="off" /></td>
        </tr>
        <tr>
            <td colspan="2">

                <form:checkboxes path="tags" 
                items="${tags}" 
                itemLabel="description"
                itemValue="id"/>

            </td>
        </tr>
        <tr>
            <td><input type="submit" value="Add" /></td>
            <td><input type="button" onclick="location.href = '${pageContext.request.contextPath}/index'" value="Cancel"/></td>
        </tr>
    </tbody>
</table>

</form:form>

Everything is working fine, except when I try to update or insert books with one or more checkboxes selected. When I try to do so, I just get a "Bad Request" page, I imagine because of something wrong I'm doing concerning databinding.

Is this the way I'm supposed to handle this situation? Is there a better way? What am I doing wrong?

4
  • I built these files into a Spring Boot Project, and it works for me as printed. Commented Sep 9, 2017 at 2:58
  • Really? It works even after you select one of the checkboxes before sending the form? Commented Sep 9, 2017 at 3:18
  • github.com/alessandroscarlatti/so-9-8-2017-2 Commented Sep 9, 2017 at 3:57
  • But that's with mocking the book service since I didn't set up the db. So as far as I can tell it could be with the validation. see stackoverflow.com/questions/23202788/… Commented Sep 9, 2017 at 4:01

1 Answer 1

1

Apparently, the List comming from the checkbox was actually an array of Strings, which could not be converted to a List of Tags, as defined the model classes.

I solved my problem by creating a custom PropertyEditor, which would allow me to bind the checkboxes with the list of tags.

public class TagPropertyEditor extends PropertyEditorSupport {

    private TagService tagService;

    public TagPropertyEditor(TagService tagService){
        this.tagService = tagService;
    }

    @Override
    public String getAsText() {
        return ((Tag) getValue()).getId().toString();
    }

    @Override
    public void setAsText(String incomingId) throws IllegalArgumentException {
        Tag tag = tagService.getTag(Integer.valueOf(incomingId));
        setValue(tag);
    }
}

And then registering on my BookController:

//...
@InitBinder
public void initBinder(WebDataBinder binder){
    binder.registerCustomEditor(Tag.class, new TagPropertyEditor(tagService));
}

And this is the checboxes tag on my JSP:

 <form:checkboxes path="tags" 
                  items="${tags}"
                  itemLabel="description"
                  itemValue="id"/>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.