HHH-11186 - Add examples for all Hibernate annotations
Document @Tuplizer annotation
This commit is contained in:
parent
027ca97090
commit
385f6d056c
|
@ -1298,7 +1298,7 @@ For entities, the tupelizer must implement the https://docs.jboss.org/hibernate/
|
|||
|
||||
For embeddables, the tupelizer must implement the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/tuple/component/ComponentTuplizer.html[`ComponentTuplizer`] interface.
|
||||
|
||||
//TODO: Add example
|
||||
See the <<chapters/domain/entity.adoc#entity-tuplizer, `@Tuplizer` mapping>> section for more info.
|
||||
|
||||
[[annotations-hibernate-tuplizers]]
|
||||
==== `@Tuplizers`
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
=== Entity types
|
||||
:sourcedir-locking: ../../../../../test/java/org/hibernate/userguide/locking
|
||||
:sourcedir-mapping: ../../../../../test/java/org/hibernate/userguide/mapping/basic
|
||||
:sourcedir-proxy: ../../../../../test/java/org/hibernate/userguide/proxy
|
||||
:extrasdir: extras
|
||||
|
||||
.Usage of the word _entity_
|
||||
|
@ -312,7 +313,76 @@ include::{sourcedir-mapping}/SubselectTest.java[tag=mapping-Subselect-entity-ref
|
|||
----
|
||||
====
|
||||
|
||||
The goala of the `@Synchronize` annotation in the `AccountSummary` entity mapping is to instruct Hibernate which database tables are needed by the
|
||||
The goal of the `@Synchronize` annotation in the `AccountSummary` entity mapping is to instruct Hibernate which database tables are needed by the
|
||||
underlying `@Subselect` SQL query. This way, when executing a HQL or JPQL which selects from the `AccountSummary` entity,
|
||||
Hibernate will trigger a Persistence Context flush if there are pending `Account`, `Client` or `AccountTransaction` entity state transitions.
|
||||
|
||||
[[entity-tuplizer]]
|
||||
==== Dynamic entity proxies using the @Tuplizer annotation
|
||||
|
||||
It is possible to map your entities as dynamic proxies using
|
||||
the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/Tuplizer.html[`@Tuplizer`] annotation.
|
||||
|
||||
In the following entity mapping, both the embeddable and the entity are mapped as interfaces, not Pojos.
|
||||
|
||||
[[entity-tuplizer-entity-mapping]]
|
||||
.Dynamic entity proxy mapping
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir-proxy}/tuplizer/Cuisine.java[tag=entity-tuplizer-entity-mapping,indent=0]
|
||||
----
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir-proxy}/tuplizer/Country.java[tag=entity-tuplizer-entity-mapping,indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
The `@Tuplizer` instructs Hibernate to use the `DynamicEntityTuplizer` and `DynamicEmbeddableTuplizer` to handle
|
||||
the associated entity and embeddable object types.
|
||||
|
||||
Both the `Cuising` entity and the `Country` embeddable types are going to be instantiated as Java dynamic proxies,
|
||||
as you can see in the following `DynamicInstantiator` example:
|
||||
|
||||
[[entity-tuplizer-instantiator]]
|
||||
.Instantiating entities and embeddables as dynamic proxies
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir-proxy}/tuplizer/DynamicEntityTuplizer.java[tag=entity-tuplizer-instantiator,indent=0]
|
||||
----
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir-proxy}/tuplizer/DynamicEmbeddableTuplizer.java[tag=entity-tuplizer-instantiator,indent=0]
|
||||
----
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir-proxy}/tuplizer/DynamicInstantiator.java[tag=entity-tuplizer-instantiator,indent=0]
|
||||
----
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir-proxy}/tuplizer/ProxyHelper.java[tag=entity-tuplizer-instantiator,indent=0]
|
||||
----
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir-proxy}/tuplizer/DataProxyHandler.java[tag=entity-tuplizer-instantiator,indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
With the `DynamicInstantiator` in place, we can work with the dynamic proxy entities just like with Pojo entities.
|
||||
|
||||
[[entity-tuplizer-dynamic-proxy-example]]
|
||||
.Persisting entities and embeddables as dynamic proxies
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{sourcedir-proxy}/tuplizer/TuplizerTest.java[tag=entity-tuplizer-dynamic-proxy-example,indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
//$Id$
|
||||
package org.hibernate.userguide.proxy.tuplizer;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
//tag::entity-tuplizer-entity-mapping[]
|
||||
|
||||
@Embeddable
|
||||
public interface Country {
|
||||
|
||||
@Column(name = "CountryName")
|
||||
String getName();
|
||||
|
||||
void setName(String name);
|
||||
}
|
||||
//end::entity-tuplizer-entity-mapping[]
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
//$Id$
|
||||
package org.hibernate.userguide.proxy.tuplizer;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.annotations.Tuplizer;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
//tag::entity-tuplizer-entity-mapping[]
|
||||
@Entity
|
||||
@Tuplizer(impl = DynamicEntityTuplizer.class)
|
||||
public interface Cuisine {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
Long getId();
|
||||
void setId(Long id);
|
||||
|
||||
String getName();
|
||||
void setName(String name);
|
||||
|
||||
@Tuplizer(impl = DynamicEmbeddableTuplizer.class)
|
||||
Country getCountry();
|
||||
void setCountry(Country country);
|
||||
}
|
||||
//end::entity-tuplizer-entity-mapping[]
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
//$Id$
|
||||
package org.hibernate.userguide.proxy.tuplizer;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A simple {@link InvocationHandler} to act as the handler for our generated
|
||||
* {@link java.lang.reflect.Proxy}-based entity instances.
|
||||
* <p/>
|
||||
* This is a trivial impl which simply keeps the property values into
|
||||
* a Map.
|
||||
*
|
||||
* @author <a href="mailto:steve@hibernate.org">Steve Ebersole </a>
|
||||
*/
|
||||
//tag::entity-tuplizer-instantiator[]
|
||||
|
||||
public final class DataProxyHandler implements InvocationHandler {
|
||||
|
||||
private String entityName;
|
||||
|
||||
private Map<String, Object> data = new HashMap<>();
|
||||
|
||||
public DataProxyHandler(String entityName, Serializable id) {
|
||||
this.entityName = entityName;
|
||||
data.put( "Id", id );
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
String methodName = method.getName();
|
||||
if ( methodName.startsWith( "set" ) ) {
|
||||
String propertyName = methodName.substring( 3 );
|
||||
data.put( propertyName, args[0] );
|
||||
}
|
||||
else if ( methodName.startsWith( "get" ) ) {
|
||||
String propertyName = methodName.substring( 3 );
|
||||
return data.get( propertyName );
|
||||
}
|
||||
else if ( "toString".equals( methodName ) ) {
|
||||
return entityName + "#" + data.get( "Id" );
|
||||
}
|
||||
else if ( "hashCode".equals( methodName ) ) {
|
||||
return this.hashCode();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getEntityName() {
|
||||
return entityName;
|
||||
}
|
||||
}
|
||||
//end::entity-tuplizer-instantiator[]
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
//$Id$
|
||||
package org.hibernate.userguide.proxy.tuplizer;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.tuple.Instantiator;
|
||||
import org.hibernate.tuple.component.PojoComponentTuplizer;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
//tag::entity-tuplizer-instantiator[]
|
||||
|
||||
public class DynamicEmbeddableTuplizer
|
||||
extends PojoComponentTuplizer {
|
||||
|
||||
public DynamicEmbeddableTuplizer(Component embeddable) {
|
||||
super( embeddable );
|
||||
}
|
||||
|
||||
protected Instantiator buildInstantiator(Component embeddable) {
|
||||
return new DynamicInstantiator(
|
||||
embeddable.getComponentClassName()
|
||||
);
|
||||
}
|
||||
}
|
||||
//end::entity-tuplizer-instantiator[]
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.userguide.proxy.tuplizer;
|
||||
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.property.access.spi.Getter;
|
||||
import org.hibernate.property.access.spi.Setter;
|
||||
import org.hibernate.proxy.ProxyFactory;
|
||||
import org.hibernate.tuple.Instantiator;
|
||||
import org.hibernate.tuple.entity.EntityMetamodel;
|
||||
import org.hibernate.tuple.entity.PojoEntityTuplizer;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
//tag::entity-tuplizer-instantiator[]
|
||||
public class DynamicEntityTuplizer extends PojoEntityTuplizer {
|
||||
|
||||
public DynamicEntityTuplizer(
|
||||
EntityMetamodel entityMetamodel,
|
||||
PersistentClass mappedEntity) {
|
||||
super( entityMetamodel, mappedEntity );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Instantiator buildInstantiator(
|
||||
EntityMetamodel entityMetamodel,
|
||||
PersistentClass persistentClass) {
|
||||
return new DynamicInstantiator(
|
||||
persistentClass.getClassName()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProxyFactory buildProxyFactory(
|
||||
PersistentClass persistentClass,
|
||||
Getter idGetter,
|
||||
Setter idSetter) {
|
||||
return super.buildProxyFactory(
|
||||
persistentClass, idGetter,
|
||||
idSetter
|
||||
);
|
||||
}
|
||||
}
|
||||
//end::entity-tuplizer-instantiator[]
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
//$Id$
|
||||
package org.hibernate.userguide.proxy.tuplizer;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.tuple.Instantiator;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
//tag::entity-tuplizer-instantiator[]
|
||||
|
||||
public class DynamicInstantiator
|
||||
implements Instantiator {
|
||||
|
||||
private final Class targetClass;
|
||||
|
||||
public DynamicInstantiator(String targetClassName) {
|
||||
try {
|
||||
this.targetClass = Class.forName( targetClassName );
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
throw new HibernateException( e );
|
||||
}
|
||||
}
|
||||
|
||||
public Object instantiate(Serializable id) {
|
||||
return ProxyHelper.newProxy( targetClass, id );
|
||||
}
|
||||
|
||||
public Object instantiate() {
|
||||
return instantiate( null );
|
||||
}
|
||||
|
||||
public boolean isInstance(Object object) {
|
||||
try {
|
||||
return targetClass.isInstance( object );
|
||||
}
|
||||
catch( Throwable t ) {
|
||||
throw new HibernateException(
|
||||
"could not get handle to entity as interface : " + t
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
//end::entity-tuplizer-instantiator[]
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
//$Id$
|
||||
package org.hibernate.userguide.proxy.tuplizer;
|
||||
import org.hibernate.EmptyInterceptor;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
public class EntityNameInterceptor extends EmptyInterceptor {
|
||||
/**
|
||||
* The callback from Hibernate to determine the entity name given
|
||||
* a presumed entity instance.
|
||||
*
|
||||
* @param object The presumed entity instance.
|
||||
* @return The entity name (pointing to the proper entity mapping).
|
||||
*/
|
||||
public String getEntityName(Object object) {
|
||||
String entityName = ProxyHelper.extractEntityName( object );
|
||||
if ( entityName == null ) {
|
||||
entityName = super.getEntityName( object );
|
||||
}
|
||||
return entityName;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
//$Id$
|
||||
package org.hibernate.userguide.proxy.tuplizer;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
//tag::entity-tuplizer-instantiator[]
|
||||
|
||||
public class ProxyHelper {
|
||||
|
||||
public static <T> T newProxy(Class<T> targetClass, Serializable id) {
|
||||
return ( T ) Proxy.newProxyInstance(
|
||||
targetClass.getClassLoader(),
|
||||
new Class[] {
|
||||
targetClass
|
||||
},
|
||||
new DataProxyHandler(
|
||||
targetClass.getName(),
|
||||
id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static String extractEntityName(Object object) {
|
||||
if ( Proxy.isProxyClass( object.getClass() ) ) {
|
||||
InvocationHandler handler = Proxy.getInvocationHandler(
|
||||
object
|
||||
);
|
||||
if ( DataProxyHandler.class.isAssignableFrom( handler.getClass() ) ) {
|
||||
DataProxyHandler myHandler = (DataProxyHandler) handler;
|
||||
return myHandler.getEntityName();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//end::entity-tuplizer-instantiator[]
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.userguide.proxy.tuplizer;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernateSessionBuilder;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
public class TuplizerTest extends BaseCoreFunctionalTestCase {
|
||||
@Test
|
||||
public void testEntityTuplizer() throws Exception {
|
||||
//tag::entity-tuplizer-dynamic-proxy-example[]
|
||||
Cuisine _cuisine = doInHibernateSessionBuilder(
|
||||
() -> sessionFactory()
|
||||
.withOptions()
|
||||
.interceptor( new EntityNameInterceptor() ),
|
||||
session -> {
|
||||
Cuisine cuisine = ProxyHelper.newProxy( Cuisine.class, null );
|
||||
cuisine.setName( "Française" );
|
||||
|
||||
Country country = ProxyHelper.newProxy( Country.class, null );
|
||||
country.setName( "France" );
|
||||
|
||||
cuisine.setCountry( country );
|
||||
session.persist( cuisine );
|
||||
|
||||
return cuisine;
|
||||
} );
|
||||
|
||||
doInHibernateSessionBuilder(
|
||||
() -> sessionFactory()
|
||||
.withOptions()
|
||||
.interceptor( new EntityNameInterceptor() ),
|
||||
session -> {
|
||||
Cuisine cuisine = session.get( Cuisine.class, _cuisine.getId() );
|
||||
|
||||
assertEquals( "Française", cuisine.getName() );
|
||||
assertEquals( "France", cuisine.getCountry().getName() );
|
||||
} );
|
||||
//end::entity-tuplizer-dynamic-proxy-example[]
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] { Cuisine.class };
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue