HHH-17946 - Avoid creating ProxyFactory if possible

This commit is contained in:
Steve Ebersole 2024-04-11 15:12:37 -05:00
parent 44aec90538
commit f68f6aae50
2 changed files with 143 additions and 13 deletions

View File

@ -138,30 +138,60 @@ public class EntityRepresentationStrategyPojoStandard implements EntityRepresent
identifierPropertyAccess = makePropertyAccess( identifierProperty );
}
final BytecodeProvider bytecodeProvider =
creationContext.getBootstrapContext().getServiceRegistry()
.requireService( BytecodeProvider.class );
this.strategySelector = creationContext.getServiceRegistry().getService( StrategySelector.class );
final BytecodeProvider bytecodeProvider = creationContext.getBootstrapContext().getServiceRegistry().requireService( BytecodeProvider.class );
this.proxyFactory = resolveProxyFactory(
bootDescriptor,
runtimeDescriptor,
proxyJtd,
bytecodeProvider,
creationContext
);
this.propertyAccessMap = buildPropertyAccessMap( bootDescriptor );
this.reflectionOptimizer = resolveReflectionOptimizer( bytecodeProvider );
this.instantiator = determineInstantiator( bootDescriptor, runtimeDescriptor.getEntityMetamodel() );
}
@SuppressWarnings("removal")
private ProxyFactory resolveProxyFactory(
PersistentClass bootDescriptor,
EntityPersister entityPersister,
JavaType<?> proxyJtd,
BytecodeProvider bytecodeProvider,
RuntimeModelCreationContext creationContext) {
final EntityMetamodel entityMetamodel = entityPersister.getEntityMetamodel();
final boolean enhancedForLazyLoading = entityPersister.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading();
// todo : `@ConcreteProxy` handling
if ( enhancedForLazyLoading
&& bootDescriptor.getRootClass() == bootDescriptor
&& !bootDescriptor.hasSubclasses() ) {
// the entity is bytecode enhanced for lazy loading and is not part of an inheritance hierarchy,
// so no need for a ProxyFactory
return null;
}
final EntityMetamodel entityMetamodel = runtimeDescriptor.getEntityMetamodel();
ProxyFactory proxyFactory = null;
if ( proxyJtd != null && entityMetamodel.isLazy() ) {
proxyFactory = createProxyFactory( bootDescriptor, bytecodeProvider, creationContext );
final ProxyFactory proxyFactory = createProxyFactory( bootDescriptor, bytecodeProvider, creationContext );
if ( proxyFactory == null ) {
entityMetamodel.setLazy( false );
}
return proxyFactory;
}
this.proxyFactory = proxyFactory;
// resolveReflectionOptimizer may lead to a makePropertyAccess call which requires strategySelector
this.strategySelector = creationContext.getServiceRegistry().getService( StrategySelector.class );
return null;
}
private Map<String, PropertyAccess> buildPropertyAccessMap(PersistentClass bootDescriptor) {
final Map<String, PropertyAccess> propertyAccessMap = new LinkedHashMap<>();
for ( Property property : bootDescriptor.getPropertyClosure() ) {
propertyAccessMap.put( property.getName(), makePropertyAccess( property ) );
}
this.propertyAccessMap = propertyAccessMap;
this.reflectionOptimizer = resolveReflectionOptimizer( bytecodeProvider );
this.instantiator = determineInstantiator( bootDescriptor, entityMetamodel );
return propertyAccessMap;
}
private EntityInstantiator determineInstantiator(PersistentClass bootDescriptor, EntityMetamodel entityMetamodel) {

View File

@ -0,0 +1,100 @@
/*
* 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.orm.test.bytecode.enhancement.lazy.proxy;
import org.hibernate.Hibernate;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.model.internal.ToOneBinder;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.metamodel.internal.EntityRepresentationStrategyPojoStandard;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.hibernate.testing.logger.LoggerInspectionRule;
import org.hibernate.testing.logger.Triggerable;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.jboss.logging.Logger;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for cases where we do not want a {@linkplain org.hibernate.proxy.ProxyFactory}
*
* @author Steve Ebersole
*/
@RunWith(BytecodeEnhancerRunner.class )
@EnhancementOptions(lazyLoading = true)
public class NoProxyFactoryTests extends BaseNonConfigCoreFunctionalTestCase {
@Rule
public LoggerInspectionRule logInspection = new LoggerInspectionRule(
Logger.getMessageLogger( CoreMessageLogger.class, EntityRepresentationStrategyPojoStandard.class.getName() )
);
/**
* See org.hibernate.internal.CoreMessageLogger#unableToCreateProxyFactory
*/
private final Triggerable triggerable = logInspection.watchForLogMessages( "HHH000305" );
@Override
protected void applyMetadataSources(MetadataSources sources) {
sources.addAnnotatedClasses( SimpleEntity.class );
}
@Test
public void testNoInheritance() {
assertThat( triggerable.wasTriggered() )
.describedAs( "Warning was logged" )
.isFalse();
final MappingMetamodelImplementor mappingMetamodel = sessionFactory().getMappingMetamodel();
final EntityPersister entityDescriptor = mappingMetamodel.findEntityDescriptor( SimpleEntity.class );
assertThat( entityDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() ).isTrue();
assertThat( entityDescriptor.getRepresentationStrategy().getProxyFactory() ).isNull();
inTransaction( (session) -> {
final SimpleEntity reference = session.getReference( SimpleEntity.class, 1 );
assertThat( Hibernate.isInitialized( reference ) ).isFalse();
assertThat( reference ).isNotInstanceOf( HibernateProxy.class );
} );
}
@Entity(name="SimpleEntity")
@Table(name="SimpleEntity")
public static class SimpleEntity {
@Id
private Integer id;
private String name;
public final Integer getId() {
return id;
}
public final void setId(Integer id) {
this.id = id;
}
public final String getName() {
return name;
}
public final void setName(String name) {
this.name = name;
}
}
}