HHH-13105 - Add registries for user-defined Types as currently a custom Type is instantiated and configured 9 times
This commit is contained in:
parent
dab50a6d8a
commit
903d542020
|
@ -8,11 +8,16 @@ package org.hibernate.type;
|
|||
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.SessionFactoryObserver;
|
||||
import org.hibernate.classic.Lifecycle;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.ReflectHelper;
|
||||
import org.hibernate.tuple.component.ComponentMetamodel;
|
||||
|
@ -38,7 +43,7 @@ import static org.hibernate.internal.CoreLogging.messageLogger;
|
|||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public final class TypeFactory implements Serializable {
|
||||
public final class TypeFactory implements Serializable, SessionFactoryObserver {
|
||||
private static final CoreMessageLogger LOG = messageLogger( TypeFactory.class );
|
||||
|
||||
/**
|
||||
|
@ -52,35 +57,47 @@ public final class TypeFactory implements Serializable {
|
|||
private final TypeConfiguration typeConfiguration;
|
||||
private final TypeScope typeScope;
|
||||
|
||||
private final Map<TypeKey, Type> typeRegistry = new HashMap<>();
|
||||
private final Map<TypeKey, CompositeCustomType> compositeTypeRegistry = new HashMap<>();
|
||||
private final Map<TypeKey, CustomType> customTypeRegistry = new HashMap<>();
|
||||
private final Map<TypeKey, EntityType> entityTypeRegistry = new HashMap<>();
|
||||
private final Map<TypeKey, SerializableType> serializableTypeRegistry = new HashMap<>();
|
||||
|
||||
public TypeFactory(TypeConfiguration typeConfiguration) {
|
||||
this.typeConfiguration = typeConfiguration;
|
||||
this.typeScope = (TypeScope) () -> typeConfiguration;
|
||||
}
|
||||
|
||||
public SessionFactoryImplementor resolveSessionFactory() {
|
||||
return typeConfiguration.getSessionFactory();
|
||||
@Override
|
||||
public void sessionFactoryCreated(SessionFactory factory) {
|
||||
typeRegistry.clear();
|
||||
compositeTypeRegistry.clear();
|
||||
customTypeRegistry.clear();
|
||||
entityTypeRegistry.clear();
|
||||
serializableTypeRegistry.clear();
|
||||
}
|
||||
|
||||
public Type byClass(Class clazz, Properties parameters) {
|
||||
TypeKey typeKey = new TypeKey( clazz, parameters );
|
||||
if ( Type.class.isAssignableFrom( clazz ) ) {
|
||||
return type( clazz, parameters );
|
||||
return typeRegistry.computeIfAbsent( typeKey, key -> type( clazz, parameters ) );
|
||||
}
|
||||
|
||||
if ( CompositeUserType.class.isAssignableFrom( clazz ) ) {
|
||||
return customComponent( clazz, parameters );
|
||||
return compositeTypeRegistry.computeIfAbsent( typeKey, key -> customComponent( clazz, parameters ) );
|
||||
}
|
||||
|
||||
if ( UserType.class.isAssignableFrom( clazz ) ) {
|
||||
return custom( clazz, parameters );
|
||||
return customTypeRegistry.computeIfAbsent( typeKey, key -> custom( clazz, parameters ) );
|
||||
}
|
||||
|
||||
if ( Lifecycle.class.isAssignableFrom( clazz ) ) {
|
||||
// not really a many-to-one association *necessarily*
|
||||
return manyToOne( clazz.getName() );
|
||||
return entityTypeRegistry.computeIfAbsent( typeKey, key -> manyToOne( clazz.getName() ) );
|
||||
}
|
||||
|
||||
if ( Serializable.class.isAssignableFrom( clazz ) ) {
|
||||
return serializable( clazz );
|
||||
return serializableTypeRegistry.computeIfAbsent( typeKey, key -> serializable( clazz ) );
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -404,4 +421,34 @@ public final class TypeFactory implements Serializable {
|
|||
public Type any(Type metaType, Type identifierType) {
|
||||
return new AnyType( typeScope, metaType, identifierType );
|
||||
}
|
||||
|
||||
private static class TypeKey {
|
||||
private final Class clazz;
|
||||
private final Properties parameters = new Properties();
|
||||
|
||||
private TypeKey(Class clazz, Properties parameters) {
|
||||
this.clazz = clazz;
|
||||
if ( parameters != null && !parameters.isEmpty() ) {
|
||||
this.parameters.putAll( parameters );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
TypeKey typeKey = (TypeKey) o;
|
||||
return Objects.equals( clazz, typeKey.clazz ) &&
|
||||
Objects.equals( parameters, typeKey.parameters );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( clazz, parameters );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -166,6 +166,7 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable {
|
|||
|
||||
scope.setSessionFactory( sessionFactory );
|
||||
sessionFactory.addObserver( this );
|
||||
sessionFactory.addObserver( this.typeFactory );
|
||||
return new MetamodelImpl( sessionFactory, this );
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
package org.hibernate.test.type.contributor;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
|
||||
import org.hibernate.type.DiscriminatorType;
|
||||
import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class ArraySpyType
|
||||
extends AbstractSingleColumnStandardBasicType<Array>
|
||||
implements DiscriminatorType<Array> {
|
||||
|
||||
private static int instanceCount;
|
||||
|
||||
public ArraySpyType() {
|
||||
super( VarcharTypeDescriptor.INSTANCE, ArrayTypeDescriptor.INSTANCE );
|
||||
instanceCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Array stringToObject(String xml) throws Exception {
|
||||
return fromString( xml );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String objectToSQLString(Array value, Dialect dialect) throws Exception {
|
||||
return toString( value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "comma-separated-array";
|
||||
}
|
||||
|
||||
public static int getInstanceCount() {
|
||||
return instanceCount;
|
||||
}
|
||||
|
||||
public static void resetInstanceCount() {
|
||||
instanceCount = 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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.test.type.contributor;
|
||||
|
||||
import java.util.List;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.annotations.Type;
|
||||
import org.hibernate.annotations.TypeDef;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@TestForIssue( jiraKey = "HHH-13105" )
|
||||
public class CustomTypeInstanceCountTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { CorporateUser.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildEntityManagerFactory() {
|
||||
ArraySpyType.resetInstanceCount();
|
||||
assertEquals( 0, ArraySpyType.getInstanceCount() );
|
||||
super.buildEntityManagerFactory();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
CorporateUser user = new CorporateUser();
|
||||
user.setUserName( "Vlad" );
|
||||
entityManager.persist( user );
|
||||
|
||||
user.getEmailAddresses().add( "vlad@hibernate.info" );
|
||||
user.getEmailAddresses().add( "vlad@hibernate.net" );
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
List<CorporateUser> users = entityManager.createQuery(
|
||||
"select u from CorporateUser u", CorporateUser.class )
|
||||
.getResultList();
|
||||
|
||||
assertEquals( 1, users.size() );
|
||||
} );
|
||||
|
||||
assertEquals( 1, ArraySpyType.getInstanceCount() );
|
||||
}
|
||||
|
||||
@Entity(name = "CorporateUser")
|
||||
@TypeDef(typeClass = ArraySpyType.class, defaultForType = Array.class)
|
||||
public static class CorporateUser {
|
||||
|
||||
@Id
|
||||
private String userName;
|
||||
|
||||
private Array emailAddresses = new Array();
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public Array getEmailAddresses() {
|
||||
return emailAddresses;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue