title: Spring and/or Hibernate: Saving many-to-many relations from one side after form submission
body: <h2>The context</h2>
  
  <p>I have a simple association between two entities - <code>Category</code> and <code>Email</code> (NtoM). I'm trying to create web interface for browsing and managing them. I have a simple e-mail subscription edit form with list of checkboxes that represents categories, to which given e-mail belongs (I registered property editor for <code>Set&lt;Category&gt;</code> type).</p>
  
  <h2>The problem</h2>
  
  <p>Form displaying works well, including marking currently assigned categories (for existing e-mails). But no changes are saved to EmailsCategories table (NtoM mapping table, the one defined with <code>@JoinTable</code> - neither newly checked categories are added, nor unchecked categories are removed.</p>
  
  <h2>The code</h2>
  
  <p>Email entity:</p>
  
  <pre class="lang-java prettyprint-override"><code>@Entity
  @Table(name = "Emails")
  public class Email
  {
      @Id
      @GeneratedValue(generator = "system-uuid")
      @GenericGenerator(name = "system-uuid", strategy = "uuid2")
      @Column(length = User.UUID_LENGTH)
      protected UUID id;
  
      @NaturalId
      @Column(nullable = false)
      @NotEmpty
      @org.hibernate.validator.constraints.Email
      protected String name;
  
      @Column(nullable = false)
      @Temporal(TemporalType.TIMESTAMP)
      protected Date createdAt;
  
      @Column
      protected String realName;
  
      @Column(nullable = false)
      protected boolean isActive = true;
  
      @ManyToMany(mappedBy = "emails", fetch = FetchType.EAGER)
      protected Set&lt;Category&gt; categories = new HashSet&lt;Category&gt;();
  
      public UUID getId()
      {
          return this.id;
      }
  
      public Email setId(UUID value)
      {
          this.id = value;
  
          return this;
      }
  
      public String getName()
      {
          return this.name;
      }
  
      public Email setName(String value)
      {
          this.name = value;
  
          return this;
      }
  
      public Date getCreatedAt()
      {
          return this.createdAt;
      }
  
      public String getRealName()
      {
          return this.realName;
      }
  
      public Email setRealName(String value)
      {
          this.realName = value;
  
          return this;
      }
  
      public boolean isActive()
      {
          return this.isActive;
      }
  
      public Email setActive(boolean value)
      {
          this.isActive = value;
  
          return this;
      }
  
      public Set&lt;Category&gt; getCategories()
      {
          return this.categories;
      }
  
      public Email setCategories(Set&lt;Category&gt; value)
      {
          this.categories = value;
  
          return this;
      }
  
      @PrePersist
      protected void onCreate()
      {
          this.createdAt = new Date();
      }
  }
  </code></pre>
  
  <p>Category entity:</p>
  
  <pre class="lang-java prettyprint-override"><code>@Entity
  @Table(name = "Categories")
  public class Category
  {
      @Id
      @GeneratedValue(generator = "system-uuid")
      @GenericGenerator(name = "system-uuid", strategy = "uuid2")
      @Column(length = User.UUID_LENGTH)
      protected UUID id;
  
      @NaturalId(mutable = true)
      @Column(nullable = false)
      @NotEmpty
      protected String name;
  
      @ManyToMany
      @JoinTable(
          name = "EmailsCategories",
          joinColumns = {
              @JoinColumn(name = "idCategory", nullable = false, updatable = false)
          },
          inverseJoinColumns = {
              @JoinColumn(name = "idEmail", nullable = false, updatable = false)
          }
      )
      protected Set&lt;Email&gt; emails = new HashSet&lt;Email&gt;();
  
      public UUID getId()
      {
          return this.id;
      }
  
      public Category setId(UUID value)
      {
          this.id = value;
  
          return this;
      }
  
      public String getName()
      {
          return this.name;
      }
  
      public Category setName(String value)
      {
          this.name = value;
  
          return this;
      }
  
      public Set&lt;Email&gt; getEmails()
      {
          return this.emails;
      }
  
      public Category setEmails(Set&lt;Email&gt; value)
      {
          this.emails = value;
  
          return this;
      }
  
      @Override
      public boolean equals(Object object)
      {
          return object != null
              &amp;&amp; object.getClass().equals(this.getClass())
              &amp;&amp; ((Category) object).getId().equals(this.id);
      }
  
      @Override
      public int hashCode()
      {
          return this.id.hashCode();
      }
  }
  </code></pre>
  
  <p>Controller:</p>
  
  <pre class="lang-java prettyprint-override"><code>@Controller
  @RequestMapping("/emails/{categoryId}")
  public class EmailsController
  {
      @Autowired
      protected CategoryService categoryService;
  
      @Autowired
      protected EmailService emailService;
  
      @ModelAttribute
      public Email addEmail(@RequestParam(required = false) UUID id)
      {
          Email email = null;
  
          if (id != null) {
              email = this.emailService.getEmail(id);
          }
          return email == null ? new Email() : email;
      }
  
      @InitBinder
      public void initBinder(WebDataBinder binder)
      {
          binder.registerCustomEditor(Set.class, "categories", new CategoriesSetEditor(this.categoryService));
      }
  
      @RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
      public String editForm(Model model, @PathVariable UUID id)
      {
          model.addAttribute("email", this.emailService.getEmail(id));
  
          model.addAttribute("categories", this.categoryService.getCategoriesList());
  
          return "emails/form";
      }
  
      @RequestMapping(value = "/save", method = RequestMethod.POST)
      public String save(@PathVariable UUID categoryId, @ModelAttribute @Valid Email email, BindingResult result, Model model)
      {
          if (result.hasErrors()) {
              model.addAttribute("categories", this.categoryService.getCategoriesList());
              return "emails/form";
          }
  
          this.emailService.save(email);
  
          return String.format("redirect:/emails/%s/", categoryId.toString());
      }
  }
  </code></pre>
  
  <p>Form view:</p>
  
  <pre class="lang-xml prettyprint-override"><code>&lt;form:form action="${pageContext.request.contextPath}/emails/${category.id}/save" method="post" modelAttribute="email"&gt;
      &lt;form:hidden path="id"/&gt;
      &lt;fieldset&gt;
          &lt;label for="emailName"&gt;&lt;spring:message code="email.form.label.Name" text="E-mail address"/&gt;:&lt;/label&gt;
          &lt;form:input path="name" id="emailName" required="required"/&gt;
          &lt;form:errors path="name" cssClass="error"/&gt;
  
          &lt;label for="emailRealName"&gt;&lt;spring:message code="email.form.label.RealName" text="Recipient display name"/&gt;:&lt;/label&gt;
          &lt;form:input path="realName" id="emailRealName"/&gt;
          &lt;form:errors path="realName" cssClass="error"/&gt;
  
          &lt;label for="emailIsActive"&gt;&lt;spring:message code="email.form.label.IsActive" text="Activation status"/&gt;:&lt;/label&gt;
          &lt;form:checkbox path="active" id="emailIsActive"/&gt;
          &lt;form:errors path="active" cssClass="error"/&gt;
  
          &lt;form:checkboxes path="categories" element="div" items="${categories}" itemValue="id" itemLabel="name"/&gt;
          &lt;form:errors path="categories" cssClass="error"/&gt;
  
          &lt;button type="submit"&gt;&lt;spring:message code="_common.form.Submit" text="Save"/&gt;&lt;/button&gt;
      &lt;/fieldset&gt;
  &lt;/form:form&gt;
  </code></pre>
  
  <h2>Edit - added DAO code</h2>
  
  <p>(<code>emailService.save()</code> is just a proxy call to <code>emailDao.save()</code>)</p>
  
  <pre class="lang-java prettyprint-override"><code>public void save(Email email)
  {
      this.getSession().saveOrUpdate(email);
  }
  </code></pre>
  
  <h2>Edit 2 - little more debug/logs</h2>
  
  <p>A simple test snippet:</p>
  
  <pre class="lang-java prettyprint-override"><code>public void test()
  {
      Category category = new Category();
      category.setName("New category");
      this.categoryDao.save(category);
  
      Email email = new Email();
      email.setName("test@me")
          .setRealName("Test &lt;at&gt; me")
          .getCategories().add(category);
      this.emailDao.save(email);
  </code></pre>
  
  <p>}</p>
  
  <p>And these are logs:</p>
  
  <pre><code>12:05:34.173 [http-bio-8080-exec-23] DEBUG org.hibernate.SQL - insert into Emails (createdAt, isActive, name, realName, id) values (?, ?, ?, ?, ?)
  12:05:34.177 [http-bio-8080-exec-23] DEBUG org.hibernate.persister.collection.AbstractCollectionPersister - Inserting collection: [pl.chilldev.mailer.web.entity.Category.emails#24d190e3-99db-4792-93ea-78c294297d2d]
  12:05:34.177 [http-bio-8080-exec-23] DEBUG org.hibernate.persister.collection.AbstractCollectionPersister - Collection was empty
  </code></pre>
  
  <p>Even with this logs it seems a little strage - it tels that it's inserting collection with one element, but then it tells it was empty...</p>