33

Brief

I am wondering what I should do as I have read many articles trying to understand this, including many SO questions. Nothing I have read has quite hit the nail on the head with this one.

I want to know what happens when a database is defined with cascade rules as well as the application since this will define whether I should take the following approach or another.

Example tables

create table foo(
  id int unsigned not null auto_increment,
  primary key(id)
);

create table bar(
  id int unsigned not null auto_increment,
  foo_id int unsigned not null,
  primary key(id),
  foreign key(foo_id) references foo(id) on delete cascade on update cascade
)

Example classes

@Entity
@Table(name = "foo")
public class Foo {

  private int id;
  private List<Bar> bars;

  @Id
  @GeneratedValue
  @Column(name = "id")
  public int getId() {
    return id;
  }

  @OneToMany(mappedBy = "foo", cascade = {CascadeType.ALL})
  public List<Bar> getBars() {
    return bars;
  }

  public void setId() {
    this.id = id;
  }

  public void setBars(List<Bar> bars) {
    this.bars = bars;
  }

}

@Entity
@Table(name = "bar")
public class Bar {

  private int id;
  private Foo foo;

  @Id
  @GeneratedValue
  @Column(name = "id")
  public int getId() {
    return id;
  }

  @ManyToOne
  @JoinColumn(name = "foo_id", nullable = false)
  public getFoo() {
    return foo;
  }

  public void setId(int id) {
    this.id = id;
  }

  public void setFoo(Foo foo) {
    this.foo = foo;
  }

}

Questions

If I now call a delete operation (be it through EntityManagerFactory or SessionFactory) on a Foo object, which of the following will occur?

  1. The hibernate operation will delete all records in the bar table whose foreign key is that of Foo's foo_id and then delete the Foo record.

  2. The hibernate operation will delete all corresponding Bar records that have been loaded into session cache (which may or may not be all bar records that exist in the actual database) and then delete the Foo record (the database cascade rule will then delete any remaining bar records).

  3. The hibernate operation will attempt to delete the Foo record first and if database failure then do one of the aforementioned steps.

  4. Something else happens for which I have not considered, if so what?

Considering the following dilemna assumptions, what is the best approach?

Dilemna

If 1 is true then it would suggest:

A) Define cascade rule in database only. Be sure to remove bars from the object in the application so they are not left detached from the database (as the database will delete their records) then make the call to delete foo.

OR

B) Define cascade rule in application only since it will manage the database integrity thoroughly.

NOT

C) Define cascade rules in both, since each achieve the desired result making the other a waste of processing.

If 2 is true then it would suggest:

Define cascade rules in both database and application so that Hibernate can take care of managing its entities and the database can clean up after since the application is not guaranteed to remove all the bar records.

If 3 is true then it would suggest:

Define cascade rules in both database and application since Hibernate appears to support the cascade rule already being defined at the database level.

If 4 is true then it would suggest:

This question is even more important as I have missed something fundamental!

Edit: Add articles I have read...

Related articles

Conflicting views for database, application or both:

SO - should-i-let-jpa-or-the-database-cascade-deletions

Conflicting views for database or application:

SO - cascading-deletes-updates-using-jpa-or-inside-of-database

This article sheds light on what JPA providers actually do (though it should be noted that they use OpenJPA provider for their proof of operations):

jpa-tutorial

It states that:

The cascading of remove and persist operations applies also on those entities that have not been loaded yet. It even passes through them to other entities, potentially traversing through whole object graph.

It goes on to state:

The cascading of refresh, merge and detach passes only through entities that are already loaded.

This would mean that proposed process 2 is not true.

Community
  • 1
  • 1
