From baf194d4228bb8fce42f080acbc080d306a6a614 Mon Sep 17 00:00:00 2001 From: Vlad Mihalcea Date: Thu, 25 May 2017 17:12:39 +0300 Subject: [PATCH] HHH-11186 - Add examples for all Hibernate annotations Document @OnDelete annotation --- .../userguide/appendices/Annotations.adoc | 2 +- .../chapters/pc/PersistenceContext.adoc | 43 ++++++ .../extras/pc-cascade-on-delete-example.sql | 3 + .../pc-cascade-on-delete-mapping-example.sql | 18 +++ .../userguide/pc/CascadeOnDeleteTest.java | 125 ++++++++++++++++++ 5 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-cascade-on-delete-example.sql create mode 100644 documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-cascade-on-delete-mapping-example.sql create mode 100644 documentation/src/test/java/org/hibernate/userguide/pc/CascadeOnDeleteTest.java diff --git a/documentation/src/main/asciidoc/userguide/appendices/Annotations.adoc b/documentation/src/main/asciidoc/userguide/appendices/Annotations.adoc index 7054114d98..7f8b548a5d 100644 --- a/documentation/src/main/asciidoc/userguide/appendices/Annotations.adoc +++ b/documentation/src/main/asciidoc/userguide/appendices/Annotations.adoc @@ -1047,7 +1047,7 @@ The two possible strategies are defined by the https://docs.jboss.org/hibernate/ CASCADE:: Use the database FOREIGN KEY cascade capabilities. NO_ACTION:: Take no action. -//TODO: Add example +See the <> chapter for more info. [[annotations-hibernate-optimisticlock]] ==== `@OptimisticLock` diff --git a/documentation/src/main/asciidoc/userguide/chapters/pc/PersistenceContext.adoc b/documentation/src/main/asciidoc/userguide/chapters/pc/PersistenceContext.adoc index cec28f8bea..72e15064aa 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/pc/PersistenceContext.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/pc/PersistenceContext.adoc @@ -696,6 +696,7 @@ Even if just the `Person` parent entity was persisted, Hibernate has managed to The `CascadeType.MERGE` allows us to merge a child entity along with the parent one. +[[pc-cascade-merge-example]] .`CascadeType.MERGE` example ==== [source, JAVA, indent=0] @@ -755,6 +756,7 @@ Such a use case requires the use of the `PessimisticLockScope.EXTENDED` value of However, `CascadeType.LOCK` allows us to reattach a parent entity along with its children to the currently running Persistence Context. +[[pc-cascade-lock-example]] .`CascadeType.LOCK` example ==== [source, JAVA, indent=0] @@ -769,6 +771,7 @@ include::{sourcedir}/CascadeLockTest.java[tags=pc-cascade-lock-example] The `CascadeType.REFRESH` is used to propagate the refresh operation from a parent entity to a child. The refresh operation will discard the current entity state, and it will override it using the one loaded from the database. +[[pc-cascade-refresh-example]] .`CascadeType.REFRESH` example ==== [source, JAVA, indent=0] @@ -790,6 +793,7 @@ In the aforementioned example, you can see that both the `Person` and `Phone` en The `CascadeType.REPLICATE` is to replicate both the parent and the child entities. The replicate operation allows you to synchronize entities coming from different sources of data. +[[pc-cascade-replicate-example]] .`CascadeType.REPLICATE` example ==== [source, JAVA, indent=0] @@ -805,3 +809,42 @@ include::{extrasdir}/pc-cascade-replicate-example.sql[] As illustrated by the SQL statements being generated, both the `Person` and `Phone` entities are replicated to the underlying database rows. +[[pc-cascade-on-delete]] +==== `@OnDelete` cascade + +While the previous cascade types propagate entity state transitions, the `@OnDelete` cascade is a DDL-level FK feature which allows you +to remove a child record whenever the parent row is deleted. + +So, when annotating the `@ManyToOne` association with `@OnDelete( action = OnDeleteAction.CASCADE )`, +the automatic schema generator will apply the ON DELETE CASCADE SQL directive to the Foreign Key declaration, +as illustrated by the following example. + +[[pc-cascade-on-delete-mapping-example]] +.`@OnDelete` mapping +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/CascadeOnDeleteTest.java[tags=pc-cascade-on-delete-mapping-example] +---- + +[source, SQL, indent=0] +---- +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. + +[[pc-cascade-on-delete-example]] +.`@OnDelete` example +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/CascadeOnDeleteTest.java[tags=pc-cascade-on-delete-example] +---- + +[source, SQL, indent=0] +---- +include::{extrasdir}/pc-cascade-on-delete-example.sql[] +---- +==== \ No newline at end of file diff --git a/documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-cascade-on-delete-example.sql b/documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-cascade-on-delete-example.sql new file mode 100644 index 0000000000..bcc8c1e021 --- /dev/null +++ b/documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-cascade-on-delete-example.sql @@ -0,0 +1,3 @@ +delete from Person where id = ? + +-- binding parameter [1] as [BIGINT] - [1] \ No newline at end of file diff --git a/documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-cascade-on-delete-mapping-example.sql b/documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-cascade-on-delete-mapping-example.sql new file mode 100644 index 0000000000..4bf9e79011 --- /dev/null +++ b/documentation/src/main/asciidoc/userguide/chapters/pc/extras/pc-cascade-on-delete-mapping-example.sql @@ -0,0 +1,18 @@ +create table Person ( + id bigint not null, + name varchar(255), + primary key (id) +) + +create table Phone ( + id bigint not null, + "number" varchar(255), + owner_id bigint, + primary key (id) +) + +alter table Phone + add constraint FK82m836qc1ss2niru7eogfndhl + foreign key (owner_id) + references Person + on delete cascade \ No newline at end of file diff --git a/documentation/src/test/java/org/hibernate/userguide/pc/CascadeOnDeleteTest.java b/documentation/src/test/java/org/hibernate/userguide/pc/CascadeOnDeleteTest.java new file mode 100644 index 0000000000..a8ebb432ba --- /dev/null +++ b/documentation/src/test/java/org/hibernate/userguide/pc/CascadeOnDeleteTest.java @@ -0,0 +1,125 @@ +package org.hibernate.userguide.pc; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +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 CascadeOnDeleteTest 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 phone = new Phone(); + phone.setId( 1L ); + phone.setNumber( "123-456-7890" ); + phone.setOwner( person ); + entityManager.persist( phone ); + } ); + + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::pc-cascade-on-delete-example[] + Person person = entityManager.find( Person.class, 1L ); + entityManager.remove( person ); + //end::pc-cascade-on-delete-example[] + } ); + } + + //tag::pc-cascade-on-delete-mapping-example[] + @Entity(name = "Person") + public static class Person { + + @Id + private Long id; + + private String name; + + //Getters and setters are omitted for brevity + //end::pc-cascade-on-delete-mapping-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; + } + //tag::pc-cascade-on-delete-mapping-example[] + } + + @Entity(name = "Phone") + public static class Phone { + + @Id + private Long id; + + @Column(name = "`number`") + private String number; + + @ManyToOne(fetch = FetchType.LAZY) + @OnDelete( action = OnDeleteAction.CASCADE ) + private Person owner; + + //Getters and setters are omitted for brevity + //end::pc-cascade-on-delete-mapping-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-mapping-example[] + } + //end::pc-cascade-on-delete-mapping-example[] +} \ No newline at end of file