HHH-12791 Cache the Component type to avoid generating one proxy per call

While Javassist only generates one proxy as the name is stable,
ByteBuddy uses random names and thus generates a new proxy for every
call, leading to the generation of 18 different proxies for the other
test of the test class.

We can't do better than using a volatile/synchronized pattern as the
Component is not fully initialized in the constructor.

Maybe we could take the risk of admitting that the getType() method is
called at least once before we pass the element to a multi-threaded
environment but that's a bet I don't want to take alone.
This commit is contained in:
Guillaume Smet 2018-07-12 19:03:04 +02:00
parent f6e8dc9845
commit 1945180569
2 changed files with 61 additions and 10 deletions

View File

@ -52,6 +52,9 @@ public class Component extends SimpleValue implements MetaAttributable {
private java.util.Map<EntityMode,String> tuplizerImpls; private java.util.Map<EntityMode,String> tuplizerImpls;
// cache the status of the type
private volatile Type type;
/** /**
* @deprecated User {@link Component#Component(MetadataBuildingContext, PersistentClass)} instead. * @deprecated User {@link Component#Component(MetadataBuildingContext, PersistentClass)} instead.
*/ */
@ -209,13 +212,28 @@ public class Component extends SimpleValue implements MetaAttributable {
@Override @Override
public Type getType() throws MappingException { public Type getType() throws MappingException {
// TODO : temporary initial step towards HHH-1907 // Resolve the type of the value once and for all as this operation generates a proxy class
final ComponentMetamodel metamodel = new ComponentMetamodel( // for each invocation.
this, // Unfortunately, there's no better way of doing that as none of the classes are immutable and
getMetadata().getMetadataBuildingOptions() // we can't know for sure the current state of the property or the value.
); Type localType = type;
final TypeFactory factory = getMetadata().getTypeConfiguration().getTypeResolver().getTypeFactory();
return isEmbedded() ? factory.embeddedComponent( metamodel ) : factory.component( metamodel ); if ( localType == null ) {
synchronized ( this ) {
if ( type == null ) {
// TODO : temporary initial step towards HHH-1907
final ComponentMetamodel metamodel = new ComponentMetamodel(
this,
getMetadata().getMetadataBuildingOptions()
);
final TypeFactory factory = getMetadata().getTypeConfiguration().getTypeResolver().getTypeFactory();
localType = isEmbedded() ? factory.embeddedComponent( metamodel ) : factory.component( metamodel );
type = localType;
}
}
}
return localType;
} }
@Override @Override
@ -288,15 +306,15 @@ public class Component extends SimpleValue implements MetaAttributable {
} }
return result; return result;
} }
public boolean isKey() { public boolean isKey() {
return isKey; return isKey;
} }
public void setKey(boolean isKey) { public void setKey(boolean isKey) {
this.isKey = isKey; this.isKey = isKey;
} }
public boolean hasPojoRepresentation() { public boolean hasPojoRepresentation() {
return componentClassName!=null; return componentClassName!=null;
} }

View File

@ -7,11 +7,20 @@
package org.hibernate.test.component.proxy; package org.hibernate.test.component.proxy;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
import java.util.List; import java.util.List;
import org.hibernate.EntityMode;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.test.annotations.basic.CollectionAsBasicTest.DelimitedStringsType;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.type.ComponentType;
import org.junit.Test; import org.junit.Test;
/** /**
@ -44,4 +53,28 @@ public class ComponentBasicProxyTest extends BaseEntityManagerFunctionalTestCase
entityManager.remove( adult ); entityManager.remove( adult );
} ); } );
} }
@Test
@TestForIssue(jiraKey = "HHH-12791")
public void testOnlyOneProxyClassGenerated() {
StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
try {
Metadata metadata = new MetadataSources( ssr ).addAnnotatedClass( Person.class )
.getMetadataBuilder().applyBasicType( new DelimitedStringsType() )
.build();
PersistentClass persistentClass = metadata.getEntityBinding( Person.class.getName() );
ComponentType componentType1 = (ComponentType) persistentClass.getIdentifierMapper().getType();
Object instance1 = componentType1.instantiate( EntityMode.POJO );
ComponentType componentType2 = (ComponentType) persistentClass.getIdentifierMapper().getType();
Object instance2 = componentType2.instantiate( EntityMode.POJO );
assertEquals( instance1.getClass(), instance2.getClass() );
}
finally {
StandardServiceRegistryBuilder.destroy( ssr );
}
}
} }