All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.type.spi.TypeConfiguration Maven / Gradle / Ivy

There is a newer version: 7.0.0.Beta1
Show newest version
/*
 * 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 .
 */
package org.hibernate.type.spi;

import java.io.InvalidObjectException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Types;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

import org.hibernate.HibernateException;
import org.hibernate.Incubating;
import org.hibernate.SessionFactory;
import org.hibernate.SessionFactoryObserver;
import org.hibernate.boot.cfgxml.spi.CfgXmlAccessService;
import org.hibernate.boot.spi.BasicTypeRegistration;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.id.uuid.LocalObjectUuidHelper;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.SessionFactoryRegistry;
import org.hibernate.metamodel.model.domain.internal.DomainMetamodelImpl;
import org.hibernate.metamodel.spi.DomainMetamodel;
import org.hibernate.query.BinaryArithmeticOperator;
import org.hibernate.query.internal.QueryHelper;
import org.hibernate.query.sqm.SqmExpressable;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
import org.hibernate.type.descriptor.sql.spi.SqlTypeDescriptorRegistry;
import org.hibernate.type.internal.StandardBasicTypeImpl;
import org.hibernate.type.internal.TypeConfigurationRegistry;

import static org.hibernate.internal.CoreLogging.messageLogger;
import static org.hibernate.query.BinaryArithmeticOperator.DIVIDE;

