From ce19b83e4e96618a4219cc9b3afd08da311da806 Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Mon, 14 Oct 2019 23:32:16 -0700 Subject: [PATCH] HHH-13633 : Add tests including for no HibernateProxy factory (cherry picked from commit 5aa421d6978140f4a1e5225c16bfed65f386c6f1) --- ...oxyFactoryWithSubclassesStatelessTest.java | 476 ++++++++++++++++++ ...oOnesProxyWithSubclassesStatelessTest.java | 332 ++++++++++++ 2 files changed, 808 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesNoProxyFactoryWithSubclassesStatelessTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesStatelessTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesNoProxyFactoryWithSubclassesStatelessTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesNoProxyFactoryWithSubclassesStatelessTest.java new file mode 100644 index 0000000000..9ba59657e3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesNoProxyFactoryWithSubclassesStatelessTest.java @@ -0,0 +1,476 @@ +/* + * 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.test.bytecode.enhancement.lazy.proxy; + +import java.io.Serializable; +import java.util.Map; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.hibernate.EntityMode; +import org.hibernate.EntityNameResolver; +import org.hibernate.Hibernate; +import org.hibernate.HibernateException; +import org.hibernate.annotations.LazyToOne; +import org.hibernate.annotations.LazyToOneOption; +import org.hibernate.annotations.Tuplizer; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.SessionFactoryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.property.access.spi.Getter; +import org.hibernate.proxy.HibernateProxy; +import org.hibernate.proxy.ProxyFactory; +import org.hibernate.stat.Statistics; +import org.hibernate.tuple.entity.EntityMetamodel; +import org.hibernate.tuple.entity.EntityTuplizer; +import org.hibernate.tuple.entity.PojoEntityTuplizer; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * @author Gail Badner + */ +@TestForIssue( jiraKey = "HHH-13640" ) +@RunWith(BytecodeEnhancerRunner.class) +public class LazyToOnesNoProxyFactoryWithSubclassesStatelessTest extends BaseNonConfigCoreFunctionalTestCase { + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" ); + } + + @Override + protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) { + super.configureSessionFactoryBuilder( sfb ); + sfb.applyStatisticsSupport( true ); + sfb.applySecondLevelCacheSupport( false ); + sfb.applyQueryCacheSupport( false ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + super.applyMetadataSources( sources ); + sources.addAnnotatedClass( Animal.class ); + sources.addAnnotatedClass( Primate.class ); + sources.addAnnotatedClass( Human.class ); + sources.addAnnotatedClass( OtherEntity.class ); + } + + @Test + public void testNewEnhancedProxyAssociation() { + inStatelessTransaction( + session -> { + Human human = new Human( "A Human" ); + OtherEntity otherEntity = new OtherEntity( "test1" ); + otherEntity.human = human; + + session.insert( human ); + session.insert( otherEntity ); + } + ); + + inStatelessSession( + session -> { + final Statistics stats = sessionFactory().getStatistics(); + stats.clear(); + final OtherEntity otherEntity = (OtherEntity) session.get( OtherEntity.class, "test1" ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "human" ) ); + assertFalse( Hibernate.isInitialized( otherEntity.human ) ); + assertFalse( HibernateProxy.class.isInstance( otherEntity.animal ) ); + assertEquals( 1, stats.getPrepareStatementCount() ); + } + ); + } + + @Test + public void testExistingInitializedAssociationLeafSubclass() { + inStatelessSession( + session -> { + Human human = new Human( "A Human" ); + OtherEntity otherEntity = new OtherEntity( "test1" ); + otherEntity.animal = human; + otherEntity.primate = human; + otherEntity.human = human; + session.insert( human ); + session.insert( otherEntity ); + } + ); + + final Statistics stats = sessionFactory().getStatistics(); + stats.clear(); + + inStatelessSession( + session -> { + + final OtherEntity otherEntity = (OtherEntity) session.get( OtherEntity.class, "test1" ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "animal" ) ); + assertTrue( Hibernate.isInitialized( otherEntity.animal ) ); + assertFalse( HibernateProxy.class.isInstance( otherEntity.animal ) ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "primate" ) ); + assertTrue( Hibernate.isInitialized( otherEntity.primate ) ); + assertFalse( HibernateProxy.class.isInstance( otherEntity.primate ) ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "human" ) ); + assertTrue( Hibernate.isInitialized( otherEntity.human ) ); + assertFalse( HibernateProxy.class.isInstance( otherEntity.human ) ); + assertSame( otherEntity.human, otherEntity.animal ); + assertSame( otherEntity.human, otherEntity.primate ); + assertEquals( 2, stats.getPrepareStatementCount() ); + } + ); + + assertEquals( 2, stats.getPrepareStatementCount() ); + } + + @Test + public void testExistingEnhancedProxyAssociationLeafSubclassOnly() { + inStatelessSession( + session -> { + Human human = new Human( "A Human" ); + OtherEntity otherEntity = new OtherEntity( "test1" ); + otherEntity.human = human; + otherEntity.otherHuman = human; + session.insert( human ); + session.insert( otherEntity ); + } + ); + + inStatelessSession( + session -> { + final Statistics stats = sessionFactory().getStatistics(); + stats.clear(); + + final OtherEntity otherEntity = (OtherEntity) session.get( OtherEntity.class, "test1" ); + assertNull( otherEntity.animal ); + assertNull( otherEntity.primate ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "human" ) ); + assertFalse( Hibernate.isInitialized( otherEntity.human ) ); + assertFalse( HibernateProxy.class.isInstance( otherEntity.human ) ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "otherHuman" ) ); + assertFalse( Hibernate.isInitialized( otherEntity.otherHuman ) ); + assertFalse( HibernateProxy.class.isInstance( otherEntity.otherHuman ) ); + assertSame( otherEntity.human, otherEntity.otherHuman ); + assertEquals( 1, stats.getPrepareStatementCount() ); + } + ); + } + + @After + public void cleanUpTestData() { + inTransaction( + session -> { + session.createQuery( "delete from OtherEntity" ).executeUpdate(); + session.createQuery( "delete from Human" ).executeUpdate(); + session.createQuery( "delete from Primate" ).executeUpdate(); + session.createQuery( "delete from Animal" ).executeUpdate(); + } + ); + } + + @Entity(name = "Animal") + @Table(name = "Animal") + @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) + @Tuplizer(impl=NoProxyFactoryPojoEntityTuplizer.class) + public static abstract class Animal { + + @Id + private String name; + + private int age; + + public String getName() { + return name; + } + + protected void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + } + + @Entity(name = "Primate") + @Table(name = "Primate") + public static class Primate extends Animal { + + public Primate(String name) { + this(); + setName( name ); + } + + protected Primate() { + // this form used by Hibernate + } + } + + @Entity(name = "Human") + @Table(name = "Human") + public static class Human extends Primate { + + private String sex; + + public Human(String name) { + this(); + setName( name ); + } + + protected Human() { + // this form used by Hibernate + } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex; + } + } + + @Entity(name = "OtherEntity") + @Table(name = "OtherEntity") + public static class OtherEntity { + + @Id + private String id; + + @ManyToOne(fetch = FetchType.LAZY) + @LazyToOne(LazyToOneOption.NO_PROXY) + private Animal animal = null; + + @ManyToOne(fetch = FetchType.LAZY) + @LazyToOne(LazyToOneOption.NO_PROXY) + private Primate primate = null; + + @ManyToOne(fetch = FetchType.LAZY) + @LazyToOne(LazyToOneOption.NO_PROXY) + private Human human = null; + + @ManyToOne(fetch = FetchType.LAZY) + @LazyToOne(LazyToOneOption.NO_PROXY) + private Human otherHuman = null; + + protected OtherEntity() { + // this form used by Hibernate + } + + public OtherEntity(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public Human getHuman() { + return human; + } + + public void setHuman(Human human) { + this.human = human; + } + } + + public static class NoProxyFactoryPojoEntityTuplizer implements EntityTuplizer { + + private final PojoEntityTuplizer pojoEntityTuplizer; + + public NoProxyFactoryPojoEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) { + pojoEntityTuplizer = new PojoEntityTuplizer( entityMetamodel, mappedEntity ); + } + + @Override + public EntityMode getEntityMode() { + return pojoEntityTuplizer.getEntityMode(); + } + + @Override + public Object instantiate(Serializable id) throws HibernateException { + return pojoEntityTuplizer.instantiate( id ); + } + + @Override + public Object instantiate(Serializable id, SharedSessionContractImplementor session) { + return pojoEntityTuplizer.instantiate( id, session ); + + } + + @Override + public Serializable getIdentifier(Object entity) throws HibernateException { + return pojoEntityTuplizer.getIdentifier( entity ); + } + + @Override + public Serializable getIdentifier(Object entity, SharedSessionContractImplementor session) { + return pojoEntityTuplizer.getIdentifier( entity, session ); + } + + @Override + public void setIdentifier(Object entity, Serializable id) throws HibernateException { + pojoEntityTuplizer.setIdentifier( entity, id ); + } + + @Override + public void setIdentifier(Object entity, Serializable id, SharedSessionContractImplementor session) { + pojoEntityTuplizer.setIdentifier( entity, id, session ); + } + + @Override + public void resetIdentifier(Object entity, Serializable currentId, Object currentVersion) { + pojoEntityTuplizer.resetIdentifier( entity, currentId, currentVersion ); + } + + @Override + public void resetIdentifier( + Object entity, + Serializable currentId, + Object currentVersion, + SharedSessionContractImplementor session) { + pojoEntityTuplizer.resetIdentifier( entity, currentId, currentVersion, session ); + } + + @Override + public Object getVersion(Object entity) throws HibernateException { + return pojoEntityTuplizer.getVersion( entity ); + } + + @Override + public void setPropertyValue(Object entity, int i, Object value) throws HibernateException { + pojoEntityTuplizer. setPropertyValue( entity, i, value ); + } + + @Override + public void setPropertyValue(Object entity, String propertyName, Object value) throws HibernateException { + pojoEntityTuplizer.setPropertyValue( entity, propertyName, value ); + } + + @Override + public Object[] getPropertyValuesToInsert( + Object entity, + Map mergeMap, + SharedSessionContractImplementor session) throws HibernateException { + return pojoEntityTuplizer.getPropertyValuesToInsert( entity, mergeMap, session ); + } + + @Override + public Object getPropertyValue(Object entity, String propertyName) throws HibernateException { + return pojoEntityTuplizer.getPropertyValue( entity, propertyName ); + } + + @Override + public void afterInitialize(Object entity, SharedSessionContractImplementor session) { + pojoEntityTuplizer.afterInitialize( entity, session ); + + } + + @Override + public boolean hasProxy() { + return pojoEntityTuplizer.hasProxy(); + } + + @Override + public Object createProxy(Serializable id, SharedSessionContractImplementor session) throws HibernateException { + return pojoEntityTuplizer.createProxy( id, session ); + } + + @Override + public boolean isLifecycleImplementor() { + return pojoEntityTuplizer.isLifecycleImplementor(); + } + + @Override + public Class getConcreteProxyClass() { + return pojoEntityTuplizer.getConcreteProxyClass(); + } + + @Override + public EntityNameResolver[] getEntityNameResolvers() { + return pojoEntityTuplizer.getEntityNameResolvers(); + } + + @Override + public String determineConcreteSubclassEntityName( + Object entityInstance, SessionFactoryImplementor factory) { + return pojoEntityTuplizer.determineConcreteSubclassEntityName( entityInstance, factory ); + } + + @Override + public Getter getIdentifierGetter() { + return pojoEntityTuplizer.getIdentifierGetter(); + } + + @Override + public Getter getVersionGetter() { + return pojoEntityTuplizer.getVersionGetter(); + } + + @Override + public ProxyFactory getProxyFactory() { + return null; + } + + @Override + public Object[] getPropertyValues(Object entity) { + return pojoEntityTuplizer.getPropertyValues( entity ); + } + + @Override + public void setPropertyValues(Object entity, Object[] values) { + pojoEntityTuplizer.setPropertyValues( entity, values ); + } + + @Override + public Object getPropertyValue(Object entity, int i) { + return pojoEntityTuplizer.getPropertyValue( entity, i ); + } + + @Override + public Object instantiate() { + return pojoEntityTuplizer.instantiate(); + } + + @Override + public boolean isInstance(Object object) { + return pojoEntityTuplizer.isInstance( object ); + } + + @Override + public Class getMappedClass() { + return pojoEntityTuplizer.getMappedClass(); + } + + @Override + public Getter getGetter(int i) { + return pojoEntityTuplizer.getGetter( i ); + } + } +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesStatelessTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesStatelessTest.java new file mode 100644 index 0000000000..28b03e2301 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/proxy/LazyToOnesProxyWithSubclassesStatelessTest.java @@ -0,0 +1,332 @@ +/* + * 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.test.bytecode.enhancement.lazy.proxy; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import org.hibernate.Hibernate; +import org.hibernate.annotations.LazyToOne; +import org.hibernate.annotations.LazyToOneOption; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.SessionFactoryBuilder; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.proxy.HibernateProxy; +import org.hibernate.stat.Statistics; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * @author Gail Badner + */ +@TestForIssue( jiraKey = "HHH-13640" ) +@RunWith(BytecodeEnhancerRunner.class) +public class LazyToOnesProxyWithSubclassesStatelessTest extends BaseNonConfigCoreFunctionalTestCase { + @Override + protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) { + super.configureStandardServiceRegistryBuilder( ssrb ); + ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" ); + } + + @Override + protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) { + super.configureSessionFactoryBuilder( sfb ); + sfb.applyStatisticsSupport( true ); + sfb.applySecondLevelCacheSupport( false ); + sfb.applyQueryCacheSupport( false ); + } + + @Override + protected void applyMetadataSources(MetadataSources sources) { + super.applyMetadataSources( sources ); + sources.addAnnotatedClass( Animal.class ); + sources.addAnnotatedClass( Primate.class ); + sources.addAnnotatedClass( Human.class ); + sources.addAnnotatedClass( OtherEntity.class ); + } + + @Test + public void testNewHibernateProxyAssociation() { + inStatelessTransaction( + session -> { + Human human = new Human( "A Human" ); + OtherEntity otherEntity = new OtherEntity( "test1" ); + otherEntity.animal = human; + session.insert( human ); + session.insert( otherEntity ); + } + ); + + inStatelessSession( + session -> { + final Statistics stats = sessionFactory().getStatistics(); + stats.clear(); + final OtherEntity otherEntity = (OtherEntity) session.get( OtherEntity.class, "test1" ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "animal" ) ); + assertFalse( Hibernate.isInitialized( otherEntity.animal ) ); + assertTrue( HibernateProxy.class.isInstance( otherEntity.animal ) ); + + assertEquals( 1, stats.getPrepareStatementCount() ); + } + ); + } + + @Test + public void testNewEnhancedProxyAssociation() { + inStatelessTransaction( + session -> { + Human human = new Human( "A Human" ); + OtherEntity otherEntity = new OtherEntity( "test1" ); + otherEntity.human = human; + + session.insert( human ); + session.insert( otherEntity ); + } + ); + + inStatelessSession( + session -> { + final Statistics stats = sessionFactory().getStatistics(); + stats.clear(); + final OtherEntity otherEntity = (OtherEntity) session.get( OtherEntity.class, "test1" ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "human" ) ); + assertFalse( Hibernate.isInitialized( otherEntity.human ) ); + assertFalse( HibernateProxy.class.isInstance( otherEntity.animal ) ); + assertEquals( 1, stats.getPrepareStatementCount() ); + } + ); + + } + + @Test + public void testExistingProxyAssociation() { + inStatelessTransaction( + session -> { + Human human = new Human( "A Human" ); + OtherEntity otherEntity = new OtherEntity( "test1" ); + otherEntity.animal = human; + otherEntity.primate = human; + session.insert( human ); + session.insert( otherEntity ); + } + ); + + inStatelessSession( + session -> { + final Statistics stats = sessionFactory().getStatistics(); + stats.clear(); + final OtherEntity otherEntity = (OtherEntity) session.get( OtherEntity.class, "test1" ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "animal" ) ); + assertFalse( Hibernate.isInitialized( otherEntity.animal ) ); + assertTrue( HibernateProxy.class.isInstance( otherEntity.animal ) ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "primate" ) ); + assertFalse( Hibernate.isInitialized( otherEntity.primate ) ); + assertTrue( HibernateProxy.class.isInstance( otherEntity.primate ) ); + assertEquals( 1, stats.getPrepareStatementCount() ); + } + ); + } + + @Test + public void testExistingHibernateProxyAssociationLeafSubclass() { + inStatelessSession( + session -> { + Human human = new Human( "A Human" ); + OtherEntity otherEntity = new OtherEntity( "test1" ); + otherEntity.animal = human; + otherEntity.primate = human; + otherEntity.human = human; + session.insert( human ); + session.insert( otherEntity ); + } + ); + + final Statistics stats = sessionFactory().getStatistics(); + stats.clear(); + + inStatelessSession( + session -> { + + final OtherEntity otherEntity = (OtherEntity) session.get( OtherEntity.class, "test1" ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "animal" ) ); + assertFalse( Hibernate.isInitialized( otherEntity.animal ) ); + assertTrue( HibernateProxy.class.isInstance( otherEntity.animal ) ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "primate" ) ); + assertFalse( Hibernate.isInitialized( otherEntity.primate ) ); + assertTrue( HibernateProxy.class.isInstance( otherEntity.primate ) ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "human" ) ); + assertFalse( Hibernate.isInitialized( otherEntity.human ) ); + + assertEquals( 1, stats.getPrepareStatementCount() ); + } + ); + + assertEquals( 1, stats.getPrepareStatementCount() ); + } + + @Test + public void testExistingEnhancedProxyAssociationLeafSubclassOnly() { + inStatelessSession( + session -> { + Human human = new Human( "A Human" ); + OtherEntity otherEntity = new OtherEntity( "test1" ); + otherEntity.human = human; + session.insert( human ); + session.insert( otherEntity ); + } + ); + + inStatelessSession( + session -> { + final Statistics stats = sessionFactory().getStatistics(); + stats.clear(); + + final OtherEntity otherEntity = (OtherEntity) session.get( OtherEntity.class, "test1" ); + assertNull( otherEntity.animal ); + assertNull( otherEntity.primate ); + assertTrue( Hibernate.isPropertyInitialized( otherEntity, "human" ) ); + assertFalse( Hibernate.isInitialized( otherEntity.human ) ); + assertFalse( HibernateProxy.class.isInstance( otherEntity.human ) ); + assertEquals( 1, stats.getPrepareStatementCount() ); + } + ); + } + + @After + public void cleanUpTestData() { + inTransaction( + session -> { + session.createQuery( "delete from OtherEntity" ).executeUpdate(); + session.createQuery( "delete from Human" ).executeUpdate(); + session.createQuery( "delete from Primate" ).executeUpdate(); + session.createQuery( "delete from Animal" ).executeUpdate(); + } + ); + } + + @Entity(name = "Animal") + @Table(name = "Animal") + @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) + public static abstract class Animal { + + @Id + private String name; + + private int age; + + public String getName() { + return name; + } + + protected void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + } + + @Entity(name = "Primate") + @Table(name = "Primate") + public static class Primate extends Animal { + + public Primate(String name) { + this(); + setName( name ); + } + + protected Primate() { + // this form used by Hibernate + } + } + + @Entity(name = "Human") + @Table(name = "Human") + public static class Human extends Primate { + + private String sex; + + public Human(String name) { + this(); + setName( name ); + } + + protected Human() { + // this form used by Hibernate + } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex; + } + } + + @Entity(name = "OtherEntity") + @Table(name = "OtherEntity") + public static class OtherEntity { + + @Id + private String id; + + @ManyToOne(fetch = FetchType.LAZY) + @LazyToOne(LazyToOneOption.NO_PROXY) + private Animal animal = null; + + @ManyToOne(fetch = FetchType.LAZY) + @LazyToOne(LazyToOneOption.NO_PROXY) + private Primate primate = null; + + @ManyToOne(fetch = FetchType.LAZY) + @LazyToOne(LazyToOneOption.NO_PROXY) + private Human human = null; + + protected OtherEntity() { + // this form used by Hibernate + } + + public OtherEntity(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public Human getHuman() { + return human; + } + + public void setHuman(Human human) { + this.human = human; + } + } +} \ No newline at end of file