HHH-16654 introduce a "default" fetch profile with eager to-ones in it
also, make the query translator always respect the fetch type specified in the fetch profile (previously it would ignore it for statically-EAGER many-to-ones, which was inconsistent and made little sense)
This commit is contained in:
parent
b3e27788fa
commit
176abffdd5
|
@ -1463,7 +1463,6 @@ public abstract class CollectionBinder {
|
|||
.addSecondPass( new FetchSecondPass( fetch, propertyHolder, propertyName, buildingContext ) );
|
||||
}
|
||||
else if ( property.isAnnotationPresent( FetchProfileOverrides.class ) ) {
|
||||
boolean result = false;
|
||||
for ( FetchProfileOverride fetch: property.getAnnotation( FetchProfileOverrides.class ).value() ) {
|
||||
buildingContext.getMetadataCollector()
|
||||
.addSecondPass( new FetchSecondPass( fetch, propertyHolder, propertyName, buildingContext ) );
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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.boot.model.internal;
|
||||
|
||||
import jakarta.persistence.FetchType;
|
||||
import org.hibernate.annotations.FetchProfileOverride;
|
||||
import org.hibernate.mapping.FetchProfile;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
import static jakarta.persistence.FetchType.EAGER;
|
||||
|
||||
/**
|
||||
* @author Gavin King
|
||||
*/
|
||||
class DefaultFetchProfileOverride implements FetchProfileOverride {
|
||||
|
||||
static final FetchProfileOverride INSTANCE = new DefaultFetchProfileOverride();
|
||||
|
||||
@Override
|
||||
public org.hibernate.annotations.FetchMode mode() {
|
||||
return org.hibernate.annotations.FetchMode.JOIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchType fetch() {
|
||||
return EAGER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String profile() {
|
||||
return FetchProfile.HIBERNATE_DEFAULT_PROFILE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return FetchProfileOverride.class;
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ import org.hibernate.mapping.FetchProfile;
|
|||
import org.hibernate.mapping.PersistentClass;
|
||||
|
||||
import static org.hibernate.internal.util.StringHelper.qualify;
|
||||
import static org.hibernate.mapping.FetchProfile.HIBERNATE_DEFAULT_PROFILE;
|
||||
import static org.hibernate.mapping.MetadataSource.ANNOTATIONS;
|
||||
|
||||
/**
|
||||
|
@ -42,19 +43,18 @@ public class FetchSecondPass implements SecondPass {
|
|||
@Override
|
||||
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
|
||||
|
||||
//TODO: handle propertyHolder.getPath() !!!!
|
||||
|
||||
// throws MappingException in case the property does not exist
|
||||
buildingContext.getMetadataCollector()
|
||||
.getEntityBinding( propertyHolder.getEntityName() )
|
||||
.getProperty( propertyName );
|
||||
|
||||
FetchProfile profile = buildingContext.getMetadataCollector().getFetchProfile( fetch.profile() );
|
||||
if ( profile == null ) {
|
||||
throw new AnnotationException( "Property '" + qualify( propertyHolder.getPath(), propertyName )
|
||||
+ "' refers to an unknown fetch profile named '" + fetch.profile() + "'" );
|
||||
if ( fetch.profile().equals( HIBERNATE_DEFAULT_PROFILE ) ) {
|
||||
profile = new FetchProfile( HIBERNATE_DEFAULT_PROFILE, ANNOTATIONS );
|
||||
buildingContext.getMetadataCollector().addFetchProfile( profile );
|
||||
}
|
||||
else {
|
||||
throw new AnnotationException( "Property '" + qualify( propertyHolder.getPath(), propertyName )
|
||||
+ "' refers to an unknown fetch profile named '" + fetch.profile() + "'" );
|
||||
}
|
||||
}
|
||||
else if ( profile.getSource() == ANNOTATIONS ) {
|
||||
if ( profile.getSource() == ANNOTATIONS ) {
|
||||
profile.addFetch(
|
||||
new FetchProfile.Fetch(
|
||||
propertyHolder.getEntityName(),
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.hibernate.annotations.OnDeleteAction;
|
|||
import org.hibernate.annotations.common.reflection.XClass;
|
||||
import org.hibernate.annotations.common.reflection.XProperty;
|
||||
import org.hibernate.boot.spi.AccessType;
|
||||
import org.hibernate.boot.spi.InFlightMetadataCollector;
|
||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||
import org.hibernate.boot.spi.PropertyData;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
|
@ -331,19 +332,34 @@ public class ToOneBinder {
|
|||
XProperty property,
|
||||
PropertyHolder propertyHolder,
|
||||
PropertyData inferredData) {
|
||||
final MetadataBuildingContext context = toOne.getBuildingContext();
|
||||
final InFlightMetadataCollector collector = context.getMetadataCollector();
|
||||
if ( property.isAnnotationPresent( FetchProfileOverride.class ) ) {
|
||||
final FetchProfileOverride fetch = property.getAnnotation( FetchProfileOverride.class );
|
||||
final MetadataBuildingContext context = toOne.getBuildingContext();
|
||||
context.getMetadataCollector()
|
||||
.addSecondPass( new FetchSecondPass( fetch, propertyHolder, inferredData.getPropertyName(), context ) );
|
||||
collector.addSecondPass( new FetchSecondPass( fetch, propertyHolder, inferredData.getPropertyName(), context ) );
|
||||
}
|
||||
else if ( property.isAnnotationPresent( FetchProfileOverrides.class ) ) {
|
||||
for ( FetchProfileOverride fetch: property.getAnnotation( FetchProfileOverrides.class ).value() ) {
|
||||
final MetadataBuildingContext context = toOne.getBuildingContext();
|
||||
context.getMetadataCollector()
|
||||
.addSecondPass( new FetchSecondPass( fetch, propertyHolder, inferredData.getPropertyName(), context ) );
|
||||
collector.addSecondPass( new FetchSecondPass( fetch, propertyHolder, inferredData.getPropertyName(), context ) );
|
||||
}
|
||||
}
|
||||
if ( !toOne.isLazy()
|
||||
&& !propertyHolder.isOrWithinEmbeddedId()
|
||||
&& !propertyHolder.isWithinElementCollection()
|
||||
&& !propertyHolder.isInIdClass()
|
||||
// this is a bit of a problem: embeddable classes don't
|
||||
// come with the entity name attached, so we can't
|
||||
// create a Fetch that refers to their fields
|
||||
&& !propertyHolder.isComponent()
|
||||
// not sure exactly what the story is here:
|
||||
&& !collector.isInSecondPass() ) {
|
||||
collector.addSecondPass( new FetchSecondPass(
|
||||
DefaultFetchProfileOverride.INSTANCE,
|
||||
propertyHolder,
|
||||
inferredData.getPropertyName(),
|
||||
context
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleFetch(ToOne toOne, XProperty property) {
|
||||
|
|
|
@ -22,6 +22,11 @@ import static jakarta.persistence.FetchType.EAGER;
|
|||
* @see org.hibernate.engine.profile.FetchProfile
|
||||
*/
|
||||
public class FetchProfile {
|
||||
/**
|
||||
* The name of an implicit fetch profile which includes all eager to-one associations.
|
||||
*/
|
||||
public static final String HIBERNATE_DEFAULT_PROFILE = "org.hibernate.defaultProfile";
|
||||
|
||||
private final String name;
|
||||
private final MetadataSource source;
|
||||
private final LinkedHashSet<Fetch> fetches = new LinkedHashSet<>();
|
||||
|
|
|
@ -7636,7 +7636,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
else if ( getLoadQueryInfluencers().hasEnabledFetchProfiles() ) {
|
||||
// There is no point in checking the fetch profile if it can't affect this fetchable
|
||||
if ( fetchTiming != FetchTiming.IMMEDIATE || fetchable.incrementFetchDepth() ) {
|
||||
// if ( fetchTiming != FetchTiming.IMMEDIATE || fetchable.incrementFetchDepth() ) {
|
||||
final String fetchableRole = fetchable.getNavigableRole().getFullPath();
|
||||
|
||||
for ( String enabledFetchProfileName : getLoadQueryInfluencers()
|
||||
|
@ -7666,7 +7666,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
|||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@SessionFactory
|
||||
@DomainModel(annotatedClasses = {NewFetchTest.class,NewFetchTest.E.class, NewFetchTest.F.class, NewFetchTest.G.class})
|
||||
@DomainModel(annotatedClasses = {NewFetchTest.class,NewFetchTest.E.class, NewFetchTest.F.class, NewFetchTest.G.class, NewFetchTest.H.class})
|
||||
@FetchProfile(name = NewFetchTest.NEW_PROFILE)
|
||||
@FetchProfile(name = NewFetchTest.OLD_PROFILE,
|
||||
fetchOverrides = @FetchProfile.FetchOverride(entity = NewFetchTest.E.class, association = "f"))
|
||||
|
@ -160,6 +160,47 @@ public class NewFetchTest {
|
|||
});
|
||||
}
|
||||
|
||||
@Test void testDefaultProfile(SessionFactoryScope scope) {
|
||||
scope.inTransaction( s-> {
|
||||
G g = new G();
|
||||
H h1 = new H();
|
||||
h1.g = g;
|
||||
H h2 = new H();
|
||||
h2.g = g;
|
||||
s.persist(g);
|
||||
s.persist(h1);
|
||||
s.persist(h2);
|
||||
});
|
||||
scope.getCollectingStatementInspector().assertExecutedCount(6);
|
||||
scope.getCollectingStatementInspector().clear();
|
||||
|
||||
List<H> hs1 = scope.fromSession( s -> {
|
||||
return s.createSelectionQuery("from H", H.class).getResultList();
|
||||
});
|
||||
assertTrue( isInitialized( hs1.get(0).g ) );
|
||||
scope.getCollectingStatementInspector().assertExecutedCount(2);
|
||||
scope.getCollectingStatementInspector().assertNumberOfJoins(0, 0);
|
||||
scope.getCollectingStatementInspector().assertNumberOfJoins(1, 0);
|
||||
|
||||
scope.getCollectingStatementInspector().clear();
|
||||
List<H> hs2 = scope.fromSession( s -> {
|
||||
s.enableFetchProfile( org.hibernate.mapping.FetchProfile.HIBERNATE_DEFAULT_PROFILE );
|
||||
return s.createSelectionQuery("from H", H.class).getResultList();
|
||||
});
|
||||
assertTrue( isInitialized( hs2.get(0).g ) );
|
||||
scope.getCollectingStatementInspector().assertExecutedCount(1);
|
||||
scope.getCollectingStatementInspector().assertNumberOfJoins(0,1);
|
||||
|
||||
scope.getCollectingStatementInspector().clear();
|
||||
List<H> hs3 = scope.fromSession( s -> {
|
||||
s.enableFetchProfile("test");
|
||||
return s.createSelectionQuery("from H", H.class).getResultList();
|
||||
});
|
||||
assertTrue( isInitialized( hs3.get(0).g ) );
|
||||
scope.getCollectingStatementInspector().assertExecutedCount(1);
|
||||
scope.getCollectingStatementInspector().assertNumberOfJoins(0,1);
|
||||
}
|
||||
|
||||
@Entity(name = "E")
|
||||
static class E {
|
||||
@Id @GeneratedValue
|
||||
|
@ -186,4 +227,13 @@ public class NewFetchTest {
|
|||
@Id @GeneratedValue
|
||||
Long id;
|
||||
}
|
||||
|
||||
@FetchProfile(name = "test")
|
||||
@Entity(name = "H")
|
||||
static class H {
|
||||
@Id @GeneratedValue
|
||||
Long id;
|
||||
@FetchProfileOverride(profile = "test", mode = JOIN)
|
||||
@ManyToOne G g;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue