HHH-16654 much more efficient implementation of default fetch profile
This commit is contained in:
parent
35da6000fd
commit
2b27d98a89
|
@ -1,43 +0,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>.
|
|
||||||
*/
|
|
||||||
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,7 +17,6 @@ import org.hibernate.mapping.FetchProfile;
|
||||||
import org.hibernate.mapping.PersistentClass;
|
import org.hibernate.mapping.PersistentClass;
|
||||||
|
|
||||||
import static org.hibernate.internal.util.StringHelper.qualify;
|
import static org.hibernate.internal.util.StringHelper.qualify;
|
||||||
import static org.hibernate.mapping.FetchProfile.HIBERNATE_DEFAULT_PROFILE;
|
|
||||||
import static org.hibernate.mapping.MetadataSource.ANNOTATIONS;
|
import static org.hibernate.mapping.MetadataSource.ANNOTATIONS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,16 +42,10 @@ public class FetchSecondPass implements SecondPass {
|
||||||
@Override
|
@Override
|
||||||
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
|
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
|
||||||
|
|
||||||
FetchProfile profile = buildingContext.getMetadataCollector().getFetchProfile( fetch.profile() );
|
final FetchProfile profile = buildingContext.getMetadataCollector().getFetchProfile( fetch.profile() );
|
||||||
if ( profile == null ) {
|
if ( profile == null ) {
|
||||||
if ( fetch.profile().equals( HIBERNATE_DEFAULT_PROFILE ) ) {
|
throw new AnnotationException( "Property '" + qualify( propertyHolder.getPath(), propertyName )
|
||||||
profile = new FetchProfile( HIBERNATE_DEFAULT_PROFILE, ANNOTATIONS );
|
+ "' refers to an unknown fetch profile named '" + fetch.profile() + "'" );
|
||||||
buildingContext.getMetadataCollector().addFetchProfile( profile );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new AnnotationException( "Property '" + qualify( propertyHolder.getPath(), propertyName )
|
|
||||||
+ "' refers to an unknown fetch profile named '" + fetch.profile() + "'" );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if ( profile.getSource() == ANNOTATIONS ) {
|
if ( profile.getSource() == ANNOTATIONS ) {
|
||||||
profile.addFetch(
|
profile.addFetch(
|
||||||
|
|
|
@ -343,23 +343,6 @@ public class ToOneBinder {
|
||||||
collector.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) {
|
private static void handleFetch(ToOne toOne, XProperty property) {
|
||||||
|
|
|
@ -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.engine.profile;
|
||||||
|
|
||||||
|
import org.hibernate.metamodel.RuntimeMetamodels;
|
||||||
|
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.sql.results.graph.FetchOptions;
|
||||||
|
import org.hibernate.tuple.NonIdentifierAttribute;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.hibernate.engine.FetchStyle.SUBSELECT;
|
||||||
|
import static org.hibernate.engine.FetchTiming.IMMEDIATE;
|
||||||
|
import static org.hibernate.engine.FetchStyle.JOIN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Gavin King
|
||||||
|
*/
|
||||||
|
public class DefaultFetchProfile extends 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 RuntimeMetamodels metamodels;
|
||||||
|
|
||||||
|
public DefaultFetchProfile(RuntimeMetamodels metamodels) {
|
||||||
|
super(HIBERNATE_DEFAULT_PROFILE);
|
||||||
|
this.metamodels = metamodels;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Fetch getFetchByRole(String role) {
|
||||||
|
final int last = role.lastIndexOf('.');
|
||||||
|
final String entityName = role.substring( 0, last );
|
||||||
|
final String property = role.substring( last + 1 );
|
||||||
|
final EntityMappingType entity = metamodels.getEntityMappingType( entityName );
|
||||||
|
if ( entity != null ) {
|
||||||
|
final AttributeMapping attributeMapping = entity.findAttributeMapping( property );
|
||||||
|
if ( attributeMapping != null && !attributeMapping.isPluralAttributeMapping() ) {
|
||||||
|
final FetchOptions fetchOptions = attributeMapping.getMappedFetchOptions();
|
||||||
|
if ( fetchOptions.getStyle() == JOIN && fetchOptions.getTiming() == IMMEDIATE ) {
|
||||||
|
return new Fetch( new Association( entity.getEntityPersister(), role ), JOIN, IMMEDIATE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.getFetchByRole( role );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasSubselectLoadableCollectionsEnabled(EntityPersister persister) {
|
||||||
|
final EntityMappingType entity = metamodels.getEntityMappingType( persister.getEntityName() );
|
||||||
|
for ( AttributeMapping attributeMapping : entity.getAttributeMappings() ) {
|
||||||
|
if ( attributeMapping.getMappedFetchOptions().getStyle() == SUBSELECT ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Fetch> getFetches() {
|
||||||
|
throw new UnsupportedOperationException( "DefaultFetchProfile has implicit fetches" );
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,10 +12,12 @@ import java.util.Map;
|
||||||
import org.hibernate.Internal;
|
import org.hibernate.Internal;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.type.BagType;
|
import org.hibernate.type.BagType;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
import static org.hibernate.engine.FetchStyle.JOIN;
|
import static org.hibernate.engine.FetchStyle.JOIN;
|
||||||
|
import static org.hibernate.engine.FetchStyle.SUBSELECT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The runtime representation of a Hibernate
|
* The runtime representation of a Hibernate
|
||||||
|
@ -181,4 +183,14 @@ public class FetchProfile {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "FetchProfile[" + name + "]";
|
return "FetchProfile[" + name + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasSubselectLoadableCollectionsEnabled(EntityPersister persister) {
|
||||||
|
for ( Fetch fetch : getFetches().values() ) {
|
||||||
|
if ( fetch.getMethod() == SUBSELECT
|
||||||
|
&& fetch.getAssociation().getOwner() == persister ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -344,12 +344,9 @@ public class LoadQueryInfluencers implements Serializable {
|
||||||
private boolean hasSubselectLoadableCollectionsEnabledInProfile(EntityPersister persister) {
|
private boolean hasSubselectLoadableCollectionsEnabledInProfile(EntityPersister persister) {
|
||||||
if ( hasEnabledFetchProfiles() ) {
|
if ( hasEnabledFetchProfiles() ) {
|
||||||
for ( String profile : getEnabledFetchProfileNames() ) {
|
for ( String profile : getEnabledFetchProfileNames() ) {
|
||||||
final FetchProfile fetchProfile = sessionFactory.getFetchProfile( profile );
|
if ( sessionFactory.getFetchProfile( profile )
|
||||||
for ( Fetch fetch : fetchProfile.getFetches().values() ) {
|
.hasSubselectLoadableCollectionsEnabled( persister ) ) {
|
||||||
// TODO: check that it's relevant to this persister??
|
return true;
|
||||||
if ( fetch.getMethod() == SUBSELECT ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,12 @@ import org.hibernate.boot.spi.MetadataImplementor;
|
||||||
import org.hibernate.engine.FetchStyle;
|
import org.hibernate.engine.FetchStyle;
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
import org.hibernate.engine.profile.Association;
|
import org.hibernate.engine.profile.Association;
|
||||||
|
import org.hibernate.engine.profile.DefaultFetchProfile;
|
||||||
import org.hibernate.engine.profile.Fetch;
|
import org.hibernate.engine.profile.Fetch;
|
||||||
import org.hibernate.engine.profile.FetchProfile;
|
import org.hibernate.engine.profile.FetchProfile;
|
||||||
import org.hibernate.engine.profile.internal.FetchProfileAffectee;
|
import org.hibernate.engine.profile.internal.FetchProfileAffectee;
|
||||||
import org.hibernate.metamodel.MappingMetamodel;
|
import org.hibernate.metamodel.MappingMetamodel;
|
||||||
|
import org.hibernate.metamodel.RuntimeMetamodels;
|
||||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
|
@ -23,6 +25,8 @@ import org.hibernate.persister.entity.EntityPersister;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static org.hibernate.engine.profile.DefaultFetchProfile.HIBERNATE_DEFAULT_PROFILE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create {@link FetchProfile} references from {@link org.hibernate.mapping.FetchProfile} references
|
* Create {@link FetchProfile} references from {@link org.hibernate.mapping.FetchProfile} references
|
||||||
*
|
*
|
||||||
|
@ -32,12 +36,13 @@ public class FetchProfileHelper {
|
||||||
|
|
||||||
public static Map<String, FetchProfile> getFetchProfiles(
|
public static Map<String, FetchProfile> getFetchProfiles(
|
||||||
MetadataImplementor bootMetamodel,
|
MetadataImplementor bootMetamodel,
|
||||||
MappingMetamodel mappingMetamodel) {
|
RuntimeMetamodels runtimeMetamodels) {
|
||||||
final Map<String, FetchProfile> fetchProfiles = new HashMap<>();
|
final Map<String, FetchProfile> fetchProfiles = new HashMap<>();
|
||||||
for ( org.hibernate.mapping.FetchProfile mappingProfile : bootMetamodel.getFetchProfiles() ) {
|
for ( org.hibernate.mapping.FetchProfile mappingProfile : bootMetamodel.getFetchProfiles() ) {
|
||||||
final FetchProfile fetchProfile = createFetchProfile( mappingMetamodel, mappingProfile );
|
final FetchProfile fetchProfile = createFetchProfile( runtimeMetamodels.getMappingMetamodel(), mappingProfile );
|
||||||
fetchProfiles.put( fetchProfile.getName(), fetchProfile );
|
fetchProfiles.put( fetchProfile.getName(), fetchProfile );
|
||||||
}
|
}
|
||||||
|
fetchProfiles.put( HIBERNATE_DEFAULT_PROFILE, new DefaultFetchProfile( runtimeMetamodels ) );
|
||||||
return fetchProfiles;
|
return fetchProfiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -272,7 +272,7 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im
|
||||||
|
|
||||||
// this needs to happen after the mapping metamodel is
|
// this needs to happen after the mapping metamodel is
|
||||||
// completely built, since we need to use the persisters
|
// completely built, since we need to use the persisters
|
||||||
fetchProfiles = getFetchProfiles( bootMetamodel, runtimeMetamodels.getMappingMetamodel() );
|
fetchProfiles = getFetchProfiles( bootMetamodel, runtimeMetamodels );
|
||||||
|
|
||||||
defaultSessionOpenOptions = createDefaultSessionOpenOptionsIfPossible();
|
defaultSessionOpenOptions = createDefaultSessionOpenOptionsIfPossible();
|
||||||
temporarySessionOpenOptions = defaultSessionOpenOptions == null ? null : buildTemporarySessionOpenOptions();
|
temporarySessionOpenOptions = defaultSessionOpenOptions == null ? null : buildTemporarySessionOpenOptions();
|
||||||
|
|
|
@ -22,10 +22,6 @@ import static jakarta.persistence.FetchType.EAGER;
|
||||||
* @see org.hibernate.engine.profile.FetchProfile
|
* @see org.hibernate.engine.profile.FetchProfile
|
||||||
*/
|
*/
|
||||||
public class 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 String name;
|
||||||
private final MetadataSource source;
|
private final MetadataSource source;
|
||||||
|
|
|
@ -35,6 +35,7 @@ import jakarta.persistence.FlushModeType;
|
||||||
import jakarta.persistence.LockModeType;
|
import jakarta.persistence.LockModeType;
|
||||||
import jakarta.persistence.Parameter;
|
import jakarta.persistence.Parameter;
|
||||||
import jakarta.persistence.TemporalType;
|
import jakarta.persistence.TemporalType;
|
||||||
|
import org.hibernate.engine.profile.DefaultFetchProfile;
|
||||||
import org.hibernate.graph.GraphSemantic;
|
import org.hibernate.graph.GraphSemantic;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,7 +79,7 @@ import org.hibernate.graph.GraphSemantic;
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* The special built-in fetch profile named
|
* The special built-in fetch profile named
|
||||||
* {@value org.hibernate.mapping.FetchProfile#HIBERNATE_DEFAULT_PROFILE} adds
|
* {@value DefaultFetchProfile#HIBERNATE_DEFAULT_PROFILE} adds
|
||||||
* a fetch join for every {@link jakarta.persistence.FetchType#EAGER eager}
|
* a fetch join for every {@link jakarta.persistence.FetchType#EAGER eager}
|
||||||
* {@code @ManyToOne} or {@code @OneToOne} association belonging to an entity
|
* {@code @ManyToOne} or {@code @OneToOne} association belonging to an entity
|
||||||
* returned by the query.
|
* returned by the query.
|
||||||
|
|
|
@ -21,6 +21,7 @@ import static org.hibernate.Hibernate.isInitialized;
|
||||||
import static org.hibernate.annotations.FetchMode.JOIN;
|
import static org.hibernate.annotations.FetchMode.JOIN;
|
||||||
import static org.hibernate.annotations.FetchMode.SELECT;
|
import static org.hibernate.annotations.FetchMode.SELECT;
|
||||||
import static org.hibernate.annotations.FetchMode.SUBSELECT;
|
import static org.hibernate.annotations.FetchMode.SUBSELECT;
|
||||||
|
import static org.hibernate.engine.profile.DefaultFetchProfile.HIBERNATE_DEFAULT_PROFILE;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@ -210,7 +211,7 @@ public class NewFetchTest {
|
||||||
|
|
||||||
scope.getCollectingStatementInspector().clear();
|
scope.getCollectingStatementInspector().clear();
|
||||||
List<H> hs2 = scope.fromSession( s -> {
|
List<H> hs2 = scope.fromSession( s -> {
|
||||||
s.enableFetchProfile( org.hibernate.mapping.FetchProfile.HIBERNATE_DEFAULT_PROFILE );
|
s.enableFetchProfile( HIBERNATE_DEFAULT_PROFILE );
|
||||||
return s.createSelectionQuery("from H", H.class).getResultList();
|
return s.createSelectionQuery("from H", H.class).getResultList();
|
||||||
});
|
});
|
||||||
assertTrue( isInitialized( hs2.get(0).g ) );
|
assertTrue( isInitialized( hs2.get(0).g ) );
|
||||||
|
|
Loading…
Reference in New Issue