HHH-11186 - Add examples for all Hibernate annotations

- CascadeType.REFRESH and CascadeType.REPLICATE
This commit is contained in:
Fábio Ueno 2017-02-13 18:50:09 -02:00 committed by Vlad Mihalcea
parent 3c2939f0e5
commit 54503bf7f4
7 changed files with 210 additions and 10 deletions

View File

@ -403,7 +403,6 @@ and is a far more efficient alternative to bulk eviction of the region via `Sess
----
include::{sourcedir}/SecondLevelCacheTest.java[tags=caching-query-region-native-evict-example]
----
====
[[caching-management]]
@ -710,6 +709,7 @@ Clustering the Infinispan second-level cache provider is as simple as adding the
<!-- Use Infinispan second level cache provider -->
<property name="hibernate.cache.region.factory_class"
value="org.hibernate.cache.infinispan.InfinispanRegionFactory"/>
----
====
As with the standalone local mode, at the bare minimum the region factory has to be configured to point to an Infinispan region factory implementation.
@ -879,6 +879,7 @@ See examples below on how entity/collection specific configurations can be appli
<property
name="hibernate.cache.infinispan.cfg"
value="my-infinispan-configuration.xml" />
----
====
[NOTE]
@ -886,7 +887,6 @@ See examples below on how entity/collection specific configurations can be appli
If the cache is configured as transactional, InfinispanRegionFactory automatically sets transaction manager so that the TM used by Infinispan is the same as TM used by Hibernate.
====
Cache configuration can differ for each type of data stored in the cache. In order to override the cache configuration template, use property `hibernate.cache.infinispan._data-type_.cfg` where `_data-type_` can be one of:
`entity`:: Entities indexed by `@Id` or `@EmbeddedId` attribute.
@ -916,10 +916,12 @@ For specifying cache template for specific region, use region name instead of th
<property
name="hibernate.cache.infinispan.com.example.MyEntity.someCollection.cfg"
value="my-entities-some-collection" />
----
====
.Use custom cache template in Wildfly
When applying entity/collection level changes inside JPA applications deployed in Wildfly, it is necessary to specify deployment name and persistence unit name:
====
[source, XML, indent=0]
----
@ -929,6 +931,7 @@ When applying entity/collection level changes inside JPA applications deployed i
<property
name="hibernate.cache.infinispan._war_or_ear_name_._unit_name_.com.example.MyEntity.someCollection.cfg"
value="my-entities-some-collection" />
----
====
[IMPORTANT]
@ -959,6 +962,7 @@ Example:
   value= "60000"/>
<property name="hibernate.cache.infinispan.entity.expiration.max_idle"
   value= "30000"/>
----
====
With the above configuration, you're overriding whatever eviction/expiration settings were defined for the default entity cache name in the Infinispan cache configuration used, regardless of whether it's the default one or user defined.
@ -978,6 +982,7 @@ Example:
<property name="hibernate.cache.infinispan.com.acme.Person.eviction.strategy"
   value= "LIRS"/>
----
====
Inside of Wildfly, same as with the entity/collection configuration override, eviction/expiration settings would also require deployment name and persistence unit information:
@ -988,6 +993,7 @@ Inside of Wildfly, same as with the entity/collection configuration override, ev
<property name="hibernate.cache.infinispan._war_or_ear_name_._unit_name_.com.acme.Person.expiration.lifespan"
   value= "65000"/>
----
====
[NOTE]
====
@ -1003,13 +1009,13 @@ Property `hibernate.cache.infinispan.use_synchronization` that allowed to regist
[[caching-provider-infinispan-remote]]
==== Remote Infinispan Caching
Lately, several questions ( link:http://community.jboss.org/message/575814#575814[here] and link:http://community.jboss.org/message/585841#585841[here] ) have appeared in the Infinispan user forums asking whether it'd be possible to have an Infinispan second level cache that instead of living in the same JVM as the Hibernate code, it resides in a remote server, i.e. an Infinispan Hot Rod server.
It's important to understand that trying to set up second level cache in this way is generally not a good idea for the following reasons:
* The purpose of a JPA/Hibernate second level cache is to store entities/collections recently retrieved from database or to maintain results of recent queries.
So, part of the aim of the second level cache is to have data accessible locally rather than having to go to the database to retrieve it everytime this is needed.
Hence, if you decide to set the second level cache to be remote as well, you're losing one of the key advantages of the second level cache: the fact that the cache is local to the code that requires it.
* Setting a remote second level cache can have a negative impact in the overall performance of your application because it means that cache misses require accessing a remote location to verify whether a particular entity/collection/query is cached.
With a local second level cache however, these misses are resolved locally and so they are much faster to execute than with a remote second level cache.
@ -1017,16 +1023,15 @@ There are however some edge cases where it might make sense to have a remote sec
* You are having memory issues in the JVM where JPA/Hibernate code and the second level cache is running.
Off loading the second level cache to remote Hot Rod servers could be an interesting way to separate systems and allow you find the culprit of the memory issues more easily.
* Your application layer cannot be clustered but you still want to run multiple application layer nodes.
In this case, you can't have multiple local second level cache instances running because they won't be able to invalidate each other for example when data in the second level cache is updated.
In this case, having a remote second level cache could be a way out to make sure your second level cache is always in a consistent state, will all nodes in the application layer pointing to it.
* Rather than having the second level cache in a remote server, you want to simply keep the cache in a separate VM still within the same machine.
In this case you would still have the additional overhead of talking across to another JVM, but it wouldn't have the latency of across a network.
+
The benefit of doing this is that:
+
** Size the cache separate from the application, since the cache and the application server have very different memory profiles.
One has lots of short lived objects, and the other could have lots of long lived objects.
** To pin the cache and the application server onto different CPU cores (using _numactl_ ), and even pin them to different physically memory based on the NUMA nodes.
** To pin the cache and the application server onto different CPU cores (using _numactl_ ), and even pin them to different physically memory based on the NUMA nodes.

View File

@ -674,9 +674,9 @@ include::{sourcedir}/CascadeDetachTest.java[tags=pc-cascade-detach-example]
==== `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.
Such a use case requires the use of the `PessimisticLockScope.EXTENDED` value of 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.
However, `CascadeType.LOCK` allows us to reattach a parent entity along with its children to the currently running Persistence Context.
.`CascadeType.LOCK` example
====
@ -685,3 +685,46 @@ However, `CascadeType.LOCK` allows us to reattach a parent entity along with it
include::{sourcedir}/CascadeLockTest.java[tags=pc-cascade-lock-example]
----
====
[[pc-cascade-refresh]]
==== `CascadeType.REFRESH`
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.
.`CascadeType.REFRESH` example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/CascadeRefreshTest.java[tags=pc-cascade-refresh-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/pc-cascade-refresh-example.sql[]
----
====
In the aforementioned example, you can see that both the `Person` and `Phone` entities are refreshed even if we only called this operation on the parent entity only.
[[pc-cascade-replicate]]
==== `CascadeType.REPLICATE`
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.
.`CascadeType.REPLICATE` example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/CascadeReplicateTest.java[tags=pc-cascade-replicate-example]
----
[source, SQL, indent=0]
----
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.

View File

@ -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

View File

@ -0,0 +1,28 @@
SELECT
id
FROM
Person
WHERE
id = 1
SELECT
id
FROM
Phone
WHERE
id = 1
UPDATE
Person
SET
name = 'John Doe Sr.'
WHERE
id = 1
UPDATE
Phone
SET
"number" = '(01) 123-456-7890',
owner_id = 1
WHERE
id = 1

View File

@ -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;
import static org.junit.Assert.assertEquals;
/**
* @author Fábio Takeo Ueno
*/
public class CascadeRefreshTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class,
Phone.class
};
}
@Test
public void refreshTest() {
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-refresh-example[]
Person person = entityManager.find( Person.class, 1L );
Phone phone = person.getPhones().get( 0 );
person.setName( "John Doe Jr." );
phone.setNumber( "987-654-3210" );
entityManager.refresh( person );
assertEquals( "John Doe", person.getName() );
assertEquals( "123-456-7890", phone.getNumber() );
//end::pc-cascade-refresh-example[]
} );
}
}

View File

@ -20,7 +20,7 @@ public class CascadeRemoveTest extends BaseEntityManagerFunctionalTestCase {
}
@Test
public void deleteTest() {
public void removeTest() {
doInJPA( this::entityManagerFactory, entityManager -> {
Person person = new Person();
person.setId( 1L );

View File

@ -0,0 +1,55 @@
package org.hibernate.userguide.pc;
import org.hibernate.ReplicationMode;
import org.hibernate.Session;
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 CascadeReplicateTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Person.class,
Phone.class
};
}
@Test
public void refreshTest() {
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-replicate-example[]
Person person = new Person();
person.setId( 1L );
person.setName( "John Doe Sr." );
Phone phone = new Phone();
phone.setId( 1L );
phone.setNumber( "(01) 123-456-7890" );
person.addPhone( phone );
entityManager.unwrap( Session.class ).replicate( person, ReplicationMode.OVERWRITE );
//end::pc-cascade-replicate-example[]
} );
}
}