mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-03-01 07:19:15 +00:00
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 @@
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
|
import org.hibernate.SessionFactory;
|
||||||
|
import org.hibernate.SessionFactoryObserver;
|
||||||
import org.hibernate.classic.Lifecycle;
|
import org.hibernate.classic.Lifecycle;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.ReflectHelper;
|
import org.hibernate.internal.util.ReflectHelper;
|
||||||
import org.hibernate.tuple.component.ComponentMetamodel;
|
import org.hibernate.tuple.component.ComponentMetamodel;
|
||||||
@ -38,7 +43,7 @@
|
|||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@SuppressWarnings({"unchecked"})
|
@SuppressWarnings({"unchecked"})
|
||||||
public final class TypeFactory implements Serializable {
|
public final class TypeFactory implements Serializable, SessionFactoryObserver {
|
||||||
private static final CoreMessageLogger LOG = messageLogger( TypeFactory.class );
|
private static final CoreMessageLogger LOG = messageLogger( TypeFactory.class );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,35 +57,47 @@ public interface TypeScope extends Serializable {
|
|||||||
private final TypeConfiguration typeConfiguration;
|
private final TypeConfiguration typeConfiguration;
|
||||||
private final TypeScope typeScope;
|
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) {
|
public TypeFactory(TypeConfiguration typeConfiguration) {
|
||||||
this.typeConfiguration = typeConfiguration;
|
this.typeConfiguration = typeConfiguration;
|
||||||
this.typeScope = (TypeScope) () -> typeConfiguration;
|
this.typeScope = (TypeScope) () -> typeConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SessionFactoryImplementor resolveSessionFactory() {
|
@Override
|
||||||
return typeConfiguration.getSessionFactory();
|
public void sessionFactoryCreated(SessionFactory factory) {
|
||||||
|
typeRegistry.clear();
|
||||||
|
compositeTypeRegistry.clear();
|
||||||
|
customTypeRegistry.clear();
|
||||||
|
entityTypeRegistry.clear();
|
||||||
|
serializableTypeRegistry.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Type byClass(Class clazz, Properties parameters) {
|
public Type byClass(Class clazz, Properties parameters) {
|
||||||
|
TypeKey typeKey = new TypeKey( clazz, parameters );
|
||||||
if ( Type.class.isAssignableFrom( clazz ) ) {
|
if ( Type.class.isAssignableFrom( clazz ) ) {
|
||||||
return type( clazz, parameters );
|
return typeRegistry.computeIfAbsent( typeKey, key -> type( clazz, parameters ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( CompositeUserType.class.isAssignableFrom( clazz ) ) {
|
if ( CompositeUserType.class.isAssignableFrom( clazz ) ) {
|
||||||
return customComponent( clazz, parameters );
|
return compositeTypeRegistry.computeIfAbsent( typeKey, key -> customComponent( clazz, parameters ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( UserType.class.isAssignableFrom( clazz ) ) {
|
if ( UserType.class.isAssignableFrom( clazz ) ) {
|
||||||
return custom( clazz, parameters );
|
return customTypeRegistry.computeIfAbsent( typeKey, key -> custom( clazz, parameters ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( Lifecycle.class.isAssignableFrom( clazz ) ) {
|
if ( Lifecycle.class.isAssignableFrom( clazz ) ) {
|
||||||
// not really a many-to-one association *necessarily*
|
// 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 ) ) {
|
if ( Serializable.class.isAssignableFrom( clazz ) ) {
|
||||||
return serializable( clazz );
|
return serializableTypeRegistry.computeIfAbsent( typeKey, key -> serializable( clazz ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -404,4 +421,34 @@ public EmbeddedComponentType embeddedComponent(ComponentMetamodel metamodel) {
|
|||||||
public Type any(Type metaType, Type identifierType) {
|
public Type any(Type metaType, Type identifierType) {
|
||||||
return new AnyType( typeScope, metaType, 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 MetamodelImplementor scope(SessionFactoryImplementor sessionFactory) {
|
|||||||
|
|
||||||
scope.setSessionFactory( sessionFactory );
|
scope.setSessionFactory( sessionFactory );
|
||||||
sessionFactory.addObserver( this );
|
sessionFactory.addObserver( this );
|
||||||
|
sessionFactory.addObserver( this.typeFactory );
|
||||||
return new MetamodelImpl( sessionFactory, this );
|
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…
x
Reference in New Issue
Block a user