HHH-13094 - Respect @Any.fetch setting to FetchType.EAGER

This commit is contained in:
John Lin 2018-11-12 17:32:07 +08:00 committed by Vlad Mihalcea
parent 7358893eac
commit 08747fc2f4
17 changed files with 394 additions and 25 deletions

View File

@ -2343,6 +2343,9 @@ public class ModelBinder {
Any anyBinding,
final AttributeRole attributeRole,
AttributePath attributePath) {
anyBinding.setLazy( anyMapping.isLazy() );
final TypeResolution keyTypeResolution = resolveType(
sourceDocument,
anyMapping.getKeySource().getTypeSource()

View File

@ -268,4 +268,9 @@ public class SingularAttributeSourceAnyImpl
public String getCascadeStyleName() {
return jaxbAnyMapping.getCascade();
}
@Override
public boolean isLazy() {
return isBytecodeLazy();
}
}

View File

@ -15,4 +15,8 @@ package org.hibernate.boot.model.source.spi;
public interface AnyMappingSource {
AnyDiscriminatorSource getDiscriminatorSource();
AnyKeySource getKeySource();
default boolean isLazy() {
return true;
}
}

View File

@ -3272,12 +3272,14 @@ public final class AnnotationBinder {
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
boolean lazy = ( anyAnn.fetch() == FetchType.LAZY );
Any value = BinderHelper.buildAnyValue(
anyAnn.metaDef(),
columns,
anyAnn.metaColumn(),
inferredData,
cascadeOnDelete,
lazy,
nullability,
propertyHolder,
entityBinder,
@ -3289,7 +3291,7 @@ public final class AnnotationBinder {
binder.setName( inferredData.getPropertyName() );
binder.setValue( value );
binder.setLazy( anyAnn.fetch() == FetchType.LAZY );
binder.setLazy( lazy );
//binder.setCascade(cascadeStrategy);
if ( isIdentifierMapper ) {
binder.setInsertable( false );

View File

@ -941,6 +941,7 @@ public class BinderHelper {
javax.persistence.Column metaColumn,
PropertyData inferredData,
boolean cascadeOnDelete,
boolean lazy,
Nullability nullability,
PropertyHolder propertyHolder,
EntityBinder entityBinder,
@ -950,6 +951,7 @@ public class BinderHelper {
Any value = new Any( context, columns[0].getTable() );
AnyMetaDef metaAnnDef = inferredData.getProperty().getAnnotation( AnyMetaDef.class );
value.setLazy( lazy );
if ( metaAnnDef != null ) {
//local has precedence over general and can be mapped for future reference if named
bindAnyMetaDefs( inferredData.getProperty(), context );

View File

@ -1494,6 +1494,7 @@ public abstract class CollectionBinder {
anyAnn.metaColumn(),
inferredData,
cascadeDeleteEnabled,
anyAnn.fetch() == FetchType.LAZY,
Nullability.NO_CONSTRAINT,
propertyHolder,
new EntityBinder(),

View File

@ -24,6 +24,7 @@ public class Any extends SimpleValue {
private String identifierTypeName;
private String metaTypeName = "string";
private Map metaValues;
private boolean lazy = true;
/**
* @deprecated Use {@link Any#Any(MetadataBuildingContext, Table)} instead.
@ -50,7 +51,8 @@ public class Any extends SimpleValue {
return getMetadata().getTypeResolver().getTypeFactory().any(
metaValues == null ? metaType : new MetaType( metaValues, metaType ),
getMetadata().getTypeResolver().heuristicType( identifierTypeName )
getMetadata().getTypeResolver().heuristicType( identifierTypeName ),
isLazy()
);
}
@ -72,6 +74,14 @@ public class Any extends SimpleValue {
this.metaValues = metaValues;
}
public boolean isLazy() {
return lazy;
}
public void setLazy(boolean lazy) {
this.lazy = lazy;
}
public void setTypeUsingReflection(String className, String propertyName)
throws MappingException {
}
@ -89,6 +99,7 @@ public class Any extends SimpleValue {
return super.isSame( other )
&& Objects.equals( identifierTypeName, other.identifierTypeName )
&& Objects.equals( metaTypeName, other.metaTypeName )
&& Objects.equals( metaValues, other.metaValues );
&& Objects.equals( metaValues, other.metaValues )
&& lazy == other.lazy;
}
}

View File

@ -47,18 +47,20 @@ public class AnyType extends AbstractType implements CompositeType, AssociationT
private final TypeFactory.TypeScope scope;
private final Type identifierType;
private final Type discriminatorType;
private final boolean eager;
/**
* Intended for use only from legacy {@link ObjectType} type definition
*/
protected AnyType(Type discriminatorType, Type identifierType) {
this( null, discriminatorType, identifierType );
this( null, discriminatorType, identifierType, true );
}
public AnyType(TypeFactory.TypeScope scope, Type discriminatorType, Type identifierType) {
public AnyType(TypeFactory.TypeScope scope, Type discriminatorType, Type identifierType, boolean lazy) {
this.scope = scope;
this.discriminatorType = discriminatorType;
this.identifierType = identifierType;
this.eager = !lazy;
}
public Type getIdentifierType() {
@ -266,7 +268,7 @@ public class AnyType extends AbstractType implements CompositeType, AssociationT
throws HibernateException {
return entityName==null || id==null
? null
: session.internalLoad( entityName, id, false, false );
: session.internalLoad( entityName, id, eager, false );
}
@Override
@ -319,7 +321,7 @@ public class AnyType extends AbstractType implements CompositeType, AssociationT
@Override
public Object assemble(Serializable cached, SharedSessionContractImplementor session, Object owner) throws HibernateException {
final ObjectTypeCacheEntry e = (ObjectTypeCacheEntry) cached;
return e == null ? null : session.internalLoad( e.entityName, e.id, false, false );
return e == null ? null : session.internalLoad( e.entityName, e.id, eager, false );
}
@Override
@ -348,7 +350,7 @@ public class AnyType extends AbstractType implements CompositeType, AssociationT
else {
final String entityName = session.bestGuessEntityName( original );
final Serializable id = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, original, session );
return session.internalLoad( entityName, id, false, false );
return session.internalLoad( entityName, id, eager, false );
}
}

View File

@ -401,7 +401,28 @@ public final class TypeFactory implements Serializable {
// any type builder ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Get the AnyType with the specified parameters.
*
* @param metaType meta type
* @param identifierType identifier type
* @return AnyType
* @deprecated use {@link TypeFactory#any(Type, Type, boolean)} instead
*/
@Deprecated
public Type any(Type metaType, Type identifierType) {
return new AnyType( typeScope, metaType, identifierType );
return any( metaType, identifierType, true );
}
/**
* Get the AnyType with the specified parameters.
*
* @param metaType meta type
* @param identifierType identifier type
* @param lazy is teh underlying proeprty lazy
* @return AnyType
*/
public Type any(Type metaType, Type identifierType, boolean lazy) {
return new AnyType( typeScope, metaType, identifierType, lazy );
}
}

View File

@ -6,18 +6,27 @@
*/
package org.hibernate.test.annotations.any;
import org.junit.Test;
import org.hibernate.Query;
import org.hibernate.LazyInitializationException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.query.Query;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class AnyTest extends BaseCoreFunctionalTestCase {
@Override
protected boolean isCleanupTestDataRequired() {
return true;
}
@Test
public void testDefaultAnyAssociation() {
Session s = openSession();
@ -26,13 +35,13 @@ public class AnyTest extends BaseCoreFunctionalTestCase {
PropertySet set1 = new PropertySet( "string" );
Property property = new StringProperty( "name", "Alex" );
set1.setSomeProperty( property );
set1.addGeneratedProperty( property );
set1.addGeneralProperty( property );
s.save( set1 );
PropertySet set2 = new PropertySet( "integer" );
property = new IntegerProperty( "age", 33 );
set2.setSomeProperty( property );
set2.addGeneratedProperty( property );
set2.addGeneralProperty( property );
s.save( set2 );
s.flush();
@ -116,10 +125,10 @@ public class AnyTest extends BaseCoreFunctionalTestCase {
list.setSomeProperty( longProperty );
list.addGeneratedProperty( stringProperty );
list.addGeneratedProperty( integerProperty );
list.addGeneratedProperty( longProperty );
list.addGeneratedProperty( charProp );
list.addGeneralProperty( stringProperty );
list.addGeneralProperty( integerProperty );
list.addGeneralProperty( longProperty );
list.addGeneralProperty( charProp );
s.save( list );
@ -151,6 +160,57 @@ public class AnyTest extends BaseCoreFunctionalTestCase {
s.close();
}
@Test
public void testFetchEager() {
doInHibernate( this::sessionFactory, s -> {
PropertySet set = new PropertySet( "string" );
Property property = new StringProperty( "name", "Alex" );
set.setSomeProperty( property );
s.save( set );
} );
PropertySet result = doInHibernate( this::sessionFactory, s -> {
return s.createQuery( "select s from PropertySet s where name = :name", PropertySet.class )
.setParameter( "name", "string" )
.getSingleResult();
} );
assertNotNull( result );
assertNotNull( result.getSomeProperty() );
assertTrue( result.getSomeProperty() instanceof StringProperty );
assertEquals( "Alex", result.getSomeProperty().asString() );
}
@Test
public void testFetchLazy() {
doInHibernate( this::sessionFactory, s -> {
LazyPropertySet set = new LazyPropertySet( "string" );
Property property = new StringProperty( "name", "Alex" );
set.setSomeProperty( property );
s.save( set );
} );
LazyPropertySet result = doInHibernate( this::sessionFactory, s -> {
return s.createQuery( "select s from LazyPropertySet s where name = :name", LazyPropertySet.class )
.setParameter( "name", "string" )
.getSingleResult();
} );
assertNotNull( result );
assertNotNull( result.getSomeProperty() );
try {
result.getSomeProperty().asString();
fail( "should not get the property string after session closed." );
}
catch (LazyInitializationException e) {
// expected
}
catch (Exception e) {
fail( "should not throw exception other than LazyInitializationException." );
}
}
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] {
@ -158,6 +218,7 @@ public class AnyTest extends BaseCoreFunctionalTestCase {
IntegerProperty.class,
LongProperty.class,
PropertySet.class,
LazyPropertySet.class,
PropertyMap.class,
PropertyList.class,
CharProperty.class
@ -172,8 +233,8 @@ public class AnyTest extends BaseCoreFunctionalTestCase {
}
// Simply having this orm.xml file in the classpath reproduces HHH-4261.
@Override
protected String[] getXmlFiles() {
return new String[] { "org/hibernate/test/annotations/any/orm.xml" };
}
@Override
protected String[] getXmlFiles() {
return new String[] { "org/hibernate/test/annotations/any/orm.xml" };
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.annotations.any;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyMetaDef;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.MetaValue;
@Entity
@Table( name = "lazy_property_set" )
public class LazyPropertySet {
private Integer id;
private String name;
private Property someProperty;
public LazyPropertySet() {
super();
}
public LazyPropertySet(String name) {
this.name = name;
}
@Id
@GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Any( metaColumn = @Column( name = "property_type" ), fetch = FetchType.LAZY )
@Cascade( value = { CascadeType.ALL } )
@AnyMetaDef( idType = "integer", metaType = "string", metaValues = {
@MetaValue( value = "S", targetEntity = StringProperty.class ),
@MetaValue( value = "I", targetEntity = IntegerProperty.class )
} )
@JoinColumn( name = "property_id" )
public Property getSomeProperty() {
return someProperty;
}
public void setSomeProperty(Property someProperty) {
this.someProperty = someProperty;
}
}

View File

@ -84,7 +84,7 @@ public class PropertyList<T extends Property> {
this.someProperty = someProperty;
}
public void addGeneratedProperty(T property) {
public void addGeneralProperty(T property) {
this.generalProperties.add( property );
}
}

View File

@ -89,7 +89,7 @@ public class PropertySet {
this.someProperty = someProperty;
}
public void addGeneratedProperty(Property property) {
public void addGeneralProperty(Property property) {
this.generalProperties.add( property );
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.any;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.test.annotations.any.*;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class AnyEagerHbmTest extends BaseCoreFunctionalTestCase {
@Override
protected boolean isCleanupTestDataRequired() {
return true;
}
@Test
public void testFetchEager() {
doInHibernate( this::sessionFactory, s -> {
org.hibernate.test.annotations.any.PropertySet set = new org.hibernate.test.annotations.any.PropertySet( "string" );
Property property = new StringProperty( "name", "Alex" );
set.setSomeProperty( property );
s.save( set );
} );
org.hibernate.test.annotations.any.PropertySet result = doInHibernate( this::sessionFactory, s -> {
return s.createQuery( "select s from PropertySet s where name = :name", org.hibernate.test.annotations.any.PropertySet.class )
.setParameter( "name", "string" )
.getSingleResult();
} );
assertNotNull( result );
assertNotNull( result.getSomeProperty() );
assertTrue( result.getSomeProperty() instanceof StringProperty );
assertEquals( "Alex", result.getSomeProperty().asString() );
}
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] {
StringProperty.class,
IntegerProperty.class,
};
}
@Override
public String[] getMappings() {
return new String[] {
"any/AnyTestEagerPropertySet.hbm.xml"
};
}
}

View File

@ -0,0 +1,73 @@
/*
* 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.any;
import org.hibernate.LazyInitializationException;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.test.annotations.any.IntegerProperty;
import org.hibernate.test.annotations.any.LazyPropertySet;
import org.hibernate.test.annotations.any.Property;
import org.hibernate.test.annotations.any.StringProperty;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
public class AnyLazyHbmTest extends BaseCoreFunctionalTestCase {
@Override
protected boolean isCleanupTestDataRequired() {
return true;
}
@Test
public void testFetchLazy() {
doInHibernate( this::sessionFactory, s -> {
LazyPropertySet set = new LazyPropertySet( "string" );
Property property = new StringProperty( "name", "Alex" );
set.setSomeProperty( property );
s.save( set );
} );
LazyPropertySet result = doInHibernate( this::sessionFactory, s -> {
return s.createQuery( "select s from LazyPropertySet s where name = :name", LazyPropertySet.class )
.setParameter( "name", "string" )
.getSingleResult();
} );
assertNotNull( result );
assertNotNull( result.getSomeProperty() );
try {
result.getSomeProperty().asString();
fail( "should not get the property string after session closed." );
}
catch (LazyInitializationException e) {
// expected
}
catch (Exception e) {
fail( "should not throw exception other than LazyInitializationException." );
}
}
@Override
protected Class[] getAnnotatedClasses() {
return new Class[] {
StringProperty.class,
IntegerProperty.class,
};
}
@Override
public String[] getMappings() {
return new String[] {
"any/AnyTestLazyPropertySet.hbm.xml"
};
}
}

View File

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<!--
~ 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>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.test.annotations.any">
<class name="PropertySet" table="lazy_property_set">
<id name="id" type="java.lang.Integer">
<generator class="increment"/>
</id>
<property name="name"/>
<any name="someProperty" id-type="integer" meta-type="string" cascade="all" lazy="false">
<meta-value value="S" class="StringProperty"/>
<meta-value value="I" class="IntegerProperty"/>
<column name="PROP_TYPE"/>
<column name="property_id"/>
</any>
</class>
</hibernate-mapping>

View File

@ -0,0 +1,27 @@
<?xml version="1.0"?>
<!--
~ 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>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.hibernate.test.annotations.any">
<class name="LazyPropertySet" table="lazy_property_set">
<id name="id" type="java.lang.Integer">
<generator class="increment"/>
</id>
<property name="name"/>
<any name="someProperty" id-type="integer" meta-type="string" cascade="all" lazy="true">
<meta-value value="S" class="StringProperty"/>
<meta-value value="I" class="IntegerProperty"/>
<column name="PROP_TYPE"/>
<column name="property_id"/>
</any>
</class>
</hibernate-mapping>