/**
 * Defines a set of available Type instances as isolated from other configurations.  The
 * isolation is defined by each instance of a TypeConfiguration.
 * 

* Note that each Type is inherently "scoped" to a TypeConfiguration. We only ever access * a Type through a TypeConfiguration - specifically the TypeConfiguration in effect for * the current persistence unit. *

* Even though each Type instance is scoped to a TypeConfiguration, Types do not inherently * have access to that TypeConfiguration (mainly because Type is an extension contract - meaning * that Hibernate does not manage the full set of Types available in ever TypeConfiguration). * However Types will often want access to the TypeConfiguration, which can be achieved by the * Type simply implementing the {@link TypeConfigurationAware} interface. * * @author Steve Ebersole * * @since 5.3 */ @Incubating public class TypeConfiguration implements SessionFactoryObserver, Serializable { private static final CoreMessageLogger log = messageLogger( Scope.class ); private final String uuid = LocalObjectUuidHelper.generateLocalObjectUuid(); private final Scope scope; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // things available during both boot and runtime lifecycle phases private final transient JavaTypeDescriptorRegistry javaTypeDescriptorRegistry; private final transient SqlTypeDescriptorRegistry sqlTypeDescriptorRegistry; private final transient BasicTypeRegistry basicTypeRegistry; private final transient Map> jdbcToHibernateTypeContributionMap = new HashMap<>(); public TypeConfiguration() { this.scope = new Scope( this ); this.javaTypeDescriptorRegistry = new JavaTypeDescriptorRegistry( this ); this.sqlTypeDescriptorRegistry = new SqlTypeDescriptorRegistry( this ); this.basicTypeRegistry = new BasicTypeRegistry( this ); StandardBasicTypes.prime( this ); TypeConfigurationRegistry.INSTANCE.registerTypeConfiguration( this ); } public String getUuid() { return uuid; } public BasicTypeRegistry getBasicTypeRegistry() { return basicTypeRegistry; } public JavaTypeDescriptorRegistry getJavaTypeDescriptorRegistry() { return javaTypeDescriptorRegistry; } public SqlTypeDescriptorRegistry getSqlTypeDescriptorRegistry() { return sqlTypeDescriptorRegistry; } public SqlTypeDescriptorIndicators getCurrentBaseSqlTypeIndicators() { return scope.getCurrentBaseSqlTypeIndicators(); } public Map> getJdbcToHibernateTypeContributionMap() { return jdbcToHibernateTypeContributionMap; } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Scoping /** * Obtain the MetadataBuildingContext currently scoping the * TypeConfiguration. * * @apiNote This will throw an exception if the SessionFactory is not yet * bound here. See {@link Scope} for more details regarding the stages * a TypeConfiguration goes through * * @return */ public MetadataBuildingContext getMetadataBuildingContext() { return scope.getMetadataBuildingContext(); } public void scope(MetadataBuildingContext metadataBuildingContext) { log.debugf( "Scoping TypeConfiguration [%s] to MetadataBuildingContext [%s]", this, metadataBuildingContext ); scope.setMetadataBuildingContext( metadataBuildingContext ); } public DomainMetamodel scope(SessionFactoryImplementor sessionFactory) { log.debugf( "Scoping TypeConfiguration [%s] to SessionFactoryImplementor [%s]", this, sessionFactory ); if ( scope.getMetadataBuildingContext() == null ) { throw new IllegalStateException( "MetadataBuildingContext not known" ); } scope.setSessionFactory( sessionFactory ); sessionFactory.addObserver( this ); return new DomainMetamodelImpl( sessionFactory, this ); } /** * Obtain the SessionFactory currently scoping the TypeConfiguration. * * @apiNote This will throw an exception if the SessionFactory is not yet * bound here. See {@link Scope} for more details regarding the stages * a TypeConfiguration goes through (this is "runtime stage") * * @return The SessionFactory * * @throws IllegalStateException if the TypeConfiguration is currently not * associated with a SessionFactory (in "runtime stage"). */ public SessionFactoryImplementor getSessionFactory() { return scope.getSessionFactory(); } /** * Obtain the ServiceRegistry scoped to the TypeConfiguration. * * @apiNote Depending on what the {@link Scope} is currently scoped to will determine where the * {@link ServiceRegistry} is obtained from. * * @return The ServiceRegistry */ public ServiceRegistry getServiceRegistry() { return scope.getServiceRegistry(); } @Override public void sessionFactoryCreated(SessionFactory factory) { // Instead of allowing scope#setSessionFactory to influence this, we use the SessionFactoryObserver callback // to handle this, allowing any SessionFactory constructor code to be able to continue to have access to the // MetadataBuildingContext through TypeConfiguration until this callback is fired. log.tracef( "Handling #sessionFactoryCreated from [%s] for TypeConfiguration", factory ); scope.setMetadataBuildingContext( null ); } @Override public void sessionFactoryClosed(SessionFactory factory) { log.tracef( "Handling #sessionFactoryClosed from [%s] for TypeConfiguration", factory ); TypeConfigurationRegistry.INSTANCE.deregisterTypeConfiguration( this ); scope.unsetSessionFactory( factory ); // todo (6.0) : finish this // release Database, descriptor Maps, etc... things that are only // valid while the TypeConfiguration is scoped to SessionFactory } public void addBasicTypeRegistrationContributions(List contributions) { for ( BasicTypeRegistration basicTypeRegistration : contributions ) { BasicType basicType = basicTypeRegistration.getBasicType(); basicTypeRegistry.register( basicType, basicTypeRegistration.getRegistrationKeys() ); try { int[] jdbcTypes = basicType.sqlTypes( null ); if ( jdbcTypes.length == 1 ) { int jdbcType = jdbcTypes[0]; Set hibernateTypes = jdbcToHibernateTypeContributionMap.computeIfAbsent( jdbcType, k -> new HashSet<>() ); hibernateTypes.add( basicType.getName() ); } } catch (Exception e) { log.errorf( e, "Cannot register [%s] Hibernate Type contribution", basicType.getName() ); } } } /** * Encapsulation of lifecycle concerns for a TypeConfiguration, mainly in * regards to eventually being associated with a SessionFactory. Goes * 3 "lifecycle" stages, pertaining to {@link #getMetadataBuildingContext()} * and {@link #getSessionFactory()}: * * * "Initialization" is where the {@link TypeConfiguration} is first * built as the "boot model" ({@link org.hibernate.boot.model}) of * the user's domain model is converted into the "runtime model" * ({@link org.hibernate.metamodel.model}). During this phase, * {@link #getMetadataBuildingContext()} will be accessible but * {@link #getSessionFactory} will throw an exception. * * "Runtime" is where the "runtime model" is accessible while the * SessionFactory is still unclosed. During this phase * {@link #getSessionFactory()} is accessible while * {@link #getMetadataBuildingContext()} will now throw an * exception * * "Sunset" is after the SessionFactory has been closed. During this * phase both {@link #getSessionFactory()} and * {@link #getMetadataBuildingContext()} will now throw an exception * * Each stage or phase is consider a "scope" for the TypeConfiguration. */ private static class Scope implements Serializable { private final TypeConfiguration typeConfiguration; private transient MetadataBuildingContext metadataBuildingContext; private transient SessionFactoryImplementor sessionFactory; private String sessionFactoryName; private String sessionFactoryUuid; private transient SqlTypeDescriptorIndicators currentSqlTypeIndicators = new SqlTypeDescriptorIndicators() { @Override public TypeConfiguration getTypeConfiguration() { return typeConfiguration; } @Override public int getPreferredSqlTypeCodeForBoolean() { return Types.BOOLEAN; } }; public Scope(TypeConfiguration typeConfiguration) { this.typeConfiguration = typeConfiguration; } public SqlTypeDescriptorIndicators getCurrentBaseSqlTypeIndicators() { return currentSqlTypeIndicators; } public MetadataBuildingContext getMetadataBuildingContext() { if ( metadataBuildingContext == null ) { throw new HibernateException( "TypeConfiguration is not currently scoped to MetadataBuildingContext" ); } return metadataBuildingContext; } public ServiceRegistry getServiceRegistry() { if ( metadataBuildingContext != null ) { return metadataBuildingContext.getBootstrapContext().getServiceRegistry(); } else if ( sessionFactory != null ) { return sessionFactory.getServiceRegistry(); } return null; } public void setMetadataBuildingContext(MetadataBuildingContext metadataBuildingContext) { this.metadataBuildingContext = metadataBuildingContext; } public SessionFactoryImplementor getSessionFactory() { if ( sessionFactory == null ) { if ( sessionFactoryName == null && sessionFactoryUuid == null ) { throw new HibernateException( "TypeConfiguration was not yet scoped to SessionFactory" ); } sessionFactory = (SessionFactoryImplementor) SessionFactoryRegistry.INSTANCE.findSessionFactory( sessionFactoryUuid, sessionFactoryName ); if ( sessionFactory == null ) { throw new HibernateException( "Could not find a SessionFactory [uuid=" + sessionFactoryUuid + ",name=" + sessionFactoryName + "]" ); } } return sessionFactory; } /** * Used by TypeFactory scoping. * * @param factory The SessionFactory that the TypeFactory is being bound to */ void setSessionFactory(SessionFactoryImplementor factory) { if ( this.sessionFactory != null ) { log.scopingTypesToSessionFactoryAfterAlreadyScoped( this.sessionFactory, factory ); } else { this.sessionFactoryUuid = factory.getUuid(); String sfName = factory.getSessionFactoryOptions().getSessionFactoryName(); if ( sfName == null ) { final CfgXmlAccessService cfgXmlAccessService = factory.getServiceRegistry() .getService( CfgXmlAccessService.class ); if ( cfgXmlAccessService.getAggregatedConfig() != null ) { sfName = cfgXmlAccessService.getAggregatedConfig().getSessionFactoryName(); } } this.sessionFactoryName = sfName; } this.sessionFactory = factory; } public void unsetSessionFactory(SessionFactory factory) { log.debugf( "Un-scoping TypeConfiguration [%s] from SessionFactory [%s]", this, factory ); this.sessionFactory = null; } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Custom serialization hook private Object readResolve() throws InvalidObjectException { if ( sessionFactory == null ) { if ( sessionFactoryName != null || sessionFactoryUuid != null ) { sessionFactory = (SessionFactoryImplementor) SessionFactoryRegistry.INSTANCE.findSessionFactory( sessionFactoryUuid, sessionFactoryName ); if ( sessionFactory == null ) { throw new HibernateException( "Could not find a SessionFactory [uuid=" + sessionFactoryUuid + ",name=" + sessionFactoryName + "]" ); } } } return this; } } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Custom serialization hook private Object readResolve() throws InvalidObjectException { log.trace( "Resolving serialized TypeConfiguration - readResolve" ); return TypeConfigurationRegistry.INSTANCE.findTypeConfiguration( getUuid() ); } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * @see QueryHelper#highestPrecedenceType2 */ public SqmExpressable resolveArithmeticType( SqmExpressable firstType, SqmExpressable secondType, BinaryArithmeticOperator operator) { return resolveArithmeticType( firstType, secondType, operator == DIVIDE ); } /** * Determine the result type of an arithmetic operation as defined by the * rules in section 6.5.7.1. * * @see QueryHelper#highestPrecedenceType2 */ public SqmExpressable resolveArithmeticType( SqmExpressable firstType, SqmExpressable secondType, boolean isDivision) { if ( isDivision ) { // covered under the note in 6.5.7.1 discussing the unportable // "semantics of the SQL division operation".. return getBasicTypeRegistry().getRegisteredType( Number.class.getName() ); } // non-division if ( matchesJavaType( firstType, Double.class ) ) { return firstType; } else if ( matchesJavaType( secondType, Double.class ) ) { return secondType; } else if ( matchesJavaType( firstType, Float.class ) ) { return firstType; } else if ( matchesJavaType( secondType, Float.class ) ) { return secondType; } else if ( matchesJavaType( firstType, BigDecimal.class ) ) { return firstType; } else if ( matchesJavaType( secondType, BigDecimal.class ) ) { return secondType; } else if ( matchesJavaType( firstType, BigInteger.class ) ) { return firstType; } else if ( matchesJavaType( secondType, BigInteger.class ) ) { return secondType; } else if ( matchesJavaType( firstType, Long.class ) ) { return firstType; } else if ( matchesJavaType( secondType, Long.class ) ) { return secondType; } else if ( matchesJavaType( firstType, Integer.class ) ) { return firstType; } else if ( matchesJavaType( secondType, Integer.class ) ) { return secondType; } else if ( matchesJavaType( firstType, Short.class ) ) { return getBasicTypeRegistry().getRegisteredType( Integer.class.getName() ); } else if ( matchesJavaType( secondType, Short.class ) ) { return getBasicTypeRegistry().getRegisteredType( Integer.class.getName() ); } else { return getBasicTypeRegistry().getRegisteredType( Number.class.getName() ); } } @SuppressWarnings("unchecked") private static boolean matchesJavaType(SqmExpressable type, Class javaType) { assert javaType != null; return type != null && javaType.isAssignableFrom( type.getExpressableJavaTypeDescriptor().getJavaType() ); } private final ConcurrentHashMap basicTypeByJavaType = new ConcurrentHashMap<>(); public BasicType getBasicTypeForJavaType(Class javaType) { final BasicType existing = basicTypeByJavaType.get( javaType ); if ( existing != null ) { return existing; } final BasicType registeredType = getBasicTypeRegistry().getRegisteredType( javaType ); if ( registeredType != null ) { basicTypeByJavaType.put( javaType, registeredType ); return registeredType; } return null; } public BasicType standardBasicTypeForJavaType(Class javaType) { if ( javaType == null ) { return null; } //noinspection unchecked return standardBasicTypeForJavaType( javaType, javaTypeDescriptor -> new StandardBasicTypeImpl( javaTypeDescriptor, javaTypeDescriptor.getJdbcRecommendedSqlType( getCurrentBaseSqlTypeIndicators() ) ) ); } public BasicType standardBasicTypeForJavaType( Class javaType, Function,BasicType> creator) { if ( javaType == null ) { return null; } return basicTypeByJavaType.computeIfAbsent( javaType, jt -> { // See if one exists in the BasicTypeRegistry and use that one if so final BasicType registeredType = basicTypeRegistry.getRegisteredType( javaType ); if ( registeredType != null ) { return registeredType; } // otherwise, apply the creator final JavaTypeDescriptor javaTypeDescriptor = javaTypeDescriptorRegistry.resolveDescriptor( javaType ); return creator.apply( javaTypeDescriptor ); } ); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy