Skip to content

Commit 385f6d0

Browse files
committed
HHH-11186 - Add examples for all Hibernate annotations
Document @Tuplizer annotation
1 parent 027ca97 commit 385f6d0

File tree

11 files changed

+466
-2
lines changed

11 files changed

+466
-2
lines changed

documentation/src/main/asciidoc/userguide/appendices/Annotations.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1298,7 +1298,7 @@ For entities, the tupelizer must implement the https://docs.jboss.org/hibernate/
12981298

12991299
For embeddables, the tupelizer must implement the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/tuple/component/ComponentTuplizer.html[`ComponentTuplizer`] interface.
13001300

1301-
//TODO: Add example
1301+
See the <<chapters/domain/entity.adoc#entity-tuplizer, `@Tuplizer` mapping>> section for more info.
13021302

13031303
[[annotations-hibernate-tuplizers]]
13041304
==== `@Tuplizers`

documentation/src/main/asciidoc/userguide/chapters/domain/entity.adoc

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
=== Entity types
33
:sourcedir-locking: ../../../../../test/java/org/hibernate/userguide/locking
44
:sourcedir-mapping: ../../../../../test/java/org/hibernate/userguide/mapping/basic
5+
:sourcedir-proxy: ../../../../../test/java/org/hibernate/userguide/proxy
56
:extrasdir: extras
67

