0

I'm implementing categorisation system where a category will usually have several subcategories, and a subcategory will have at least one parent, but there will certainly be cases when a subcategory will have more than one parent.

That's why I chose ManyToMany approach.

So, the Category:

public class Category implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "cat_id", nullable = false)
    private Integer catId;
    ....    
    @ManyToMany(cascade = CascadeType.ALL)
    @JoinTable(
        name = "cats_subcats",
        joinColumns = @JoinColumn(name = "cat_id"),
        inverseJoinColumns = @JoinColumn(name = "subcat_id")
    )
    private Set<Subcategory> subcats;
    ....

The Subcategory:

public class SubCategory implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "subcat_id", nullable = false)
    private Integer subcatId;
    ....
    @ManyToMany(cascade = CascadeType.ALL, mappedBy = "subcats")
    private Set<Category> cats;
    ....

This setup works, it creates the join table, inserts my two dummy subcats, and also creates the two joining records in the join table.

I then proceeded with testing how it would behave in different scenarios.

First, I wanted to remove one subcategory from an existing category with three subcategories.

My managed bean:

....
@PostConstruct
    public void init() {

    category = new Category();
    category.setName("Programmatically added ctg");
    category.setSlug("programmatically-added-crg");

    Set<Subcategory> subcats = new HashSet<>(2);

    Subcategory subcat = new Subcategory();
    subcat.setName("Subcat one");
    subcats.add(subcat);

    Subcategory subcat2 = new Subcategory();
    subcat2.setName("Subcat to be removed");
    subcats.add(subcat2);

    Subcategory subcat3 = new Subcategory();
    subcat3.setName("The most recent subcat");
    subcats.add(subcat3);

    category.setSubcats(subcats);

    // this initially saves both the cat and the subcats
    ctgService.save(category);
    categories = ctgService.getAll();

    // now I remove one of the three subcats
    category.getSubcats().remove(subcat2);

    // this is a method belonging to my service (EJB)
    ctgService.update(category);

    // upon re-fetching, I can see in my DB that the subcat has not been removed
    categories = ctgService.getAll();
}
....

I got it to work by changing (in Category entity) @ManyToMany(cascade = CascadeType.ALL) to @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}).

Indeed, it removes the subcat as desired but... When I take a look at my categories (there's only one in this scenario) - I can see that it somehow has been re-inserted because it now has the cat_id of 2 instead of 1.

Could anyone shed some light on any/both of the issues I'm experiencing?

developer10
  • 1,450
  • 2
  • 15
  • 31
  • I've had the first one. Seems to be a bug. About the second one, I think you must also do subcat2.getCats().remove(category) ? Since it is bidirectional – Jaqen H'ghar Oct 19 '16 at 18:35

1 Answers1

0

I think you want 'orpahnremoval' but it's not available on @ManyToMany

How do I delete orphan entities using hibernate and JPA on a many-to-many relationship?

Community
  • 1
  • 1
Daniel Scott
  • 7,418
  • 5
  • 39
  • 58
  • I'm already aware of `orphanRemoval = true` (thanks anyway) but as you pointed out, it is unfortunately not available here. However, as I said near the end of the question, if I change `CascadeType.ALL` to some individual cascade types, it works as intended, but for some reason the id of my category is increased by 1 (as if it was first removed, then recreated but without resetting the autoincrement value back to 1. – developer10 Oct 14 '16 at 20:39