JPA Onetomany Not Deleting Child

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:

  1. 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.

  2. To have same behaviour regardless of deal.getItems initialisation, You will have to delete the dealItem by removing it from deal.getItems in addition to deleting the dealItem directly.

  3. 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 lazy deaItems collection) and the directly fetched dealItem 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 via dealItemControl.deleteDealItem the dealItem is deleted

  • However, once you call deal.getItems, JPA not only manages deal, but also every dealItem associated with the deal object. So when when you delete the dealItemControl.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



Leave a reply



Submit