HHH-12297 - Relations are not loaded when using Fetch Profiles
This commit is contained in:
parent
229839b14a
commit
93c475f7e2
|
@ -15,8 +15,11 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
|
||||
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
|
||||
import org.hibernate.cache.spi.entry.CacheEntry;
|
||||
import org.hibernate.engine.profile.Fetch;
|
||||
import org.hibernate.engine.profile.FetchProfile;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.SessionEventListenerManager;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
@ -144,9 +147,12 @@ public final class TwoPhaseLoad {
|
|||
);
|
||||
}
|
||||
|
||||
String entityName = persister.getEntityName();
|
||||
String[] propertyNames = persister.getPropertyNames();
|
||||
final Type[] types = persister.getPropertyTypes();
|
||||
for ( int i = 0; i < hydratedState.length; i++ ) {
|
||||
final Object value = hydratedState[i];
|
||||
Boolean overridingEager = getOverridingEager( session, entityName, propertyNames[i], types[i] );
|
||||
if ( value == LazyPropertyInitializer.UNFETCHED_PROPERTY ) {
|
||||
// IMPLEMENTATION NOTE: This is a lazy property on a bytecode-enhanced entity.
|
||||
// hydratedState[i] needs to remain LazyPropertyInitializer.UNFETCHED_PROPERTY so that
|
||||
|
@ -157,12 +163,12 @@ public final class TwoPhaseLoad {
|
|||
// HHH-10989: We need to resolve the collection so that a CollectionReference is added to StatefulPersistentContext.
|
||||
// As mentioned above, hydratedState[i] needs to remain LazyPropertyInitializer.UNFETCHED_PROPERTY
|
||||
// so do not assign the resolved, unitialized PersistentCollection back to hydratedState[i].
|
||||
types[i].resolve( value, session, entity );
|
||||
types[i].resolve( value, session, entity, overridingEager );
|
||||
}
|
||||
}
|
||||
else if ( value!= PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
|
||||
else if ( value != PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
|
||||
// we know value != LazyPropertyInitializer.UNFETCHED_PROPERTY
|
||||
hydratedState[i] = types[i].resolve( value, session, entity );
|
||||
hydratedState[i] = types[i].resolve( value, session, entity, overridingEager );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,7 +294,54 @@ public final class TwoPhaseLoad {
|
|||
factory.getStatistics().loadEntity( persister.getEntityName() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if eager of the association is overriden by anything.
|
||||
*
|
||||
* @param session session
|
||||
* @param entityName entity name
|
||||
* @param associationName association name
|
||||
*
|
||||
* @return null if there is no overriding, true if it is overridden to eager and false if it is overridden to lazy
|
||||
*/
|
||||
private static Boolean getOverridingEager(
|
||||
SharedSessionContractImplementor session,
|
||||
String entityName,
|
||||
String associationName,
|
||||
Type type) {
|
||||
if ( type.isAssociationType() || type.isCollectionType() ) {
|
||||
Boolean overridingEager = isEagerFetchProfile( session, entityName + "." + associationName );
|
||||
|
||||
if ( LOG.isDebugEnabled() ) {
|
||||
if ( overridingEager != null ) {
|
||||
LOG.debugf(
|
||||
"Overriding eager fetching using active fetch profile. EntityName: %s, associationName: %s, eager fetching: %s",
|
||||
entityName,
|
||||
associationName,
|
||||
overridingEager
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return overridingEager;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Boolean isEagerFetchProfile(SharedSessionContractImplementor session, String role) {
|
||||
LoadQueryInfluencers loadQueryInfluencers = session.getLoadQueryInfluencers();
|
||||
|
||||
for ( String fetchProfileName : loadQueryInfluencers.getEnabledFetchProfileNames() ) {
|
||||
FetchProfile fp = session.getFactory().getFetchProfile( fetchProfileName );
|
||||
Fetch fetch = fp.getFetchByRole( role );
|
||||
if ( fetch != null && Fetch.Style.JOIN == fetch.getStyle() ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostLoad cannot occur during initializeEntity, as that call occurs *before*
|
||||
* the Set collections are added to the persistence context by Loader.
|
||||
|
@ -296,7 +349,7 @@ public final class TwoPhaseLoad {
|
|||
* postLoad if it acts upon the collection.
|
||||
*
|
||||
* HHH-6043
|
||||
*
|
||||
*
|
||||
* @param entity The entity
|
||||
* @param session The Session
|
||||
* @param postLoadEvent The (re-used) post-load event
|
||||
|
@ -305,7 +358,7 @@ public final class TwoPhaseLoad {
|
|||
final Object entity,
|
||||
final SharedSessionContractImplementor session,
|
||||
final PostLoadEvent postLoadEvent) {
|
||||
|
||||
|
||||
if ( session.isEventSource() ) {
|
||||
final PersistenceContext persistenceContext
|
||||
= session.getPersistenceContext();
|
||||
|
|
|
@ -286,7 +286,7 @@ public abstract class CollectionType extends AbstractType implements Association
|
|||
final Serializable key = (Serializable) getPersister(session)
|
||||
.getKeyType()
|
||||
.assemble( cached, session, owner);
|
||||
return resolveKey( key, session, owner );
|
||||
return resolveKey( key, session, owner, null );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -452,14 +452,19 @@ public abstract class CollectionType extends AbstractType implements Association
|
|||
public Object resolve(Object value, SharedSessionContractImplementor session, Object owner)
|
||||
throws HibernateException {
|
||||
|
||||
return resolveKey( getKeyOfOwner( owner, session ), session, owner );
|
||||
return resolve(value, session, owner, null);
|
||||
}
|
||||
|
||||
private Object resolveKey(Serializable key, SharedSessionContractImplementor session, Object owner) {
|
||||
@Override
|
||||
public Object resolve(Object value, SharedSessionContractImplementor session, Object owner, Boolean overridingEager) throws HibernateException {
|
||||
return resolveKey( getKeyOfOwner( owner, session ), session, owner, overridingEager );
|
||||
}
|
||||
|
||||
private Object resolveKey(Serializable key, SharedSessionContractImplementor session, Object owner, Boolean overridingEager) {
|
||||
// if (key==null) throw new AssertionFailure("owner identifier unknown when re-assembling
|
||||
// collection reference");
|
||||
return key == null ? null : // TODO: can this case really occur??
|
||||
getCollection( key, session, owner );
|
||||
getCollection( key, session, owner, overridingEager );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -745,7 +750,7 @@ public abstract class CollectionType extends AbstractType implements Association
|
|||
* @param owner The collection owner
|
||||
* @return The collection
|
||||
*/
|
||||
public Object getCollection(Serializable key, SharedSessionContractImplementor session, Object owner) {
|
||||
public Object getCollection(Serializable key, SharedSessionContractImplementor session, Object owner, Boolean overridingEager) {
|
||||
|
||||
CollectionPersister persister = getPersister( session );
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContext();
|
||||
|
@ -772,10 +777,11 @@ public abstract class CollectionType extends AbstractType implements Association
|
|||
persistenceContext.addUninitializedCollection( persister, collection, key );
|
||||
|
||||
// some collections are not lazy:
|
||||
boolean eager = overridingEager != null ? overridingEager : !persister.isLazy();
|
||||
if ( initializeImmediately() ) {
|
||||
session.initializeCollection( collection, false );
|
||||
}
|
||||
else if ( !persister.isLazy() ) {
|
||||
else if ( eager ) {
|
||||
persistenceContext.addNonLazyCollection( collection );
|
||||
}
|
||||
|
||||
|
|
|
@ -452,9 +452,14 @@ public abstract class EntityType extends AbstractType implements AssociationType
|
|||
*/
|
||||
@Override
|
||||
public Object resolve(Object value, SharedSessionContractImplementor session, Object owner) throws HibernateException {
|
||||
return resolve(value, session, owner, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolve(Object value, SharedSessionContractImplementor session, Object owner, Boolean overridingEager) throws HibernateException {
|
||||
if ( value != null && !isNull( owner, session ) ) {
|
||||
if ( isReferenceToPrimaryKey() ) {
|
||||
return resolveIdentifier( (Serializable) value, session );
|
||||
return resolveIdentifier( (Serializable) value, session, overridingEager );
|
||||
}
|
||||
else if ( uniqueKeyPropertyName != null ) {
|
||||
return loadByUniqueKey( getAssociatedEntityName(), uniqueKeyPropertyName, value, session );
|
||||
|
@ -664,11 +669,14 @@ public abstract class EntityType extends AbstractType implements AssociationType
|
|||
*
|
||||
* @throws org.hibernate.HibernateException Indicates problems performing the load.
|
||||
*/
|
||||
protected final Object resolveIdentifier(Serializable id, SharedSessionContractImplementor session) throws HibernateException {
|
||||
protected final Object resolveIdentifier(Serializable id, SharedSessionContractImplementor session, Boolean overridingEager) throws HibernateException {
|
||||
|
||||
boolean isProxyUnwrapEnabled = unwrapProxy &&
|
||||
getAssociatedEntityPersister( session.getFactory() )
|
||||
.isInstrumented();
|
||||
|
||||
boolean eager = overridingEager != null ? overridingEager : this.eager;
|
||||
|
||||
Object proxyOrEntity = session.internalLoad(
|
||||
getAssociatedEntityName(),
|
||||
id,
|
||||
|
@ -684,6 +692,10 @@ public abstract class EntityType extends AbstractType implements AssociationType
|
|||
return proxyOrEntity;
|
||||
}
|
||||
|
||||
protected final Object resolveIdentifier(Serializable id, SharedSessionContractImplementor session) throws HibernateException {
|
||||
return resolveIdentifier( id, session, null );
|
||||
}
|
||||
|
||||
protected boolean isNull(Object owner, SharedSessionContractImplementor session) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -460,23 +460,33 @@ public interface Type extends Serializable {
|
|||
Object hydrate(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
|
||||
throws HibernateException, SQLException;
|
||||
|
||||
/**
|
||||
* @see #resolve(Object, SharedSessionContractImplementor, Object, Boolean)
|
||||
*/
|
||||
Object resolve(Object value, SharedSessionContractImplementor session, Object owner)
|
||||
throws HibernateException;
|
||||
|
||||
/**
|
||||
* The second phase of 2-phase loading. Only really pertinent for entities and collections. Here we resolve the
|
||||
* identifier to an entity or collection instance
|
||||
*
|
||||
*
|
||||
* @param value an identifier or value returned by <tt>hydrate()</tt>
|
||||
* @param owner the parent entity
|
||||
* @param session the session
|
||||
*
|
||||
* @param overridingEager can override eager from the mapping. For example because of {@link org.hibernate.engine.spi.LoadQueryInfluencers}
|
||||
* If null, then it does not override. If true or false then it overrides the mapping value.
|
||||
*
|
||||
* @return the given value, or the value associated with the identifier
|
||||
*
|
||||
* @throws HibernateException An error from Hibernate
|
||||
*
|
||||
* @see #hydrate
|
||||
*/
|
||||
Object resolve(Object value, SharedSessionContractImplementor session, Object owner)
|
||||
throws HibernateException;
|
||||
|
||||
default Object resolve(Object value, SharedSessionContractImplementor session, Object owner, Boolean overridingEager)
|
||||
throws HibernateException {
|
||||
return resolve(value, session, owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a hydrated, but unresolved value, return a value that may be used to reconstruct property-ref
|
||||
* associations.
|
||||
|
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* 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.fetchprofiles;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
|
||||
import org.hibernate.LazyInitializationException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.annotations.FetchMode;
|
||||
import org.hibernate.annotations.FetchProfile;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@TestForIssue( jiraKey = "HHH-12297")
|
||||
public class CollectionLoadedInTwoPhaseLoadTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
// NOTE
|
||||
// there are two fetch profiles because when I use only one the relation OrgUnit.people
|
||||
// is missing in the fetch profile.
|
||||
// It is missing because of logic in FetchProfile.addFetch(). Do not understand the implementation
|
||||
// of the method now, so the workaround is to use two fetch profiles.
|
||||
static final String FETCH_PROFILE_NAME = "fp1";
|
||||
static final String FETCH_PROFILE_NAME_2 = "fp2";
|
||||
|
||||
private final String OU_1 = "ou_1";
|
||||
private final String OU_2 = "ou_2";
|
||||
private final String P_1 = "p_1";
|
||||
private final String P_2 = "p_2";
|
||||
|
||||
public void configure(Configuration cfg) {
|
||||
cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIfEverythingIsLoaded() {
|
||||
createSampleData();
|
||||
sessionFactory().getStatistics().clear();
|
||||
try {
|
||||
OrgUnit ou1 = this.loadOrgUnitWithFetchProfile( OU_1 );
|
||||
Person p1 = ou1.findPerson( P_1 );
|
||||
OrgUnit ou2 = p1.findOrgUnit( OU_2 );
|
||||
Person p2 = ou2.findPerson( P_2 );
|
||||
@SuppressWarnings( "unused" )
|
||||
String email = p2.getEmail();
|
||||
assertEquals( 4, sessionFactory().getStatistics().getEntityLoadCount() );
|
||||
}
|
||||
catch (LazyInitializationException e) {
|
||||
fail( "Everything should be initialized" );
|
||||
}
|
||||
}
|
||||
|
||||
public OrgUnit loadOrgUnitWithFetchProfile(String groupId) {
|
||||
return doInHibernate( this::sessionFactory, session -> {
|
||||
session.enableFetchProfile( FETCH_PROFILE_NAME );
|
||||
session.enableFetchProfile( FETCH_PROFILE_NAME_2 );
|
||||
return session.get( OrgUnit.class, groupId );
|
||||
} );
|
||||
}
|
||||
|
||||
private void createSampleData() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
OrgUnit ou1 = new OrgUnit( OU_1, "org unit one" );
|
||||
OrgUnit ou2 = new OrgUnit( OU_2, "org unit two" );
|
||||
Person p1 = new Person( P_1, "p1@coompany.com" );
|
||||
Person p2 = new Person( P_2, "p2@company.com" );
|
||||
|
||||
ou1.addPerson( p1 );
|
||||
ou2.addPerson( p1 );
|
||||
ou2.addPerson( p2 );
|
||||
|
||||
session.persist( ou1 );
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
Person.class,
|
||||
OrgUnit.class
|
||||
};
|
||||
}
|
||||
|
||||
@Entity(name = "OrgUnit")
|
||||
@FetchProfile(name = FETCH_PROFILE_NAME, fetchOverrides = {
|
||||
@FetchProfile.FetchOverride(entity = OrgUnit.class, association = "people", mode = FetchMode.JOIN)
|
||||
})
|
||||
public static class OrgUnit {
|
||||
|
||||
@Id
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "orgUnits", cascade = CascadeType.PERSIST)
|
||||
private List<Person> people = new ArrayList<>();
|
||||
|
||||
public OrgUnit() {
|
||||
}
|
||||
|
||||
public OrgUnit(String name, String description) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public Person findPerson(String personName) {
|
||||
if (people == null) {
|
||||
return null;
|
||||
}
|
||||
for ( Person person : people ) {
|
||||
if (person.getName().equals( personName )) return person;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addPerson(Person person) {
|
||||
if (people.contains( person )) return;
|
||||
people.add(person);
|
||||
person.addOrgUnit( this);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public List<Person> getPeople() {
|
||||
return people;
|
||||
}
|
||||
|
||||
public void setPeople(List<Person> people) {
|
||||
this.people = people;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
OrgUnit group = (OrgUnit) o;
|
||||
return Objects.equals( name, group.name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OrgUnit{" +
|
||||
"name='" + name + '\'' +
|
||||
", description='" + description + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "Person")
|
||||
@FetchProfile(name = FETCH_PROFILE_NAME_2, fetchOverrides = {
|
||||
@FetchProfile.FetchOverride(entity = Person.class, association = "orgUnits", mode = FetchMode.JOIN)
|
||||
})
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
private String name;
|
||||
|
||||
private String email;
|
||||
|
||||
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
|
||||
private List<OrgUnit> orgUnits = new ArrayList<>();
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(String name, String email) {
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public OrgUnit findOrgUnit(String orgUnitName) {
|
||||
if ( orgUnits == null) {
|
||||
return null;
|
||||
}
|
||||
for ( OrgUnit orgUnit : orgUnits ) {
|
||||
if (orgUnit.getName().equals( orgUnitName )) return orgUnit;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addOrgUnit(OrgUnit orgUnit) {
|
||||
if ( orgUnits.contains( orgUnit)) return;
|
||||
orgUnits.add( orgUnit);
|
||||
orgUnit.addPerson(this);
|
||||
}
|
||||
|
||||
public List<OrgUnit> getOrgUnits() {
|
||||
return orgUnits;
|
||||
}
|
||||
|
||||
public void setOrgUnits(List<OrgUnit> orgUnits) {
|
||||
this.orgUnits = orgUnits;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
Person person = (Person) o;
|
||||
return Objects.equals( name, person.name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person{" +
|
||||
"name='" + name + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
/*
|
||||
* 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.fetchprofiles;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import org.hibernate.LazyInitializationException;
|
||||
import org.hibernate.annotations.FetchMode;
|
||||
import org.hibernate.annotations.FetchProfile;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.sun.tools.internal.ws.wsdl.parser.Util.fail;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@TestForIssue( jiraKey = "HHH-12297")
|
||||
public class EntityLoadedInTwoPhaseLoadTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
static final String FETCH_PROFILE_NAME = "fp1";
|
||||
|
||||
public void configure(Configuration cfg) {
|
||||
cfg.setProperty( Environment.GENERATE_STATISTICS, "true" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIfAllRelationsAreInitialized() {
|
||||
long startId = this.createSampleData();
|
||||
sessionFactory().getStatistics().clear();
|
||||
try {
|
||||
Start start = this.loadStartWithFetchProfile( startId );
|
||||
@SuppressWarnings( "unused" )
|
||||
String value = start.getVia2().getMid().getFinish().getValue();
|
||||
assertEquals( 4, sessionFactory().getStatistics().getEntityLoadCount() );
|
||||
}
|
||||
catch (LazyInitializationException e) {
|
||||
fail( "Everything should be initialized" );
|
||||
}
|
||||
}
|
||||
|
||||
public Start loadStartWithFetchProfile(long startId) {
|
||||
return doInHibernate( this::sessionFactory, session -> {
|
||||
session.enableFetchProfile( FETCH_PROFILE_NAME );
|
||||
return session.get( Start.class, startId );
|
||||
} );
|
||||
}
|
||||
|
||||
private long createSampleData() {
|
||||
return doInHibernate( this::sessionFactory, session -> {
|
||||
Finish finish = new Finish( "foo" );
|
||||
Mid mid = new Mid( finish );
|
||||
Via2 via2 = new Via2( mid );
|
||||
Start start = new Start( null, via2 );
|
||||
|
||||
session.persist( start );
|
||||
|
||||
return start.getId();
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
Start.class,
|
||||
Mid.class,
|
||||
Finish.class,
|
||||
Via1.class,
|
||||
Via2.class
|
||||
};
|
||||
}
|
||||
|
||||
@Entity(name = "Finish")
|
||||
public static class Finish {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
@Column(name = "value", nullable = false)
|
||||
private String value;
|
||||
|
||||
public Finish() {
|
||||
}
|
||||
|
||||
public Finish(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Mid")
|
||||
@FetchProfile(name = FETCH_PROFILE_NAME, fetchOverrides = {
|
||||
@FetchProfile.FetchOverride(entity = Mid.class, association = "finish", mode = FetchMode.JOIN)
|
||||
})
|
||||
public static class Mid {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
|
||||
private Finish finish;
|
||||
|
||||
public Mid() {
|
||||
}
|
||||
|
||||
public Mid(Finish finish) {
|
||||
this.finish = finish;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Finish getFinish() {
|
||||
return finish;
|
||||
}
|
||||
|
||||
public void setFinish(Finish finish) {
|
||||
this.finish = finish;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "Start")
|
||||
@FetchProfile(name = FETCH_PROFILE_NAME, fetchOverrides = {
|
||||
@FetchProfile.FetchOverride(entity = Start.class, association = "via1", mode = FetchMode.JOIN),
|
||||
@FetchProfile.FetchOverride(entity = Start.class, association = "via2", mode = FetchMode.JOIN)
|
||||
})
|
||||
public static class Start {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
|
||||
private Via1 via1;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
|
||||
private Via2 via2;
|
||||
|
||||
public Start() {
|
||||
}
|
||||
|
||||
public Start(Via1 via1, Via2 via2) {
|
||||
this.via1 = via1;
|
||||
this.via2 = via2;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Via1 getVia1() {
|
||||
return via1;
|
||||
}
|
||||
|
||||
public void setVia1(Via1 via1) {
|
||||
this.via1 = via1;
|
||||
}
|
||||
|
||||
public Via2 getVia2() {
|
||||
return via2;
|
||||
}
|
||||
|
||||
public void setVia2(Via2 via2) {
|
||||
this.via2 = via2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "Via1")
|
||||
@FetchProfile(name = FETCH_PROFILE_NAME, fetchOverrides = {
|
||||
@FetchProfile.FetchOverride(entity = Via1.class, association = "mid", mode = FetchMode.JOIN)
|
||||
})
|
||||
public static class Via1 {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
|
||||
private Mid mid;
|
||||
|
||||
public Via1() {
|
||||
}
|
||||
|
||||
public Via1(Mid mid) {
|
||||
this.mid = mid;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Mid getMid() {
|
||||
return mid;
|
||||
}
|
||||
|
||||
public void setMid(Mid mid) {
|
||||
this.mid = mid;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "Via2")
|
||||
@FetchProfile(name = FETCH_PROFILE_NAME, fetchOverrides = {
|
||||
@FetchProfile.FetchOverride(entity = Via2.class, association = "mid", mode = FetchMode.JOIN)
|
||||
})
|
||||
public static class Via2 {
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
|
||||
private Mid mid;
|
||||
|
||||
public Via2() {
|
||||
}
|
||||
|
||||
public Via2(Mid mid) {
|
||||
this.mid = mid;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Mid getMid() {
|
||||
return mid;
|
||||
}
|
||||
|
||||
public void setMid(Mid mid) {
|
||||
this.mid = mid;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -275,11 +275,6 @@ public class JoinFetchProfileTest extends BaseCoreFunctionalTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* fetch-profiles should have no effect what-so-ever on the direct results of the HQL query.
|
||||
*
|
||||
* TODO : this is actually not strictly true. what we should have happen is to subsequently load those fetches
|
||||
*/
|
||||
@Test
|
||||
public void testHQL() {
|
||||
performWithStandardData(
|
||||
|
@ -292,8 +287,8 @@ public class JoinFetchProfileTest extends BaseCoreFunctionalTestCase {
|
|||
List sections = session.createQuery( "from CourseOffering" ).list();
|
||||
int sectionCount = sections.size();
|
||||
assertEquals( "unexpected CourseOffering count", 1, sectionCount );
|
||||
assertEquals( 1, sessionFactory().getStatistics().getEntityLoadCount() );
|
||||
assertEquals( 0, sessionFactory().getStatistics().getEntityFetchCount() );
|
||||
assertEquals( 4, sessionFactory().getStatistics().getEntityLoadCount() );
|
||||
assertEquals( 2, sessionFactory().getStatistics().getEntityFetchCount() );
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue