22

I have some tables managed by Hibernate with various foreign key constraints. Cascade on delete is currently managed by Hibernate alone. For playing around with test data I often create and remove some rows by hand. It would help me a lot if I could add ON DELETE CASCADE to the foreign key constraints but I don't know if Hibernate trips over this because the database removes stuff before Hibernate does.


A lot of people seem to concentrate on DDL. My intention is not to instruct Hibernate to create DDL with SQL DELETE CASCADES. I just want to know if it does any harm if I specify an ON DELETE CASCADE in the database in addition to having JPA's cascade = CascadeType.REMOVE on the reference annotation, e.g., @ManyToOne.

musiKk
  • 14,751
  • 4
  • 55
  • 82
  • I think the answer above only covers Hibernate managed Cascade however I think this is what you are looking for: http://stackoverflow.com/questions/16502481/on-cascade-delete-on-jpa2-many-to-many-relationship – Alan Hay Oct 30 '13 at 15:58
  • The answer is about DDL creation which is not important to me. I want to know whether CASCADE at the DB level is OK for Hibernate. The fact that there is an `@OnDelete` annotation _may_ be proof that it is. – musiKk Oct 30 '13 at 16:09
  • Well that was just a starting point. I'm not sure agree it's just about DDL. Some further research may have led you to some comments here by Gavin King hiself: http://www.mail-archive.com/hibernate-devel@lists.sourceforge.net/msg03803.html – Alan Hay Oct 30 '13 at 22:58
  • 1
    Might be related: https://stackoverflow.com/questions/22286649/jpa-hibernate-cascade-delete-in-both-database-and-annotation – lanoxx Mar 06 '18 at 10:26

8 Answers8

6

You can use CascadeType.DELETE, however this annotation only applies to the objects in the EntityManager, not the database. You want to be sure that ON DELETE CASCADE is added to the database constraint. To verify, you can configure JPA to generate a ddl file. Take a look at the ddl file, you'll notice that ON DELETE CASCADE is not part of the constraint. Add ON DELETE CASCADE to actual SQL in the ddl file, then update your database schema from the ddl. This will fix your problem .

This link shows how to use ON DELETE CASCADE on for CONSTRAINT in MySQL. You do this on the constraint. You can also do it in a CREATE TABLE or ALTER TABLE statement. It's likely that JPA creates the constraint in an ALTER TABLE statement. Simply add ON DELETE CASCADE to that statement.

Note that some JPA implementors do provide a means for this functionality.

Hibernate does supply this functionality using the @OnDelete annotation, thus it is preferred to use this or simply update the ddl file if you would like to stick with standard JPA functionality.

cmd
  • 11,622
  • 7
  • 51
  • 61
5

You can safely use ON DELETE CASCADE as long as you do not have CascadeType.REMOVE or orphanRemoval enabled to prevent propagation of the entity removal event.

Having @OnDelete(action = OnDeleteAction.CASCADE) on your relation will force Hibernate to generate ON DELETE CASCADE for related foreign keys. However your Dialect has to indicate that such relation are supported by returning true in supportsCascadeDelete method.

Dialect.supportsCascadeDelete()

OnDelete

Bohdan Levchenko
  • 3,411
  • 2
  • 24
  • 28
  • 1
    Thanks for the answer. I would have expected Hibernate to take care of the dialect's support and do the right thing either way. Apparently one needs to be aware of that. One more reason I'm glad to have left ORMs behind me... I hope I never have to deal with them again. – musiKk May 19 '20 at 08:31
2

I see two potential issues:

  1. If an entity that represents the table to which you cascade operations directly in the database is versioned, then it would not work because when Hibernate tries to delete records on its own, the version check would fail (Hibernate would assume concurrent thread already updated or deleted the corresponding records).
  2. If there are use cases in which your business logic re-persists such entity instances after removal has been cascaded to them from the parent (for example, you are deleting old parent and migrating associated children to a new one, although for better clarity I would not cascade removal at all if such a use case exists for an association, but it's up to you as it is allowed by the JPA spec), then Hibernate would just un-schedule the deletion of children and delete only the parent, so you would still end up with deleted children if you cascade deletion in the database.

Probably there are some other situations that could be problematic in some scenarios, so I would recommend not to do it.

Dragan Bozanovic
  • 23,102
  • 5
  • 43
  • 110
0

You can use the native database functionality to delete the child records upon deletion of parent record.

Be aware of bi-directional relationships and to be sure, ensure you just specify insert and update in cascade (to be on safer side).

Abhijith Nagarajan
  • 3,865
  • 18
  • 23
0

You mention for testing purposes. I'm guessing, execute some test, delete data, replay test...

When using second-level caching or query cache, the cache will and up being stale if you directly remove the data from the database. This might result in unexpected test results.

So yes, this will conflict with Hibernate if you use second-level / query caching as the entity's will not get evicted from cache. Make sure all caches get cleared after you directly deleted any data. See this question on how to clear cache.

The official Hibernate docs also mention this:

Be aware that caches are not aware of changes made to the persistent store by other applications. They can, however, be configured to regularly expire cached data.

Chris
  • 958
  • 8
  • 19
0

From my experiments, since I have struggled with this for a couple hours, using updatable = false in the @JoinColumn will prevent Hibernate from doing

update theTable set theReference = null where theIdentifier = ?

For example (in Kotlin):

@Entity
@Table(name = "course")
class Course(
    @Column(name = "title")
    var title: String? = null,
    @OneToMany(cascade = [CascadeType.PERSIST])
    @JoinColumn(name = "course_id", updatable = false)
    val reviews: MutableList<Review> = mutableListOf()
) {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    var id: Int? = null

}
Fabian Pijcke
  • 2,920
  • 25
  • 29
0

A few days back I was looking for solutions here about deleting child rows on deleting parent row(Casacading Deletes), I seem to have solved it on a project am currently working on. I use Casade.ALL for my bidirectional many-to-one relationship. Solution:

// Parent Entity.
@Entity
public class Parent implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "parent")
    private List<RequisitionDetail> children = new LinkedList<>();
    ...
    // Getters & Setters.
    ...
}

// Child Entity.
@Entity
public class Child implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "parent_id")
    private Parent parent;
    ...
    // Getters & Setters.
    ...
}

// Save your entities as below.
public void requisitionInsert() {
    try {
        Parent parent = new Parent();
        Child child = new Child();
        List<Child> children = new LinkedList<>();
        ...
        // Add parent to child
        child.setParent(parent);
        // Add child to list
        children.add(child);
        // Add children list to parent 
        parent.setChilds(children);
        // Save parent entity
        ParentDao.saveParent(parent);
    } catch (Exception e) {
        throw new RuntimeException();
    }
}

My cascading deletes failed at first since my parent rows had no child rows and doing it this way sloved my errors and cascading deletes work fine too.

-2

Don't use cascade = CascadeType.REMOVE Documentation here

Because of your db may be destroyed. You can use formal order. Delete sub stable and then remove master table

Muthu
  • 156
  • 8