HHH-12974 - Document @OnDelete behavior in regards to disabling the Persistence Context entity removal cascading event

This commit is contained in:
Vlad Mihalcea 2018-09-20 11:31:40 +03:00
parent b3e56a5db7
commit 48909896b6
7 changed files with 198 additions and 8 deletions

View File

@ -825,7 +825,7 @@ the automatic schema generator will apply the ON DELETE CASCADE SQL directive to
as illustrated by the following example.
[[pc-cascade-on-delete-mapping-example]]
.`@OnDelete` mapping
.`@OnDelete` `@ManyToOne` mapping
====
[source, JAVA, indent=0]
----
@ -843,10 +843,10 @@ include::{extrasdir}/pc-cascade-on-delete-mapping-example.sql[]
----
====
Now, you can just remove the `Person` entity, and the associated `Phone` is going to be removed automatically.
Now, you can just remove the `Person` entity, and the associated `Phone` entities are going to be deleted automatically via the Foreign Key cascade.
[[pc-cascade-on-delete-example]]
.`@OnDelete` example
.`@OnDelete` `@ManyToOne` delete example
====
[source, JAVA, indent=0]
----
@ -857,4 +857,45 @@ include::{sourcedir}/CascadeOnDeleteTest.java[tags=pc-cascade-on-delete-example]
----
include::{extrasdir}/pc-cascade-on-delete-example.sql[]
----
====
The `@OnDelete` annotation can also be placed on a collection, as
illustrated in the following example.
[[pc-cascade-on-delete-collection-mapping-example]]
.`@OnDelete` `One@ToMany` mapping
====
[source, JAVA, indent=0]
----
include::{sourcedir}/CascadeOnDeleteCollectionTest.java[tags=pc-cascade-on-delete-collection-mapping-Person-example]
----
[source, JAVA, indent=0]
----
include::{sourcedir}/CascadeOnDeleteCollectionTest.java[tags=pc-cascade-on-delete-collection-mapping-Phone-example]
----
====
Now, when removing the `Person` entity, all the associated `Phone` child entities are deleted via the Foreign Key cascade even if the `@OneToMany` collection was using the `CascadeType.ALL` attribute.
[[pc-cascade-on-delete-collection-example]]
.`@OnDelete` `@ManyToOne` delete example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/CascadeOnDeleteCollectionTest.java[tags=pc-cascade-on-delete-collection-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/pc-cascade-on-delete-example.sql[]
----
====
[NOTE]
====
Without the `@OnDelete` annotation, the `@OneToMany` association relies on the `cascade` attribute to propagate the `remove` entity state transition from the parent entity to its children.
However, when the `@OnDelete` annotation is in place, Hibernate prevents the child entity `DELETE` statement from being executed while flushing the Persistence Context.
This way, only the parent entity gets deleted, and all the associated child records are removed by the database engine, instead of being deleted explicitly via `DELETE` statements.
====

View File

@ -0,0 +1,149 @@
package org.hibernate.userguide.pc;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
/**
* @author Vlad Mihalcea
*/
public class CascadeOnDeleteCollectionTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class,
Phone.class
};
}
@Test
public void test() {
doInJPA( this::entityManagerFactory, entityManager -> {
Person person = new Person();
person.setId( 1L );
person.setName( "John Doe" );
entityManager.persist( person );
Phone phone1 = new Phone();
phone1.setId( 1L );
phone1.setNumber( "123-456-7890" );
phone1.setOwner( person );
person.addPhone( phone1 );
Phone phone2 = new Phone();
phone2.setId( 2L );
phone2.setNumber( "101-010-1234" );
phone2.setOwner( person );
person.addPhone( phone2 );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::pc-cascade-on-delete-collection-example[]
Person person = entityManager.find( Person.class, 1L );
entityManager.remove( person );
//end::pc-cascade-on-delete-collection-example[]
} );
}
//tag::pc-cascade-on-delete-collection-mapping-Person-example[]
@Entity(name = "Person")
public static class Person {
@Id
private Long id;
private String name;
@OneToMany(mappedBy = "owner", cascade = CascadeType.ALL)
@OnDelete(action = OnDeleteAction.CASCADE)
private List<Phone> phones = new ArrayList<>();
//Getters and setters are omitted for brevity
//end::pc-cascade-on-delete-collection-mapping-Person-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void addPhone(Phone phone) {
phone.setOwner( this );
phones.add( phone );
}
//tag::pc-cascade-on-delete-collection-mapping-Person-example[]
}
//end::pc-cascade-on-delete-collection-mapping-Person-example[]
//tag::pc-cascade-on-delete-collection-mapping-Phone-example[]
@Entity(name = "Phone")
public static class Phone {
@Id
private Long id;
@Column(name = "`number`")
private String number;
@ManyToOne(fetch = FetchType.LAZY)
private Person owner;
//Getters and setters are omitted for brevity
//end::pc-cascade-on-delete-collection-mapping-Phone-example[]
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Person getOwner() {
return owner;
}
public void setOwner(Person owner) {
this.owner = owner;
}
//tag::pc-cascade-on-delete-collection-mapping-Phone-example[]
}
//end::pc-cascade-on-delete-collection-mapping-Phone-example[]
}

View File

@ -19,7 +19,7 @@ import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.wildfly.common.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
/**
* @author Vlad Mihalcea

View File

@ -36,7 +36,7 @@ import org.jboss.logging.Logger;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertTrue;
import static org.wildfly.common.Assert.assertFalse;
import static org.junit.Assert.assertFalse;
/**
* @author Vlad Mihalcea

View File

@ -26,7 +26,7 @@ import org.hibernate.testing.TestForIssue;
import org.junit.Before;
import org.junit.Test;
import static org.wildfly.common.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
/**
* @author Vlad Mihalcea

View File

@ -21,7 +21,7 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.wildfly.common.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
/**
* Tests related to contains operations on a PersistentBag.

View File

@ -31,7 +31,7 @@ import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
import static org.wildfly.common.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
/**
* @author Chris Cranford