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?