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;
// cache the status of the type
private volatile Type type;
/**
* @deprecated User {@link Component#Component(MetadataBuildingContext, PersistentClass)} instead.
*/
@ -209,13 +212,28 @@ public class Component extends SimpleValue implements MetaAttributable {
@Override
public Type getType() throws MappingException {
// Resolve the type of the value once and for all as this operation generates a proxy class
// for each invocation.
// Unfortunately, there's no better way of doing that as none of the classes are immutable and
// we can't know for sure the current state of the property or the value.
Type localType = type;
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();
return isEmbedded() ? factory.embeddedComponent( metamodel ) : factory.component( metamodel );
localType = isEmbedded() ? factory.embeddedComponent( metamodel ) : factory.component( metamodel );
type = localType;
}
}
}
return localType;
}
@Override

View File

@ -7,11 +7,20 @@
package org.hibernate.test.component.proxy;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
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.mapping.PersistentClass;
import org.hibernate.test.annotations.basic.CollectionAsBasicTest.DelimitedStringsType;
import org.hibernate.testing.TestForIssue;
import org.hibernate.type.ComponentType;
import org.junit.Test;
/**
@ -44,4 +53,28 @@ public class ComponentBasicProxyTest extends BaseEntityManagerFunctionalTestCase
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 );
}
}
}