Mastering Java EE Development with WildFly
上QQ阅读APP看书,第一时间看更新

Tricks for many to many CascadeType.REMOVE

The CascadeType.ALL in the many to many is really dangerous in the code. CascadeType.REMOVE is automatically inherited when using CascadeType.ALL, but the deleting of the entity doesn't work only on the annotated table; it will work on the other side of the association too.

Now put CascadeType.ALL in the Author entity books with the many to many association instead:

@ManyToMany(mappedBy = "authors",
cascade = CascadeType.ALL)
private List<Book> books = new ArrayList<>();

Delete the author:

Author davide_scala = getByName(entityManager, "Davide Scala");
entityManager.delete(davide_scala);
Author luca_stancapiano = getByName(entityManager, "Luca Stancapiano");
assertEquals(1, luca_stancapiano.books.size());

All books together with the deleted Author will be deleted, even if other Authors were still associated with the deleted Books:

delete from Book_Author where book_id=2
delete from Book where id=2
delete from Author where id=3

Usually, this behavior is not what the developer expects, so at the first deletion they will be surprised.

The same problem we can get when setting the CascadeType.ALL to the Book entity side. Here is a sample:

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "Book_Author",
joinColumns = {
@JoinColumn(
name = "book_id",
referencedColumnName = "id"
)
},
inverseJoinColumns = {
@JoinColumn(
name = "author_id",
referencedColumnName = "id"
)
}
)

This time, not only Books but also Authors are deleted:

Author davide_scala = getByName(entityManager, "Davide Scala");
entityManager.delete(davide_scala);
Author luca_stancapiano = getByName(entityManager, "Luca Stancapiano");
assertNull(luca_stancapiano);

The deleting of the Author causes the deletion of all associated Books, which in the same time removes all associated Authors. This is a dangerous operation because it results in a massive deletion of all entities that’s rarely is the behavior that the users expect:

delete from Book_Author where book_id=2
delete from Book_Author where book_id=1
delete from Author where id=2
delete from Book where id=1
delete from Author where id=1
delete from Book where id=2
delete from Author where id=3

This use case is wrong in so many cases. There are a series of unnecessary SELECT statements and a dangerous complete deletion. Take care when you use CascadeType.ALL in a many-to-many association.

Practical test cases for real many-to-many associations are rare. Often you need additional information stored in a secondary link table. In this case, it is much better to use two one-to-many associations instead of an intermediate link class. In fact, most associations are one-to-many and many-to-one. For this reason, when you use other association styles, that should work.