perp
perp

Reputation: 3963

JPA: unidirectional many-to-one and cascading delete

Say I have a unidirectional @ManyToOne relationship like the following:

@Entity
public class Parent implements Serializable {

    @Id
    @GeneratedValue
    private long id;
}

@Entity
public class Child implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    @ManyToOne
    @JoinColumn
    private Parent parent;  
}

If I have a parent P and children C1...Cn referencing back to P, is there a clean and pretty way in JPA to automatically remove the children C1...Cn when P is removed (i.e. entityManager.remove(P))?

What I'm looking for is a functionality similar to ON DELETE CASCADE in SQL.

Upvotes: 132

Views: 167505

Answers (8)

bladekp
bladekp

Reputation: 1647

As others mentioned, in hibernate you can use @OnDelete with all limitations it have. Unfortunately there is no option to do so in JPA, workaround could be to define this rule directly in schema, on foreign key definition (for example in liquibase syntax):

<addForeignKeyConstraint onDelete="CASCADE" baseTableName="child" baseColumnNames="parent_id" constraintName="child_parent_id_fk" referencedTableName="parent" referencedColumnNames="id"/>

Upvotes: 0

Thomas Hunziker
Thomas Hunziker

Reputation: 2038

If you are using hibernate as your JPA provider you can use the annotation @OnDelete. This annotation will add to the relation the trigger ON DELETE CASCADE, which delegates the deletion of the children to the database.

Example:

public class Parent {
   
        @Id
        private long id;

}


public class Child {
        
        @Id
        private long id;
  
        @ManyToOne
        @OnDelete(action = OnDeleteAction.CASCADE)
        private Parent parent;
}
     

With this solution a unidirectional relationship from the child to the parent is enough to automatically remove all children. This solution does not need any listeners etc. Also a JPQL query like DELETE FROM Parent WHERE id = 1 will remove the children.

Upvotes: 140

Chedy
Chedy

Reputation: 57

You don't need to use bi-directional association instead of your code, you have just to add CascaType.Remove as a property to ManyToOne annotation, then use @OnDelete(action = OnDeleteAction.CASCADE), it's works fine for me.

Upvotes: 1

Chakresh Tiwari
Chakresh Tiwari

Reputation: 747

Use this way to delete only one side

    @ManyToOne(cascade=CascadeType.PERSIST, fetch = FetchType.LAZY)
//  @JoinColumn(name = "qid")
    @JoinColumn(name = "qid", referencedColumnName = "qid", foreignKey = @ForeignKey(name = "qid"), nullable = false)
    // @JsonIgnore
    @JsonBackReference
    private QueueGroup queueGroup;

Upvotes: 0

ranjesh
ranjesh

Reputation: 71

I have seen in unidirectional @ManytoOne, delete don't work as expected. When parent is deleted, ideally child should also be deleted, but only parent is deleted and child is NOT deleted and is left as orphan

Technology used are Spring Boot/Spring Data JPA/Hibernate

Sprint Boot : 2.1.2.RELEASE

Spring Data JPA/Hibernate is used to delete row .eg

parentRepository.delete(parent)

ParentRepository extends standard CRUD repository as shown below ParentRepository extends CrudRepository<T, ID>

Following are my entity class

@Entity(name = “child”)
public class Child  {

    @Id
    @GeneratedValue
    private long id;

    @ManyToOne( fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = “parent_id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Parent parent;
}

@Entity(name = “parent”)
public class Parent {

    @Id
    @GeneratedValue
    private long id;

    @Column(nullable = false, length = 50)
    private String firstName;


}

Upvotes: 3

Swarit Agarwal
Swarit Agarwal

Reputation: 2648

@Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)

Given annotation worked for me. Can have a try

For Example :-

     public class Parent{
            @Id
            @GeneratedValue(strategy=GenerationType.AUTO)
            @Column(name="cct_id")
            private Integer cct_id;
            @OneToMany(cascade=CascadeType.REMOVE, fetch=FetchType.EAGER,mappedBy="clinicalCareTeam", orphanRemoval=true)
            @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
            private List<Child> childs;
        }
            public class Child{
            @ManyToOne(fetch=FetchType.EAGER)
            @JoinColumn(name="cct_id")
            private Parent parent;
    }

Upvotes: -1

Vineet Reynolds
Vineet Reynolds

Reputation: 76709

Relationships in JPA are always unidirectional, unless you associate the parent with the child in both directions. Cascading REMOVE operations from the parent to the child will require a relation from the parent to the child (not just the opposite).

You'll therefore need to do this:

  • Either, change the unidirectional @ManyToOne relationship to a bi-directional @ManyToOne, or a unidirectional @OneToMany. You can then cascade REMOVE operations so that EntityManager.remove will remove the parent and the children. You can also specify orphanRemoval as true, to delete any orphaned children when the child entity in the parent collection is set to null, i.e. remove the child when it is not present in any parent's collection.
  • Or, specify the foreign key constraint in the child table as ON DELETE CASCADE. You'll need to invoke EntityManager.clear() after calling EntityManager.remove(parent) as the persistence context needs to be refreshed - the child entities are not supposed to exist in the persistence context after they've been deleted in the database.

Upvotes: 86

tekumara
tekumara

Reputation: 8807

Create a bi-directional relationship, like this:

@Entity
public class Parent implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
    private Set<Child> children;
}

Upvotes: 16

Related Questions