HHH-11186 - Add examples for all Hibernate annotations
Document more annotations: - @Cascade
This commit is contained in:
parent
828fb09eb8
commit
260b21bd63
|
@ -642,6 +642,8 @@ For JPA cascading, prefer using the http://docs.oracle.com/javaee/7/api/javax/pe
|
|||
|
||||
When combining both JPA and Hibernate `CascadeType` strategies, Hibernate will merge both sets of cascades.
|
||||
|
||||
See the <<chapters/pc/PersistenceContext.adoc#pc-cascade,Cascading>> chapter for more info.
|
||||
|
||||
[[annotations-hibernate-check]]
|
||||
==== `@Check`
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
== Persistence Contexts
|
||||
:sourcedir: ../../../../../test/java/org/hibernate/userguide/pc
|
||||
:sourcedir-caching: ../../../../../test/java/org/hibernate/userguide/caching
|
||||
:extrasdir: extras
|
||||
|
||||
Both the `org.hibernate.Session` API and `javax.persistence.EntityManager` API represent a context for dealing with persistent data.
|
||||
This concept is called a `persistence context`.
|
||||
|
@ -562,4 +563,125 @@ To verify if an entity instance is currently attached to the running persistence
|
|||
----
|
||||
include::{sourcedir-caching}/FirstLevelCacheTest.java[tags=caching-management-contains-example]
|
||||
----
|
||||
====
|
||||
====
|
||||
|
||||
[[pc-cascade]]
|
||||
=== Cascading entity state transitions
|
||||
|
||||
JPA allows you to propagate the state transition from a parent entity to a child.
|
||||
For this purpose, the JPA `javax.persistence.CascadeType` defines various cascade types:
|
||||
|
||||
`ALL`:: cascades all entity state transitions
|
||||
`PERSIST`:: cascades the entity persist operation.
|
||||
`MERGE`:: cascades the entity merge operation.
|
||||
`REMOVE`:: cascades the entity remove operation.
|
||||
`REFRESH`:: cascades the entity refresh operation.
|
||||
`DETACH`:: cascades the entity detach operation.
|
||||
|
||||
Additionally, the `CascadeType.ALL` will propagate any Hibernate-specific operation, which is defined by the `org.hibernate.annotations.CascadeType` enum:
|
||||
|
||||
`SAVE_UPDATE`:: cascades the entity saveOrUpdate operation.
|
||||
`REPLICATE`:: cascades the entity replicate operation.
|
||||
`LOCK`:: cascades the entity lock operation.
|
||||
|
||||
The following examples will explain some of the aforementioned cascade operations using the following entities:
|
||||
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/Person.java[tags=pc-cascade-domain-model-example]
|
||||
|
||||
include::{sourcedir}/Phone.java[tags=pc-cascade-domain-model-example]
|
||||
----
|
||||
|
||||
[[pc-cascade-persist]]
|
||||
==== `CascadeType.PERSIST`
|
||||
|
||||
The `CascadeType.PERSIST` allows us to persist a child entity along with the parent one.
|
||||
|
||||
[[pc-cascade-persist-example]]
|
||||
.`CascadeType.PERSIST` example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/CascadePersistTest.java[tags=pc-cascade-persist-example]
|
||||
----
|
||||
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
include::{extrasdir}/pc-cascade-persist-example.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
Even if just the `Person` parent entity was persisted, Hibernate has managed to cascade the persist operation to the associated `Phone` child entity as well.
|
||||
|
||||
[[pc-cascade-merge]]
|
||||
==== `CascadeType.MERGE`
|
||||
|
||||
The `CascadeType.MERGE` allows us to merge a child entity along with the parent one.
|
||||
|
||||
.`CascadeType.MERGE` example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/CascadeMergeTest.java[tags=pc-cascade-merge-example]
|
||||
----
|
||||
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
include::{extrasdir}/pc-cascade-merge-example.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
During merge, the current state of the entity is copied onto the entity version that was just fetched from the database.
|
||||
That's the reason why Hibernate executed the SELECT statement which fetched both the `Person` entity along with its children.
|
||||
|
||||
[[pc-cascade-remove]]
|
||||
==== `CascadeType.REMOVE`
|
||||
|
||||
The `CascadeType.REMOVE` allows us to remove a child entity along with the parent one.
|
||||
Traditionally, Hibernate called this operation delete, that's why the `org.hibernate.annotations.CascadeType` provides a `DELETE` cascade option.
|
||||
However, `CascadeType.REMOVE` and `org.hibernate.annotations.CascadeType.DELETE` are identical.
|
||||
|
||||
[[pc-cascade-remove-example]]
|
||||
.`CascadeType.REMOVE` example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/CascadeRemoveTest.java[tags=pc-cascade-remove-example]
|
||||
----
|
||||
|
||||
[source, SQL, indent=0]
|
||||
----
|
||||
include::{extrasdir}/pc-cascade-remove-example.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
[[pc-cascade-detach]]
|
||||
==== `CascadeType.DETACH`
|
||||
|
||||
`CascadeType.DETACH` is used to propagate the detach operation from a parent entity to a child.
|
||||
|
||||
[[pc-cascade-detach-example]]
|
||||
.`CascadeType.DETACH` example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/CascadeDetachTest.java[tags=pc-cascade-detach-example]
|
||||
----
|
||||
====
|
||||
|
||||
[[pc-cascade-lock]]
|
||||
==== `CascadeType.LOCK`
|
||||
|
||||
Although unintuitively, `CascadeType.LOCK` does not propagate a lock request from a parent entity to its children.
|
||||
Such a use case requires the use of the `PessimisticLockScope.EXTENDED` value pf the `javax.persistence.lock.scope` property.
|
||||
|
||||
However, `CascadeType.LOCK` allows us to reattach a parent entity along with it s children to the currently running Persistence Context.
|
||||
|
||||
.`CascadeType.LOCK` example
|
||||
====
|
||||
[source, JAVA, indent=0]
|
||||
----
|
||||
include::{sourcedir}/CascadeLockTest.java[tags=pc-cascade-lock-example]
|
||||
----
|
||||
====
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
SELECT
|
||||
p.id as id1_0_1_,
|
||||
p.name as name2_0_1_,
|
||||
ph.owner_id as owner_id3_1_3_,
|
||||
ph.id as id1_1_3_,
|
||||
ph.id as id1_1_0_,
|
||||
ph."number" as number2_1_0_,
|
||||
ph.owner_id as owner_id3_1_0_
|
||||
FROM
|
||||
Person p
|
||||
LEFT OUTER JOIN
|
||||
Phone ph
|
||||
on p.id=ph.owner_id
|
||||
WHERE
|
||||
p.id = 1
|
|
@ -0,0 +1,5 @@
|
|||
INSERT INTO Person ( name, id )
|
||||
VALUES ( 'John Doe', 1 )
|
||||
|
||||
INSERT INTO Phone ( `number`, person_id, id )
|
||||
VALUE ( '123-456-7890', 1, 1 )
|
|
@ -0,0 +1,3 @@
|
|||
DELETE FROM Phone WHERE id = 1
|
||||
|
||||
DELETE FROM Person WHERE id = 1
|
|
@ -0,0 +1,59 @@
|
|||
package org.hibernate.userguide.pc;
|
||||
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Fábio Takeo Ueno
|
||||
*/
|
||||
public class CascadeDetachTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Person.class,
|
||||
Phone.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void detachTest() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Person person = new Person();
|
||||
person.setId( 1L );
|
||||
person.setName( "John Doe" );
|
||||
|
||||
Phone phone = new Phone();
|
||||
phone.setId( 1L );
|
||||
phone.setNumber( "123-456-7890" );
|
||||
|
||||
person.addPhone( phone );
|
||||
entityManager.persist( person );
|
||||
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
|
||||
//tag::pc-cascade-detach-example[]
|
||||
Person person = entityManager.find( Person.class, 1L );
|
||||
assertEquals( 1, person.getPhones().size() );
|
||||
Phone phone = person.getPhones().get( 0 );
|
||||
|
||||
assertTrue( entityManager.contains( person ));
|
||||
assertTrue( entityManager.contains( phone ));
|
||||
|
||||
entityManager.detach( person );
|
||||
|
||||
assertFalse( entityManager.contains( person ));
|
||||
assertFalse( entityManager.contains( phone ));
|
||||
|
||||
//end::pc-cascade-detach-example[]
|
||||
} );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package org.hibernate.userguide.pc;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Fábio Takeo Ueno
|
||||
*/
|
||||
public class CascadeLockTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Person.class,
|
||||
Phone.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lockTest() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Person person = new Person();
|
||||
person.setId( 1L );
|
||||
person.setName( "John Doe" );
|
||||
|
||||
Phone phone = new Phone();
|
||||
phone.setId( 1L );
|
||||
phone.setNumber( "123-456-7890" );
|
||||
|
||||
person.addPhone( phone );
|
||||
entityManager.persist( person );
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
|
||||
//tag::pc-cascade-lock-example[]
|
||||
Person person = entityManager.find( Person.class, 1L );
|
||||
assertEquals( 1, person.getPhones().size() );
|
||||
Phone phone = person.getPhones().get( 0 );
|
||||
|
||||
assertTrue( entityManager.contains( person ) );
|
||||
assertTrue( entityManager.contains( phone ) );
|
||||
|
||||
entityManager.detach( person );
|
||||
|
||||
assertFalse( entityManager.contains( person ) );
|
||||
assertFalse( entityManager.contains( phone ) );
|
||||
|
||||
entityManager.unwrap( Session.class )
|
||||
.buildLockRequest( new LockOptions( LockMode.NONE ) )
|
||||
.lock( person );
|
||||
|
||||
assertTrue( entityManager.contains( person ) );
|
||||
assertTrue( entityManager.contains( phone ) );
|
||||
//end::pc-cascade-lock-example[]
|
||||
} );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package org.hibernate.userguide.pc;
|
||||
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
|
||||
/**
|
||||
* @author Fábio Takeo Ueno
|
||||
*/
|
||||
public class CascadeMergeTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Person.class,
|
||||
Phone.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mergeTest() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Person person = new Person();
|
||||
person.setId( 1L );
|
||||
person.setName( "John Doe" );
|
||||
|
||||
Phone phone = new Phone();
|
||||
phone.setId( 1L );
|
||||
phone.setNumber( "123-456-7890" );
|
||||
|
||||
person.addPhone( phone );
|
||||
|
||||
entityManager.persist( person );
|
||||
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
|
||||
//tag::pc-cascade-merge-example[]
|
||||
Phone phone = entityManager.find( Phone.class, 1L );
|
||||
Person person = phone.getOwner();
|
||||
|
||||
person.setName( "John Doe Jr." );
|
||||
phone.setNumber( "987-654-3210" );
|
||||
|
||||
entityManager.clear();
|
||||
|
||||
entityManager.merge( person );
|
||||
//end::pc-cascade-merge-example[]
|
||||
} );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package org.hibernate.userguide.pc;
|
||||
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
|
||||
/**
|
||||
* @author Fábio Takeo Ueno
|
||||
*/
|
||||
public class CascadePersistTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Person.class,
|
||||
Phone.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistTest() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
//tag::pc-cascade-persist-example[]
|
||||
Person person = new Person();
|
||||
person.setId( 1L );
|
||||
person.setName( "John Doe" );
|
||||
|
||||
Phone phone = new Phone();
|
||||
phone.setId( 1L );
|
||||
phone.setNumber( "123-456-7890" );
|
||||
|
||||
person.addPhone( phone );
|
||||
|
||||
entityManager.persist( person );
|
||||
//end::pc-cascade-persist-example[]
|
||||
} );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package org.hibernate.userguide.pc;
|
||||
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
|
||||
/**
|
||||
* @author Fábio Takeo Ueno
|
||||
*/
|
||||
public class CascadeRemoveTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] {
|
||||
Person.class,
|
||||
Phone.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteTest() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
Person person = new Person();
|
||||
person.setId( 1L );
|
||||
person.setName( "John Doe" );
|
||||
|
||||
Phone phone = new Phone();
|
||||
phone.setId( 1L );
|
||||
phone.setNumber( "123-456-7890" );
|
||||
|
||||
person.addPhone( phone );
|
||||
entityManager.persist( person );
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
//tag::pc-cascade-remove-example[]
|
||||
Person person = entityManager.find( Person.class, 1L );
|
||||
|
||||
entityManager.remove( person );
|
||||
//end::pc-cascade-remove-example[]
|
||||
} );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package org.hibernate.userguide.pc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
//tag::pc-cascade-domain-model-example[]
|
||||
@Entity
|
||||
public class Person {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@OneToMany(mappedBy = "owner", cascade = CascadeType.ALL)
|
||||
private List<Phone> phones = new ArrayList<>();
|
||||
|
||||
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 List<Phone> getPhones() {
|
||||
return phones;
|
||||
}
|
||||
|
||||
public void addPhone(Phone phone) {
|
||||
this.phones.add( phone );
|
||||
phone.setOwner( this );
|
||||
}
|
||||
}
|
||||
|
||||
//end::pc-cascade-domain-model-example[]
|
|
@ -0,0 +1,49 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
//tag::pc-cascade-domain-model-example[]
|
||||
@Entity
|
||||
public class Phone {
|
||||
|
||||
@Id
|
||||
private Long id;
|
||||
|
||||
@Column(name = "`number`")
|
||||
private String number;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
private Person owner;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
//end::pc-cascade-domain-model-example[]
|
Loading…
Reference in New Issue