orphanRemoval
has nothing to do with ON DELETE CASCADE
.
orphanRemoval
is an entirely ORM-specific thing. It marks "child" entity to be removed when it's no longer referenced from the "parent" entity, e.g. when you remove the child entity from the corresponding collection of the parent entity.
ON DELETE CASCADE
is a database-specific thing, it deletes the "child" row in the database when the "parent" row is deleted.
An example taken form here:
When an Employee
entity object is removed, the remove operation is cascaded to the referenced Address
entity object. In this regard, orphanRemoval=true
and cascade=CascadeType.REMOVE
are identical, and if orphanRemoval=true
is specified, CascadeType.REMOVE
is redundant.
The difference between the two settings is in the response to disconnecting a relationship. For example, such as when setting the address field to null
or to another Address
object.
If orphanRemoval=true
is specified the disconnected Address
instance
is automatically removed. This is useful for cleaning up dependent
objects (e.g. Address
) that should not exist without a reference from
an owner object (e.g. Employee
).
If only cascade=CascadeType.REMOVE
is specified, no automatic action
is taken since disconnecting a relationship is not a remove
operation.
To avoid dangling references as a result of orphan removal, this feature should only be enabled for fields that hold private non shared dependent objects.
I hope this makes it more clear.
The moment you remove a child entity from the collection you will also be removing that child entity from the DB as well. orphanRemoval also implies that you cannot change parents; if there's a department that has employees, once you remove that employee to put it in another deparment, you will have inadvertantly removed that employee from the DB at flush/commit(whichver comes first). The morale is to set orphanRemoval to true so long as you are certain that children of that parent will not migrate to a different parent throughout their existence. Turning on orphanRemoval also automatically adds REMOVE to cascade list.
JPA translates entity state transitions to SQL statements, like INSERT, UPDATE or DELETE.
When you persist
an entity, you are scheduling the INSERT statement to be executed when the EntityManager
is flushed, either automatically or manually.
when you remove
an entity, you are scheduling the DELETE statement, which will be executed when the Persistence Context is flushed.
For convenience, JPA allows you to propagate entity state transitions from parent entities to child one.
So, if you have a parent Post
entity that has a @OneToMany
association with the PostComment
child entity:
The comments
collection in the Post
entity is mapped as follows:
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Comment> comments = new ArrayList<>();
The cascade
attribute tells the JPA provider to pass the entity state transition from the parent Post
entity to all PostComment
entities contained in the comments
collection.
So, if you remove the Post
entity:
Post post = entityManager.find(Post.class, 1L);
assertEquals(2, post.getComments().size());
entityManager.remove(post);
The JPA provider is going to remove the PostComment
entity first, and when all child entities are deleted, it will delete the Post
entity as well:
DELETE FROM post_comment WHERE id = 1
DELETE FROM post_comment WHERE id = 2
DELETE FROM post WHERE id = 1
When you set the orphanRemoval
attribute to true
, the JPA provider is going to schedule a remove
operation when the child entity is removed from the collection.
So, in our case,
Post post = entityManager.find(Post.class, 1L);
assertEquals(2, post.getComments().size());
PostComment postComment = post.getComments().get(0);
assertEquals(1L, postComment.getId());
post.getComments().remove(postComment);
The JPA provider is going to remove the associated post_comment
record since the PostComment
entity is no longer referenced in the comments
collection:
DELETE FROM post_comment WHERE id = 1
The ON DELETE CASCADE
is defined at the FK level:
ALTER TABLE post_comment
ADD CONSTRAINT fk_post_comment_post_id
FOREIGN KEY (post_id) REFERENCES post
ON DELETE CASCADE;
Once you do that, if you delete a post
row:
DELETE FROM post WHERE id = 1
All the associated post_comment
entities are removed automatically by the database engine. However, this can be a very dangerous operation if you delete a root entity by mistake.
The advantage of the JPA cascade
and orphanRemoval
options is that you can also benefit from optimistic locking to prevent lost updates.
If you use the JPA cascading mechanism, you don't need to use DDL-level ON DELETE CASCADE
, which can be a very dangerous operation if you remove a root entity that has many child entities on multiple levels.
The equivalent JPA mapping for the DDL ON DELETE CASCADE
is cascade=CascadeType.REMOVE
. Orphan removal means that dependent entities are removed when the relationship to their "parent" entity is destroyed. For example if a child is removed from a @OneToMany
relationship without explicitely removing it in the entity manager.
The difference is:
- orphanRemoval = true: "Child" entity is removed when it's no longer referenced (its parent may not be removed).
- CascadeType.REMOVE: "Child" entity is removed only when its "Parent" is removed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With