78
.Usage of the word _entity_
@@ -312,7 +313,76 @@ include::{sourcedir-mapping}/SubselectTest.java[tag=mapping-Subselect-entity-ref
312313
----
313314
====
314315

315-
The goala of the `@Synchronize` annotation in the `AccountSummary` entity mapping is to instruct Hibernate which database tables are needed by the
316+
The goal of the `@Synchronize` annotation in the `AccountSummary` entity mapping is to instruct Hibernate which database tables are needed by the
316317
underlying `@Subselect` SQL query. This way, when executing a HQL or JPQL which selects from the `AccountSummary` entity,
317318
Hibernate will trigger a Persistence Context flush if there are pending `Account`, `Client` or `AccountTransaction` entity state transitions.
318319

320+
[[entity-tuplizer]]
321+
==== Dynamic entity proxies using the @Tuplizer annotation
322+
323+
It is possible to map your entities as dynamic proxies using
324+
the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/Tuplizer.html[`@Tuplizer`] annotation.
325+
326+
In the following entity mapping, both the embeddable and the entity are mapped as interfaces, not Pojos.
327+
328+
[[entity-tuplizer-entity-mapping]]
329+
.Dynamic entity proxy mapping
330+
====
331+
[source,java]
332+
----
333+
include::{sourcedir-proxy}/tuplizer/Cuisine.java[tag=entity-tuplizer-entity-mapping,indent=0]
334+
----
335+
336+
[source,java]
337+
----
338+
include::{sourcedir-proxy}/tuplizer/Country.java[tag=entity-tuplizer-entity-mapping,indent=0]
339+
----
340+
====
341+
342+
The `@Tuplizer` instructs Hibernate to use the `DynamicEntityTuplizer` and `DynamicEmbeddableTuplizer` to handle
343+
the associated entity and embeddable object types.
344+
345+
Both the `Cuising` entity and the `Country` embeddable types are going to be instantiated as Java dynamic proxies,
346+
as you can see in the following `DynamicInstantiator` example:
347+
348+
[[entity-tuplizer-instantiator]]
349+
.Instantiating entities and embeddables as dynamic proxies
350+
====
351+
[source,java]
352+
----
353+
include::{sourcedir-proxy}/tuplizer/DynamicEntityTuplizer.java[tag=entity-tuplizer-instantiator,indent=0]
354+
----
355+
356+
[source,java]
357+
----
358+
include::{sourcedir-proxy}/tuplizer/DynamicEmbeddableTuplizer.java[tag=entity-tuplizer-instantiator,indent=0]
359+
----
360+
361+
[source,java]
362+
----
363+
include::{sourcedir-proxy}/tuplizer/DynamicInstantiator.java[tag=entity-tuplizer-instantiator,indent=0]
364+
----
365+
366+
[source,java]
367+
----
368+
include::{sourcedir-proxy}/tuplizer/ProxyHelper.java[tag=entity-tuplizer-instantiator,indent=0]
369+
----
370+
371+
[source,java]
372+
----
373+
include::{sourcedir-proxy}/tuplizer/DataProxyHandler.java[tag=entity-tuplizer-instantiator,indent=0]
374+
----
375+
====
376+
377+
With the `DynamicInstantiator` in place, we can work with the dynamic proxy entities just like with Pojo entities.
378+
379+
[[entity-tuplizer-dynamic-proxy-example]]
380+
.Persisting entities and embeddables as dynamic proxies
381+
====
382+
[source,java]
383+
----
384+
include::{sourcedir-proxy}/tuplizer/TuplizerTest.java[tag=entity-tuplizer-dynamic-proxy-example,indent=0]
385+
----
386+
====
387+
388+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
8+
//$Id$
9+
package org.hibernate.userguide.proxy.tuplizer;
10+
import javax.persistence.Column;
11+
import javax.persistence.Embeddable;
12+
13+
/**
14+
* @author Emmanuel Bernard
15+
*/
16+
//tag::entity-tuplizer-entity-mapping[]
17+
18+
@Embeddable
19+
public interface Country {
20+
21+
@Column(name = "CountryName")
22+
String getName();
23+
24+
void setName(String name);
25+
}
26+
//end::entity-tuplizer-entity-mapping[]
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
8+
//$Id$
9+
package org.hibernate.userguide.proxy.tuplizer;
10+
import javax.persistence.Entity;
11+
import javax.persistence.GeneratedValue;
12+
import javax.persistence.Id;
13+
14+
import org.hibernate.annotations.Tuplizer;
15+
16+
/**
17+
* @author Emmanuel Bernard
18+
*/
19+
//tag::entity-tuplizer-entity-mapping[]
20+
@Entity
21+
@Tuplizer(impl = DynamicEntityTuplizer.class)
22+
public interface Cuisine {
23+
24+
@Id
25+
@GeneratedValue
26+
Long getId();
27+
void setId(Long id);
28+
29+
String getName();
30+
void setName(String name);
31+
32+
@Tuplizer(impl = DynamicEmbeddableTuplizer.class)
33+
Country getCountry();
34+
void setCountry(Country country);
35+
}
36+
//end::entity-tuplizer-entity-mapping[]
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
8+
//$Id$
9+
package org.hibernate.userguide.proxy.tuplizer;
10+
import java.io.Serializable;
11+
import java.lang.reflect.InvocationHandler;
12+
import java.lang.reflect.Method;
13+
import java.util.HashMap;
14+
import java.util.Map;
15+
16+
/**
17+
* A simple {@link InvocationHandler} to act as the handler for our generated
18+
* {@link java.lang.reflect.Proxy}-based entity instances.
19+
* <p/>
20+
* This is a trivial impl which simply keeps the property values into
21+
* a Map.
22+
*
23+
* @author <a href="mailto:steve@hibernate.org">Steve Ebersole </a>
24+
*/
25+
//tag::entity-tuplizer-instantiator[]
26+
27+
public final class DataProxyHandler implements InvocationHandler {
28+
29+
private String entityName;
30+
31+
private Map<String, Object> data = new HashMap<>();
32+
33+
public DataProxyHandler(String entityName, Serializable id) {
34+
this.entityName = entityName;
35+
data.put( "Id", id );
36+
}
37+
38+
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
39+
String methodName = method.getName();
40+
if ( methodName.startsWith( "set" ) ) {
41+
String propertyName = methodName.substring( 3 );
42+
data.put( propertyName, args[0] );
43+
}
44+
else if ( methodName.startsWith( "get" ) ) {
45+
String propertyName = methodName.substring( 3 );
46+
return data.get( propertyName );
47+
}
48+
else if ( "toString".equals( methodName ) ) {
49+
return entityName + "#" + data.get( "Id" );
50+
}
51+
else if ( "hashCode".equals( methodName ) ) {
52+
return this.hashCode();
53+
}
54+
return null;
55+
}
56+
57+
public String getEntityName() {
58+
return entityName;
59+
}
60+
}
61+
//end::entity-tuplizer-instantiator[]
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
8+
//$Id$
9+
package org.hibernate.userguide.proxy.tuplizer;
10+
import org.hibernate.mapping.Component;
11+
import org.hibernate.tuple.Instantiator;
12+
import org.hibernate.tuple.component.PojoComponentTuplizer;
13+
14+
/**
15+
* @author Emmanuel Bernard
16+
*/
17+
//tag::entity-tuplizer-instantiator[]
18+
19+
public class DynamicEmbeddableTuplizer
20+
extends PojoComponentTuplizer {
21+
22+
public DynamicEmbeddableTuplizer(Component embeddable) {
23+
super( embeddable );
24+
}
25+
26+
protected Instantiator buildInstantiator(Component embeddable) {
27+
return new DynamicInstantiator(
28+
embeddable.getComponentClassName()
29+
);
30+
}
31+
}
32+
//end::entity-tuplizer-instantiator[]
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.userguide.proxy.tuplizer;
8+
9+
import org.hibernate.mapping.PersistentClass;
10+
import org.hibernate.property.access.spi.Getter;
11+
import org.hibernate.property.access.spi.Setter;
12+
import org.hibernate.proxy.ProxyFactory;
13+
import org.hibernate.tuple.Instantiator;
14+
import org.hibernate.tuple.entity.EntityMetamodel;
15+
import org.hibernate.tuple.entity.PojoEntityTuplizer;
16+
17+
/**
18+
* @author Emmanuel Bernard
19+
*/
20+
//tag::entity-tuplizer-instantiator[]
21+
public class DynamicEntityTuplizer extends PojoEntityTuplizer {
22+
23+
public DynamicEntityTuplizer(
24+
EntityMetamodel entityMetamodel,
25+
PersistentClass mappedEntity) {
26+
super( entityMetamodel, mappedEntity );
27+
}
28+
29+
@Override
30+
protected Instantiator buildInstantiator(
31+
EntityMetamodel entityMetamodel,
32+
PersistentClass persistentClass) {
33+
return new DynamicInstantiator(
34+
persistentClass.getClassName()
35+
);
36+
}
37+
38+
@Override
39+
protected ProxyFactory buildProxyFactory(
40+
PersistentClass persistentClass,
41+
Getter idGetter,
42+
Setter idSetter) {
43+
return super.buildProxyFactory(
44+
persistentClass, idGetter,
45+
idSetter
46+
);
47+
}
48+
}
49+
//end::entity-tuplizer-instantiator[]
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
8+
//$Id$
9+
package org.hibernate.userguide.proxy.tuplizer;
10+
11+
import java.io.Serializable;
12+
13+
import org.hibernate.HibernateException;
14+
import org.hibernate.tuple.Instantiator;
15+
16+
/**
17+
* @author Emmanuel Bernard
18+
*/
19+
//tag::entity-tuplizer-instantiator[]
20+
21+
public class DynamicInstantiator
22+
implements Instantiator {
23+
24+
private final Class targetClass;
25+
26+
public DynamicInstantiator(String targetClassName) {
27+
try {
28+
this.targetClass = Class.forName( targetClassName );
29+
}
30+
catch (ClassNotFoundException e) {
31+
throw new HibernateException( e );
32+
}
33+
}
34+
35+
public Object instantiate(Serializable id) {
36+
return ProxyHelper.newProxy( targetClass, id );
37+
}
38+
39+
public Object instantiate() {
40+
return instantiate( null );
41+
}
42+
43+
public boolean isInstance(Object object) {
44+
try {
45+
return targetClass.isInstance( object );
46+
}
47+
catch( Throwable t ) {
48+
throw new HibernateException(
49+
"could not get handle to entity as interface : " + t
50+
);
51+
}
52+
}
53+
}
54+
//end::entity-tuplizer-instantiator[]
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
8+
//$Id$
9+
package org.hibernate.userguide.proxy.tuplizer;
10+
import org.hibernate.EmptyInterceptor;
11+
12+
/**
13+
* @author Emmanuel Bernard
14+
*/
15+
public class EntityNameInterceptor extends EmptyInterceptor {
16+
/**
17+
* The callback from Hibernate to determine the entity name given
18+
* a presumed entity instance.
19+
*
20+
* @param object The presumed entity instance.
21+
* @return The entity name (pointing to the proper entity mapping).
22+
*/
23+
public String getEntityName(Object object) {
24+
String entityName = ProxyHelper.extractEntityName( object );
25+
if ( entityName == null ) {
26+
entityName = super.getEntityName( object );
27+
}
28+
return entityName;
29+
}
30+
}
31+

0 commit comments

Comments
 (0)