JPA OneToMany not deleting child
JPA's behaviour is correct (meaning as per the specification): objects aren't deleted simply because you've removed them from a OneToMany collection. There are vendor-specific extensions that do that but native JPA doesn't cater for it.
In part this is because JPA doesn't actually know if it should delete something removed from the collection. In object modeling terms, this is the difference between composition and "aggregation*.
In composition, the child entity has no existence without the parent. A classic example is between House and Room. Delete the House and the Rooms go too.
Aggregation is a looser kind of association and is typified by Course and Student. Delete the Course and the Student still exists (probably in other Courses).
So you need to either use vendor-specific extensions to force this behaviour (if available) or explicitly delete the child AND remove it from the parent's collection.
I'm aware of:
- Hibernate: cascade delete_orphan. See 10.11. Transitive persistence; and
- EclipseLink: calls this "private ownership". See How to Use the @PrivateOwned Annotation.
JPA + @OneToMany + DELETE: Item is not deleted if I access parent later
I have replicated this code locally and verified my explanation
Summary:
Cascade does not have impact. Even if you remove your cascade operation, save each item separately , then when you come to this method, it will not delete your item.
To have same behaviour regardless of
deal.getItems
initialisation, You will have to delete thedealItem
by removing it fromdeal.getItems
in addition to deleting the dealItem directly.On a bi-directional relationship, you will have to explicitly manage both sides. Exactly the same way, you add the dealItem to deal as well set deal field of dealItem before you save.
Overall Explanation
JPA can have only one representation of a particular item associated with it's session.
It is the foundation for providing Repeatble Read, Dirty Checking etc.
JPA also tracks every object associated with its session and If any of the tracked objects have changes, they will flushed when the transaction committed.
When only
deal
object (with lazydeaItems
collection) and the directly fetcheddealItem
are the only two entities associated with the session, then JPA has one presentation for each in the session, since there is no conflict, when you delete it, it deletes it viadealItemControl.deleteDealItem
the dealItem is deletedHowever, once you call
deal.getItems
, JPA not only manages deal, but also every dealItem associated with thedeal
object. So when when you delete thedealItemControl.deleteDealItem
, JPA has an issue because deal.getItems tells it is not marked for delete. So the delete is not issued.
Reference: JPA QL generated also confirms my explanation
1. With deal.getItems
and Queries Generated
@OneToMany(mappedBy = "deal", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<DealItemEntity> items;
DealEntity dealEntity = dealControl.findDealByDealCode(dealCode);
....
dealItemControl.deleteDealItem(dealItemEntity);
....
dealEntity.getItems()
select deal0_.* from deal deal0_ where deal0_.id=?
select dealitem0_.*
deal1_.*
from
deal_item dealitem0_ inner join deal deal1_ on dealitem0_.deal_id=deal1_.id
where
dealitem0_.id=?
select items0_.* from deal_item items0_ where items0_.deal_id=?
2. Without deal.getItems
and Queries Generated
@OneToMany(mappedBy = "deal", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<DealItemEntity> items;
DealEntity dealEntity = dealControl.findDealByDealCode(dealCode);
....
dealItemControl.deleteDealItem(dealItemEntity);
select deal0_.* from deal deal0_ where deal0_.id=?
select dealitem0_.*
deal1_.*
from
deal_item dealitem0_ inner join deal deal1_ on dealitem0_.deal_id=deal1_.id
where
dealitem0_.id=?
delete from deal_item where id=?
Spring-Data-JPA - How to delete child record in OneToMany relationship
I had to do this to make this work.
@OneToMany(
mappedBy = "person",
fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
orphanRemoval = true
)
private Set<Address> addresses = new HashSet<>();
@OneToMany delete children
First of all, you don't need set cascadeTypes
and @Cascade
. I think it might be a source of your problems.
Did you override
equals
and hashCode
in ContractCompany
? because maybe you are not removing the desired object from List<ContactCompany>
Look at this
@OneToMany(mappedBy = "client", ...)
mappedBy
informs hibernate that client is the owner of this relation, so you need to get client
, get a list of clientContracts
, remove one (make sure it's removed from the list), update client and object is removed.
Example source code below
CompanyContract toBeRemoved = //companyContract which you want to delete
client.getCompanyContracts().remove(toBeRemoved);
toBeRemoved.client = null;
save both objects, toBeRemoved
and client
Hibernate @OneToMany does not delete child on removal from the underlying collection
You have to also set null
on the other side of the relation. Something like this:
Portfolio portfolio = ...;
PortfolioContributors portfolioContributors = ...;
portfolio.getPortfolioContributors().remove(portfolioContributors);
portfolioContributors.setPortfolio(null);
Related Topics
Singleton Design Pattern: Pitfalls
How to Get the Session Object If I Have the Entity-Manager
What Are the Pros and Cons of Performing Calculations in SQL VS. in Your Application
Create a Jtds Connection String
How to Read Request.Getinputstream() Multiple Times
Java, List Only Subdirectories from a Directory, Not Files
How to Resize Jlabel Imageicon
In Java, How to Efficiently and Elegantly Stream a Tree Node's Descendants
How to Keep the Iteration Order of a List When Using Collections.Tomap() on a Stream
Filtering Database Rows with Spring-Data-JPA and Spring-Mvc
How to Get Backspace \B to Work in Eclipse's Console
Using Gson to Parse a JSON Array
Performance of Stringtokenizer Class VS. String.Split Method in Java
How to Pass Parameters to Anonymous Class