It's always been trouble handling cascades in JPA or Hibernate but i really didn't get it now.
I want to delete a parent row brand and childs that are referencing to it. I used CascadeType.ALL but no luck.
Here are my models (Transact <- TransactProduct -> Product -> Brand -> Customer), I'm showing only relations for clarity:
@Entity
@Table(name = "customer")
public class Customer {
@OneToMany(fetch = FetchType.EAGER, mappedBy = "customer", cascade = CascadeType.ALL)
private Collection<Brand> brands;
}
@Entity
@Table(name = "brand")
public class Brand implements Serializable {
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;
@OneToMany(mappedBy = "brand", cascade = CascadeType.ALL)
private Collection<Product> products;
}
@Entity
@Table(name = "product")
public class Product {
@ManyToOne
@JoinColumn(name = "brand_id")
private Brand brand;
@OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
private Collection<TransactProduct> transactProducts;
}
@Entity
@Table(name = "transact")
public class Transact {
@OneToMany(mappedBy = "transact", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Collection<TransactProduct> transactProducts;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
}
@Entity
@Table(name = "transact_product")
public class TransactProduct {
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;
@ManyToOne
@JoinColumn(name = "transact_id");
private Transact transact;
}
Brand Repository :
@Repository
public interface BrandRepository extends JpaRepository<Brand, Long> {}
In my controller I want to delete a brand like this :
Brand brand = brandRepository.findOne(id);
brandRepository.delete(brand);
After findOne it writes those to console :
select * from brand brand0_ left outer join customer customer1_ on brand0_.customer_id=customer1_.id where brand0_.id=?
select * from brand brands0_ where brands0_.customer_id=?
And after delete it is :
select * from brand brand0_ left outer join product products1_ on brand0_.id=products1_.brand_id where brand0_.id=?
select * from customer customer0_ left outer join brand brands1_ on customer0_.id=brands1_.customer_id where customer0_.id=?
It doesn't even run a delete query. What should I do to delete brands and cascade those delete process to products and transact products that reference to it? I am using CascadeType.All but it doesn't work.
Thanks.
EDIT : I added my customer model to question. And also want to note that when i delete customer entity using :
customerRepository.delete(id);
it does,
delete from brand where id=?
delete from customer where id=?
I added orphanRemoval to Brand entity as Andy Dufresne mentioned but it didn't work. Maybe it's because Brand entity also has a parent entity? Because when I use same method for removing Customers it simply works. And Customer entity doesn't have parent.
As Andy Dufresne stated, to remove the
Brand.products
when you delete aBrand
, you need theCascadeType.REMOVE
and orphanRemoval=true. Also, if you want to delete theBrand.customer
when you remove theBrand
, you need also to Cascade the remove event to theCustomer
Entity. Don't forget that you need a transaction, since you're using Spring, have the@Transactional
annotation at your method or Controller class.Specifying
orphanRemoval=true
in JPA 2.0 (HibernateCascadeType.DELETE_ORPHAN
) tells JPA to remove the child records when the parent is deleted.Can you update your @OneToMany mappings to use this attribute and try? For e.g. for Brand it would look like
After debugging and getting into source code of jpa and hibernate I've figured that out.
I want to delete brand and it childs but it also have a parent(Customer). When you call JPARepository.delete function after some delegations it comes to AbstractEntityManagerImpl class and this code runs :
Here internalGetSession() function actually implemented in EntityManagerImpl class as :
Here we can observe session object. Without removing an brands parent customer, session object contains PersistenceContext as
And with this context it just doesn't remove Brand entity. To be able to remove brand we should set it's parent null first and persist to db, then remove brand. After setting brand's parent(customer) null PersistentContext becomes :
So after then, it deletes brand.
And also orphanRemovel=true is required to be able to delete Brand's child entities as mentioned in the other answer.
So after all what i changed in my code is as below :
I added orphanRemoval = true to my Brand Entity
And I modified my deletion logic as below :
And also, I don't know why in such a case deleting an entity doesn't work. It should be something with how hibernate and jpa works internally.