Psyrus
  • 1,271
  • 12
  • 12
  • I'm gonna go with code only. – djb Mar 09 '14 at 19:33
  • @djb That would be option 1. B) and is probably the safest since the only potential for undesirable effects with that option is if process 2 is true (though I think 2 is the least likely). However, if process 1 is true, then it suggests that database cascade rules should not be present, which if so, then why don't other related SO questions say this? They either allude to cascade rules being present in both or they don't mention this aspect of the cascade implications – Psyrus Mar 09 '14 at 19:50
  • All I know is it has to do it in a way to avoid key constraint violations. So in practice, it will have to make and traverse a tree, deleting parents down to the most leafy children. – djb Mar 10 '14 at 09:12
  • Yes I'm thinking that process 1 is true and the way it does that is as you suggest. However, this surely means that the cascade rules should be defined at one or other but not both. I am starting to lean towards defining at database only, since I want to keep a standard and not mix, but complex relationships / large numbers of children could get expensive in JPA – Psyrus Mar 10 '14 at 21:32
  • There is also a Non-JPA Hibernate annotation @OnDelete which allows you to optimise the deletes: http://eddii.wordpress.com/2006/11/16/hibernate-on-deletecascade-performance/ – Alan Hay Apr 04 '14 at 13:36
  • Might be related: https://stackoverflow.com/questions/19686941/can-i-add-on-delete-cascade-to-tables-managed-by-hibernate/49125725#49125725 – lanoxx Mar 06 '18 at 10:26
  • Thanks @lanoxx for the link - orphanRemoval is actually an additional factor to this question, because this flag should cause the JPA vendor to perform delete operations in cases where CASCADE does not (e.g re-assigning the value of the field, thereby orphaning the original value). I must add, even 4 years on from asking this question, I still cannot be sure of the answer... And still have not taken the time to setup a proper investigation (a developer world seems ever busy...). But if and when I do, I will update the question, if someone has not answered by then. – Psyrus May 25 '18 at 23:10
  • I should also add, that I rarely encounter this conundrum theses days, as I generally follow a practice that does not include creating cascade rules at the database layer, or even in the application, for a variety of reasons that are not discussed here and outside the remit of the question. That said, it does still bother me that I do not know the answer :P I strongly suspect proposal "1" is true. Setting up a test with generated SQL included in the log output would put this one to bed – Psyrus May 25 '18 at 23:27
  • After 6 years from your question still there is no clear explanation for me. Only thoughts and preferences, no proofs in code. Pity. I'll try to investigate that. – jwpol Aug 08 '20 at 10:43

2 Answers2

2

if you declare cascade in the database and hibernate the database will always delete first if it supports it and the hibernate calls will not really delete anything but run anyway. However, since you are using hibernate its main advantage is to allow easy transition to a new database which may not support database side cascade ability. Thus you would want to leave them in there even if your database supports cascade and the hiberate underlining jdbc statements are not currently doing anything (they may do something in the future)

  • This is the key information I am looking for. However, if the database always deletes first, that must mean that hibernate makes a call to delete the parent first as presented in scenario 3 of the question. If that is true, then in the case where the cascades have not been set in the database, then the call would fail since there are children of that parent existing in the database. This would mean Hibernate then has to request the deletion of the children first and then the parent as also indicated in scenario 3. – Psyrus Apr 15 '14 at 10:21
  • It is like a catch 22 so am looking for clarification on this. If it calls delete on the children first, there will be no failures, but also no need for database cascade rules, but if it calls delete on the parent first then there is chance for failure in the cases where there are no database rules. From what you say, you are suggesting that it must call delete on the parent first? – Psyrus Apr 15 '14 at 10:28
  • if you delete using a query and not an attached object then it wont throw any errors and you wont have an issue. Now if the object doesn't exist in the database and is still attached you would have to check the hibernate code or run a test, I am thinking it should not throw an error since hibernate is generating jdbc code and hopefully its not trying to select the object before deletion - if it is I assume this would be a hibernate bug but have not tried it myself. If you stick to not deleting using attached objects you wont have an issue – user24772222222222222222222222 Apr 20 '14 at 02:56
  • Deleting via attached objects is something I would like to do. I have implemented much of my current application in this manner. At present, I have not defined cascade rules within hibernate leaving it solely to the database. Not ideal because objects can become detached without care. But I cannot find an ideal solution without knowing the answer to this question. I guess the proof would be to setup cascade rules in Hibernate, turn on statement logging in database and see what gets executed. Maybe one day when I find time I will do that... – Psyrus Apr 29 '14 at 14:55
  • while yes there is an advantage in Hibernate being more easy to transition to another database there are also drawbacks, one could as easily say: "Since you are using a database with native support for cascading use that, rather than to waste performance with Hibernate and run the risk that someone does changes in the database manually / through any other means than your hibernate supporting application." It always depends on what your general requirements/goals are. – Frank Hopkins Jun 17 '19 at 16:02
1

Why would you even consider it? It is best to stick with hibernate cascade options. On the other side having cascade on both sides would run cascade delete two times. Once from hibernate and once managed by database.

Example 189. from hibernate 5.2 docs. which generates below sql.

@Entity(name = "Person")
public static class Person {
    @ManyToMany(cascade = {CascadeType.DELETE})
    private List<Address> addresses = new ArrayList<>();
    ...
}

Person person1 = entityManager.find( Person.class, personId );
entityManager.remove( person1 );
DELETE FROM Person_Address
WHERE  Person_id = 1

DELETE FROM Person
WHERE  id = 1

Now you see that hibernate deletes child entities before it deletes parent. Database cascade will run on sql person delete but it has nothing to remove now when children were removed before.

Conrad
  • 536
  • 5
  • 13
  • Agree, but when having transitive relations (say more than depth 3) hibernate is soon getting slow and I'm talking about seconds here. – brainfrozen May 07 '20 at 16:53