HHH-17818 Add a UG paragraph and examples for `@ConcreteProxy`
This commit is contained in:
parent
84cb94b990
commit
7adab31924
|
@ -462,6 +462,10 @@ https://bytebuddy.net/[Byte Buddy].
|
|||
However, if the entity class is final, a proxy will not be created; you will get a POJO even when you only need a proxy reference.
|
||||
In this case, you could proxy an interface that this particular entity implements, as illustrated by the following example.
|
||||
|
||||
NOTE: Supplying a custom proxy class has been allowed historically, but has never seen much use. Also, setting the `lazy` property to `false`, effectively disallowing proxy creation for an entity type, can easily lead to performance degradation due to the N + 1 query issue. As of 6.2 `@Proxy` has been formally deprecated.
|
||||
|
||||
See the <<concrete-proxy,@ConcreteProxy>> paragraph if you wish to resolve the concrete type of proxies for the purpose of `instanceof` checks and typecasts.
|
||||
|
||||
[[entity-proxy-interface-mapping]]
|
||||
.Final entity class implementing the `Identifiable` interface
|
||||
====
|
||||
|
@ -493,6 +497,62 @@ include::{extrasdir}/entity/entity-proxy-persist-mapping.sql[]
|
|||
As you can see in the associated SQL snippet, Hibernate issues no SQL SELECT query since the proxy can be
|
||||
constructed without needing to fetch the actual entity POJO.
|
||||
|
||||
[[concrete-proxy]]
|
||||
==== Create proxies that resolve their inheritance subtype
|
||||
|
||||
When working with lazy associations or entity references for types that define and inheritance hierarchy Hibernate often creates proxies starting from the root class, with no information about the actual subtype that's referenced by the lazy instance. This can be a problem when using `instanceof` to check the type of said lazy entity references or when trying to cast to the concrete subtype.
|
||||
|
||||
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/ConcreteProxy.html[`@ConcreteProxy`] annotation can be used on an entity hierarchy root mapping to specify that Hibernate should always try to resolve the actual subtype corresponding to the proxy instance created. This effectively means that proxies for that entity hierarchy will always be created from the correct subclass, allowing to preserve laziness and enable using type checks and casts.
|
||||
|
||||
[[entity-concrete-proxy-mapping]]
|
||||
.Root entity class annotated with `@ConcreteProxy`
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{example-dir-proxy}/concrete/AbstractConcreteProxyTest.java[tag=entity-concrete-proxy-mapping,indent=0]
|
||||
----
|
||||
====
|
||||
|
||||
In the following example we load the parent's lazy association and resolve to the concrete `SingleSubChild1` type:
|
||||
|
||||
[[entity-concrete-proxy-find]]
|
||||
.Loading parent entity with lazy association
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{example-dir-proxy}/concrete/AbstractConcreteProxyTest.java[tag=entity-concrete-proxy-find,indent=0]
|
||||
----
|
||||
|
||||
[source,sql]
|
||||
----
|
||||
include::{extrasdir}/entity/entity-concrete-proxy-find.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
IMPORTANT: This added functionality does not come free: in order to determine the concrete type to use when creating the Proxy instance, Hibernate might need to access the association target's table(s) to discover the actual subtype corresponding to a specific identifier value.
|
||||
|
||||
The concrete type will be determined:
|
||||
|
||||
* With *single table* inheritance, the discriminator column value will be left joined when fetching associations or simply read from the entity table when getting references.
|
||||
* When using *joined* inheritance, all subtype tables will need to be left joined to determine the concrete type. Note however that when using an explicit discriminator column, the behavior is the same as for single-table inheritance.
|
||||
* Finally, for *table-per-class* inheritance, all subtype tables will need to be (union) queried to determine the concrete type.
|
||||
|
||||
In the following example, you can see how Hibernate issues a query to resolve the concrete proxy type for an entity reference:
|
||||
|
||||
[[entity-concrete-proxy-reference]]
|
||||
.Resolving the concrete proxy type for `getReference()`:
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
include::{example-dir-proxy}/concrete/AbstractConcreteProxyTest.java[tag=entity-concrete-proxy-reference,indent=0]
|
||||
----
|
||||
|
||||
[source,sql]
|
||||
----
|
||||
include::{extrasdir}/entity/entity-concrete-proxy-reference.sql[]
|
||||
----
|
||||
====
|
||||
|
||||
[[entity-persister]]
|
||||
==== Define a custom entity persister
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
select
|
||||
sp1_0.id,
|
||||
sp1_0.single_id,
|
||||
s1_0.disc_col
|
||||
from
|
||||
SingleParent sp1_0
|
||||
left join
|
||||
SingleBase s1_0
|
||||
on s1_0.id=sp1_0.single_id
|
||||
where
|
||||
sp1_0.id=?
|
||||
|
||||
-- binding parameter (1:BIGINT) <- [1]
|
||||
-- extracted value (2:BIGINT) -> [1]
|
||||
-- extracted value (3:VARCHAR) -> [SingleSubChild1]
|
|
@ -0,0 +1,10 @@
|
|||
select
|
||||
sc1_0.disc_col
|
||||
from
|
||||
SingleBase sc1_0
|
||||
where
|
||||
sc1_0.disc_col in ('SingleChild1', 'SingleSubChild1')
|
||||
and sc1_0.id=?
|
||||
|
||||
-- binding parameter (1:BIGINT) <- [1]
|
||||
-- extracted value (1:VARCHAR) -> [SingleSubChild1]
|
|
@ -41,11 +41,13 @@ public abstract class AbstractConcreteProxyTest extends BaseNonConfigCoreFunctio
|
|||
inspector.clear();
|
||||
// test find and association
|
||||
inSession( session -> {
|
||||
//tag::entity-concrete-proxy-find[]
|
||||
final SingleParent parent1 = session.find( SingleParent.class, 1L );
|
||||
assertThat( parent1.getSingle(), instanceOf( SingleSubChild1.class ) );
|
||||
assertThat( Hibernate.isInitialized( parent1.getSingle() ), is( false ) );
|
||||
final SingleSubChild1 proxy = (SingleSubChild1) parent1.getSingle();
|
||||
assertThat( Hibernate.isInitialized( proxy ), is( false ) );
|
||||
//end::entity-concrete-proxy-find[]
|
||||
inspector.assertExecutedCount( 1 );
|
||||
inspector.assertNumberOfJoins( 0, SqlAstJoinType.LEFT, 1 );
|
||||
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "disc_col", 1 );
|
||||
|
@ -68,9 +70,13 @@ public abstract class AbstractConcreteProxyTest extends BaseNonConfigCoreFunctio
|
|||
inspector.clear();
|
||||
// test get reference
|
||||
inSession( session -> {
|
||||
//tag::entity-concrete-proxy-reference[]
|
||||
final SingleChild1 proxy1 = session.getReference( SingleChild1.class, 1L );
|
||||
assertThat( proxy1, instanceOf( SingleSubChild1.class ) );
|
||||
assertThat( Hibernate.isInitialized( proxy1 ), is( false ) );
|
||||
final SingleSubChild1 subChild1 = (SingleSubChild1) proxy1;
|
||||
assertThat( Hibernate.isInitialized( subChild1 ), is( false ) );
|
||||
//end::entity-concrete-proxy-reference[]
|
||||
inspector.assertExecutedCount( 1 );
|
||||
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "disc_col", 2 );
|
||||
inspector.clear();
|
||||
|
@ -286,6 +292,9 @@ public abstract class AbstractConcreteProxyTest extends BaseNonConfigCoreFunctio
|
|||
return (SQLStatementInspector) sessionFactory().getSessionFactoryOptions().getStatementInspector();
|
||||
}
|
||||
|
||||
// InheritanceType.SINGLE_TABLE
|
||||
|
||||
//tag::entity-concrete-proxy-mapping[]
|
||||
@Entity( name = "SingleParent" )
|
||||
public static class SingleParent {
|
||||
@Id
|
||||
|
@ -307,8 +316,6 @@ public abstract class AbstractConcreteProxyTest extends BaseNonConfigCoreFunctio
|
|||
}
|
||||
}
|
||||
|
||||
// InheritanceType.SINGLE_TABLE
|
||||
|
||||
@Entity( name = "SingleBase" )
|
||||
@Inheritance( strategy = InheritanceType.SINGLE_TABLE )
|
||||
@DiscriminatorColumn( name = "disc_col" )
|
||||
|
@ -351,6 +358,9 @@ public abstract class AbstractConcreteProxyTest extends BaseNonConfigCoreFunctio
|
|||
}
|
||||
}
|
||||
|
||||
// Other subtypes omitted for brevity
|
||||
//end::entity-concrete-proxy-mapping[]
|
||||
|
||||
@Entity( name = "SingleChild2" )
|
||||
public static class SingleChild2 extends SingleBase {
|
||||
private Integer child2Prop;
|
||||
|
|
Loading…
Reference in New Issue