Verified and fixed BasicValue resolutions based on type-defs and UserTypes

This commit is contained in:
Steve Ebersole 2020-05-01 11:29:44 -05:00
parent 41093ae66c
commit e38a4de536
14 changed files with 543 additions and 151 deletions

View File

@ -33,11 +33,12 @@ import org.hibernate.MappingException;
import org.hibernate.SessionFactory;
import org.hibernate.annotations.AnyMetaDef;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.util.StringHelper;
import org.hibernate.boot.CacheRegionDefinition;
import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.model.TypeDefinition;
import org.hibernate.boot.model.TypeDefinitionRegistry;
import org.hibernate.boot.model.TypeDefinitionRegistryStandardImpl;
import org.hibernate.boot.model.convert.internal.AttributeConverterManager;
import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterAutoApplyHandler;
@ -131,10 +132,11 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
private final Map<String,PersistentClass> entityBindingMap = new HashMap<>();
private final Map<String,Collection> collectionBindingMap = new HashMap<>();
private final Map<String, TypeDefinition> typeDefinitionMap = new HashMap<>();
private final Map<String, FilterDefinition> filterDefinitionMap = new HashMap<>();
private final Map<String, String> imports = new HashMap<>();
private final TypeDefinitionRegistry typeDefRegistry = new TypeDefinitionRegistryStandardImpl();
private Database database;
private final Map<String, NamedHqlQueryDefinition> namedQueryMap = new HashMap<>();
@ -340,39 +342,20 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Hibernate Type handling
@Override
public TypeDefinitionRegistry getTypeDefinitionRegistry() {
return typeDefRegistry;
}
@Override
public TypeDefinition getTypeDefinition(String registrationKey) {
return typeDefinitionMap.get( registrationKey );
return typeDefRegistry.resolve( registrationKey );
}
@Override
public void addTypeDefinition(TypeDefinition typeDefinition) {
if ( typeDefinition == null ) {
throw new IllegalArgumentException( "Type definition is null" );
}
// Need to register both by name and registration keys.
if ( !StringHelper.isEmpty( typeDefinition.getName() ) ) {
addTypeDefinition( typeDefinition.getName(), typeDefinition );
}
if ( typeDefinition.getRegistrationKeys() != null ) {
for ( String registrationKey : typeDefinition.getRegistrationKeys() ) {
addTypeDefinition( registrationKey, typeDefinition );
}
}
}
private void addTypeDefinition(String registrationKey, TypeDefinition typeDefinition) {
final TypeDefinition previous = typeDefinitionMap.put(
registrationKey, typeDefinition );
if ( previous != null ) {
log.debugf(
"Duplicate typedef name [%s] now -> %s",
registrationKey,
typeDefinition.getTypeImplementorClass().getName()
);
}
typeDefRegistry.register( typeDefinition );
}
@Override
@ -2259,7 +2242,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector
entityBindingMap,
mappedSuperClasses,
collectionBindingMap,
typeDefinitionMap,
typeDefRegistry.copyRegistrationMap(),
filterDefinitionMap,
fetchProfileMap,
imports,

View File

@ -6,7 +6,7 @@
*/
package org.hibernate.boot.internal;
import org.hibernate.boot.model.TypeDefinitionRegistry;
import org.hibernate.boot.model.TypeDefinitionRegistryStandardImpl;
import org.hibernate.boot.model.naming.ObjectNameNormalizer;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.ClassLoaderAccess;
@ -24,7 +24,7 @@ public class MetadataBuildingContextRootImpl implements MetadataBuildingContext
private final MappingDefaults mappingDefaults;
private final InFlightMetadataCollector metadataCollector;
private final ObjectNameNormalizer objectNameNormalizer;
private final TypeDefinitionRegistry typeDefinitionRegistry;
private final TypeDefinitionRegistryStandardImpl typeDefinitionRegistry;
public MetadataBuildingContextRootImpl(
BootstrapContext bootstrapContext,
@ -40,7 +40,7 @@ public class MetadataBuildingContextRootImpl implements MetadataBuildingContext
return MetadataBuildingContextRootImpl.this;
}
};
this.typeDefinitionRegistry = new TypeDefinitionRegistry();
this.typeDefinitionRegistry = new TypeDefinitionRegistryStandardImpl();
}
@Override
@ -74,7 +74,7 @@ public class MetadataBuildingContextRootImpl implements MetadataBuildingContext
}
@Override
public TypeDefinitionRegistry getTypeDefinitionRegistry() {
public TypeDefinitionRegistryStandardImpl getTypeDefinitionRegistry() {
return typeDefinitionRegistry;
}
}

View File

@ -24,6 +24,7 @@ import org.hibernate.SessionFactory;
import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.model.TypeDefinition;
import org.hibernate.boot.model.TypeDefinitionRegistry;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;

View File

@ -25,7 +25,7 @@ import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.type.BasicType;
import org.hibernate.type.CustomType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
@ -48,6 +48,7 @@ import org.hibernate.usertype.UserType;
* @author Steve Ebersole
* @author John Verhaeg
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class TypeDefinition implements Serializable {
private static final AtomicInteger nameCounter = new AtomicInteger();
@ -96,8 +97,6 @@ public class TypeDefinition implements Serializable {
}
public BasicValue.Resolution<?> resolve(
JavaTypeDescriptor<?> explicitJtd,
SqlTypeDescriptor explicitStd,
Map localConfigParameters,
MutabilityPlan explicitMutabilityPlan,
MetadataBuildingContext context) {
@ -137,8 +136,6 @@ public class TypeDefinition implements Serializable {
return createResolution(
name,
typeInstance,
explicitJtd,
explicitStd,
explicitMutabilityPlan,
context
);
@ -213,7 +210,10 @@ public class TypeDefinition implements Serializable {
@Override
public MutabilityPlan<Object> getMutabilityPlan() {
throw new NotYetImplementedFor6Exception( getClass() );
// a TypeDefinition does not explicitly provide a MutabilityPlan (yet?)
return resolvedBasicType.isMutable()
? getDomainJavaDescriptor().getMutabilityPlan()
: ImmutableMutabilityPlan.instance();
}
};
}
@ -226,8 +226,6 @@ public class TypeDefinition implements Serializable {
public static BasicValue.Resolution<?> createLocalResolution(
String name,
Class typeImplementorClass,
JavaTypeDescriptor<?> explicitJtd,
SqlTypeDescriptor explicitStd,
MutabilityPlan explicitMutabilityPlan,
Map localTypeParams,
MetadataBuildingContext buildingContext) {
@ -245,8 +243,6 @@ public class TypeDefinition implements Serializable {
return createResolution(
name,
typeInstance,
explicitJtd,
explicitStd,
explicitMutabilityPlan,
buildingContext
);
@ -255,8 +251,6 @@ public class TypeDefinition implements Serializable {
private static BasicValue.Resolution<?> createResolution(
String name,
Object namedTypeInstance,
JavaTypeDescriptor<?> explicitJtd,
SqlTypeDescriptor explicitStd,
MutabilityPlan explicitMutabilityPlan,
MetadataBuildingContext metadataBuildingContext) {
if ( namedTypeInstance instanceof UserType ) {

View File

@ -6,98 +6,27 @@
*/
package org.hibernate.boot.model;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.hibernate.internal.util.StringHelper;
import org.jboss.logging.Logger;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
/**
* @author Chris Cranford
* @author Steve Ebersole
*/
public class TypeDefinitionRegistry {
private static final Logger log = Logger.getLogger( TypeDefinitionRegistry.class );
private final TypeDefinitionRegistry parent;
public interface TypeDefinitionRegistry {
public enum DuplicationStrategy {
enum DuplicationStrategy {
KEEP,
OVERWRITE,
DISALLOW
}
private final Map<String, TypeDefinition> typeDefinitionMap = new HashMap<>();
TypeDefinition resolve(String typeName);
TypeDefinition resolveAutoApplied(BasicJavaDescriptor<?> jtd);
public TypeDefinitionRegistry() {
this( null );
}
TypeDefinitionRegistry register(TypeDefinition typeDefinition);
TypeDefinitionRegistry register(TypeDefinition typeDefinition, DuplicationStrategy duplicationStrategy);
public TypeDefinitionRegistry(TypeDefinitionRegistry parent) {
this.parent = parent;
}
public TypeDefinition resolve(String typeName) {
final TypeDefinition localDefinition = typeDefinitionMap.get( typeName );
if ( localDefinition != null ) {
return localDefinition;
}
if ( parent != null ) {
return parent.resolve( typeName );
}
return null;
}
public TypeDefinitionRegistry register(TypeDefinition typeDefinition) {
return register( typeDefinition, DuplicationStrategy.OVERWRITE );
}
public TypeDefinitionRegistry register(TypeDefinition typeDefinition, DuplicationStrategy duplicationStrategy) {
if ( typeDefinition == null ) {
throw new IllegalArgumentException( "TypeDefinition to register cannot be null" );
}
if ( typeDefinition.getTypeImplementorClass() == null ) {
throw new IllegalArgumentException( "TypeDefinition to register cannot define null #typeImplementorClass" );
}
if ( !StringHelper.isEmpty( typeDefinition.getName() ) ) {
register( typeDefinition.getName(), typeDefinition, duplicationStrategy );
}
if ( typeDefinition.getRegistrationKeys() != null ) {
for ( String registrationKey : typeDefinition.getRegistrationKeys() ) {
register( registrationKey, typeDefinition, duplicationStrategy );
}
}
return this;
}
private void register(String name, TypeDefinition typeDefinition, DuplicationStrategy duplicationStrategy) {
if ( duplicationStrategy == DuplicationStrategy.KEEP ) {
if ( !typeDefinitionMap.containsKey( name ) ) {
typeDefinitionMap.put( name, typeDefinition );
}
}
else {
final TypeDefinition existing = typeDefinitionMap.put( name, typeDefinition );
if ( existing != null && existing != typeDefinition ) {
if ( duplicationStrategy == DuplicationStrategy.OVERWRITE ) {
log.debugf( "Overwrote existing registration [%s] for type definition.", name );
}
else {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Attempted to overwrite registration [%s] for type definition.",
name
)
);
}
}
}
}
Map<String, TypeDefinition> copyRegistrationMap();
}

View File

@ -0,0 +1,118 @@
/*
* 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.boot.model;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.jboss.logging.Logger;
/**
* @author Chris Cranford
*/
public class TypeDefinitionRegistryStandardImpl implements TypeDefinitionRegistry {
private static final Logger log = Logger.getLogger( TypeDefinitionRegistryStandardImpl.class );
private final TypeDefinitionRegistry parent;
private final Map<String, TypeDefinition> typeDefinitionMap = new HashMap<>();
public TypeDefinitionRegistryStandardImpl() {
this( null );
}
public TypeDefinitionRegistryStandardImpl(TypeDefinitionRegistry parent) {
this.parent = parent;
}
@Override
public TypeDefinition resolve(String typeName) {
final TypeDefinition localDefinition = typeDefinitionMap.get( typeName );
if ( localDefinition != null ) {
return localDefinition;
}
if ( parent != null ) {
return parent.resolve( typeName );
}
return null;
}
@Override
public TypeDefinition resolveAutoApplied(BasicJavaDescriptor<?> jtd) {
// For now, check the definition map for a entry keyed by the JTD name.
// Ultimately should maybe have TypeDefinition or the registry keep explicit track of
// auto-applied defs
if ( jtd.getJavaType() == null ) {
return null;
}
return typeDefinitionMap.get( jtd.getJavaType().getName() );
}
@Override
public TypeDefinitionRegistry register(TypeDefinition typeDefinition) {
return register( typeDefinition, DuplicationStrategy.OVERWRITE );
}
@Override
public TypeDefinitionRegistry register(TypeDefinition typeDefinition, DuplicationStrategy duplicationStrategy) {
if ( typeDefinition == null ) {
throw new IllegalArgumentException( "TypeDefinition to register cannot be null" );
}
if ( typeDefinition.getTypeImplementorClass() == null ) {
throw new IllegalArgumentException( "TypeDefinition to register cannot define null #typeImplementorClass" );
}
if ( !StringHelper.isEmpty( typeDefinition.getName() ) ) {
register( typeDefinition.getName(), typeDefinition, duplicationStrategy );
}
if ( typeDefinition.getRegistrationKeys() != null ) {
for ( String registrationKey : typeDefinition.getRegistrationKeys() ) {
register( registrationKey, typeDefinition, duplicationStrategy );
}
}
return this;
}
private void register(String name, TypeDefinition typeDefinition, DuplicationStrategy duplicationStrategy) {
if ( duplicationStrategy == DuplicationStrategy.KEEP ) {
if ( !typeDefinitionMap.containsKey( name ) ) {
typeDefinitionMap.put( name, typeDefinition );
}
}
else {
final TypeDefinition existing = typeDefinitionMap.put( name, typeDefinition );
if ( existing != null && existing != typeDefinition ) {
if ( duplicationStrategy == DuplicationStrategy.OVERWRITE ) {
log.debugf( "Overwrote existing registration [%s] for type definition.", name );
}
else {
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Attempted to overwrite registration [%s] for type definition.",
name
)
);
}
}
}
}
@Override
public Map<String, TypeDefinition> copyRegistrationMap() {
return new HashMap<>( typeDefinitionMap );
}
}

View File

@ -21,6 +21,7 @@ import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmNamedQueryType;
import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmTypeDefinitionType;
import org.hibernate.boot.jaxb.hbm.spi.ResultSetMappingBindingDefinition;
import org.hibernate.boot.model.TypeDefinitionRegistry;
import org.hibernate.boot.model.TypeDefinitionRegistryStandardImpl;
import org.hibernate.boot.model.naming.ObjectNameNormalizer;
import org.hibernate.boot.model.source.internal.OverriddenMappingDefaults;
import org.hibernate.boot.model.source.spi.MetadataSourceProcessor;
@ -52,7 +53,7 @@ public class MappingDocument implements HbmLocalMetadataBuildingContext, Metadat
private final ToolingHintContext toolingHintContext;
private final TypeDefinitionRegistry typeDefinitionRegistry;
private final TypeDefinitionRegistryStandardImpl typeDefinitionRegistry;
public MappingDocument(
@ -78,7 +79,7 @@ public class MappingDocument implements HbmLocalMetadataBuildingContext, Metadat
this.toolingHintContext = Helper.collectToolingHints( null, documentRoot );
this.typeDefinitionRegistry = new TypeDefinitionRegistry( rootBuildingContext.getTypeDefinitionRegistry() );
this.typeDefinitionRegistry = new TypeDefinitionRegistryStandardImpl( rootBuildingContext.getTypeDefinitionRegistry() );
}
public JaxbHbmHibernateMapping getDocumentRoot() {

View File

@ -43,7 +43,7 @@ public class TypeDefinitionBinder {
definition.getTypeImplementorClass().getName()
);
context.getMetadataCollector().addTypeDefinition( definition );
context.getTypeDefinitionRegistry().register( definition );
}
}

View File

@ -22,6 +22,7 @@ import org.hibernate.boot.internal.ClassmateContext;
import org.hibernate.boot.internal.NamedProcedureCallDefinitionImpl;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.model.TypeDefinition;
import org.hibernate.boot.model.TypeDefinitionRegistry;
import org.hibernate.boot.model.convert.internal.InstanceBasedConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterAutoApplyHandler;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
@ -171,9 +172,16 @@ public interface InFlightMetadataCollector extends Mapping, MetadataImplementor
* @param typeDefinition The named type definition to add.
*
* @throws DuplicateMappingException If a TypeDefinition already exists with that name.
*
* @deprecated Use {@link #getTypeDefinitionRegistry()} instead
*
* @see #getTypeDefinitionRegistry()
*/
@Deprecated
void addTypeDefinition(TypeDefinition typeDefinition);
TypeDefinitionRegistry getTypeDefinitionRegistry();
/**
* Adds a filter definition to this repository.
*

View File

@ -1463,7 +1463,16 @@ public final class AnnotationBinder {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( typeBindMessageF, defAnn.name() );
}
context.getMetadataCollector().addTypeDefinition(
// context.getMetadataCollector().addTypeDefinition(
// new TypeDefinition(
// defAnn.name(),
// defAnn.typeClass(),
// null,
// params,
// context.getMetadataCollector().getTypeConfiguration()
// )
// );
context.getTypeDefinitionRegistry().register(
new TypeDefinition(
defAnn.name(),
defAnn.typeClass(),
@ -1478,7 +1487,8 @@ public final class AnnotationBinder {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( typeBindMessageF, defAnn.defaultForType().getName() );
}
context.getMetadataCollector().addTypeDefinition(
context.getTypeDefinitionRegistry().register(
new TypeDefinition(
defAnn.defaultForType().getName(),
defAnn.typeClass(),

View File

@ -361,6 +361,13 @@ public class BasicValue extends SimpleValue implements SqlTypeDescriptorIndicato
// Use JTD if we know it to apply any specialized resolutions
final TypeDefinition autoAppliedTypeDef = getBuildingContext().getTypeDefinitionRegistry()
.resolveAutoApplied( (BasicJavaDescriptor<?>) jtd );
if ( autoAppliedTypeDef != null ) {
log.debugf( "BasicValue resolution matched auto-applied type-definition" );
return autoAppliedTypeDef.resolve( getTypeParameters(), null, getBuildingContext() );
}
if ( jtd instanceof EnumJavaTypeDescriptor ) {
return InferredBasicValueResolver.fromEnum(
(EnumJavaTypeDescriptor) jtd,
@ -499,8 +506,6 @@ public class BasicValue extends SimpleValue implements SqlTypeDescriptorIndicato
final TypeDefinition typeDefinition = context.getTypeDefinitionRegistry().resolve( name );
if ( typeDefinition != null ) {
return typeDefinition.resolve(
explicitJtdAccess.apply( typeConfiguration ),
explicitStdAccess.apply( typeConfiguration ),
localTypeParams,
explicitMutabilityPlanAccess != null
? explicitMutabilityPlanAccess.apply( typeConfiguration )
@ -527,8 +532,6 @@ public class BasicValue extends SimpleValue implements SqlTypeDescriptorIndicato
);
context.getTypeDefinitionRegistry().register( implicitDefinition );
return implicitDefinition.resolve(
explicitJtdAccess != null ? explicitJtdAccess.apply( typeConfiguration ) : null,
explicitStdAccess != null ? explicitStdAccess.apply( typeConfiguration ) : null,
null,
explicitMutabilityPlanAccess != null
? explicitMutabilityPlanAccess.apply( typeConfiguration )
@ -540,8 +543,6 @@ public class BasicValue extends SimpleValue implements SqlTypeDescriptorIndicato
return TypeDefinition.createLocalResolution(
name,
typeNamedClass,
explicitJtdAccess.apply( typeConfiguration ),
explicitStdAccess.apply( typeConfiguration ),
explicitMutabilityPlanAccess != null
? explicitMutabilityPlanAccess.apply( typeConfiguration )
: null,
@ -549,8 +550,9 @@ public class BasicValue extends SimpleValue implements SqlTypeDescriptorIndicato
context
);
}
catch (ClassLoadingException ignore) {
catch (ClassLoadingException e) {
// allow the exception below to trigger
log.debugf( "Could not resolve type-name [%s] as Java type : %s", name, e );
}
throw new MappingException( "Could not resolve named type : " + name );

View File

@ -0,0 +1,352 @@
/*
* 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.orm.test.bootstrap.binding.annotations.basics;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Objects;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
import org.hibernate.type.CustomType;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.UrlTypeDescriptor;
import org.hibernate.type.descriptor.sql.CharTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;
import org.hibernate.usertype.UserType;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.DomainModelScope;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
/**
* Resolution for custom-types
*
* @author Steve Ebersole
*/
@ServiceRegistry(
settings = @ServiceRegistry.Setting( name = AvailableSettings.HBM2DDL_AUTO, value = "create-drop" )
)
@DomainModel( annotatedClasses = CustomTypeResolutionTests.Person.class )
public class CustomTypeResolutionTests {
@Test
public void testMappings(DomainModelScope scope) {
final PersistentClass entityBinding = scope.getDomainModel().getEntityBinding( Person.class.getName() );
final Property genderProperty = entityBinding.getProperty( "gender" );
final BasicValue genderValue = (BasicValue) genderProperty.getValue();
final BasicValue.Resolution<?> genderValueResolution = genderValue.resolve();
assertThat( genderValueResolution.getLegacyResolvedBasicType(), instanceOf( GenderType.class ) );
final Property string1Property = entityBinding.getProperty( "string1" );
final BasicValue string1Value = (BasicValue) string1Property.getValue();
final BasicValue.Resolution<?> string1ValueResolution = string1Value.resolve();
assertThat( string1ValueResolution.getLegacyResolvedBasicType(), instanceOf( CustomType.class ) );
assertThat( string1ValueResolution.getJdbcMapping(), instanceOf( CustomType.class ) );
assertThat( string1ValueResolution.getLegacyResolvedBasicType(), is( string1ValueResolution.getJdbcMapping() ) );
final CustomType string1TypeWrapper = (CustomType) string1ValueResolution.getJdbcMapping();
assertThat( string1TypeWrapper.getUserType(), instanceOf( UserTypeImpl.class ) );
final Property string2Property = entityBinding.getProperty( "string2" );
final BasicValue string2Value = (BasicValue) string2Property.getValue();
final BasicValue.Resolution<?> string2ValueResolution = string2Value.resolve();
assertThat( string2ValueResolution.getLegacyResolvedBasicType(), instanceOf( CustomType.class ) );
assertThat( string2ValueResolution.getJdbcMapping(), instanceOf( CustomType.class ) );
assertThat( string2ValueResolution.getLegacyResolvedBasicType(), is( string2ValueResolution.getJdbcMapping() ) );
final CustomType string2TypeWrapper = (CustomType) string2ValueResolution.getJdbcMapping();
assertThat( string2TypeWrapper.getUserType(), instanceOf( UserTypeImpl.class ) );
final Property url1Property = entityBinding.getProperty( "url1" );
final BasicValue url1Value = (BasicValue) url1Property.getValue();
final BasicValue.Resolution<?> url1ValueResolution = url1Value.resolve();
assertThat( url1ValueResolution.getLegacyResolvedBasicType(), instanceOf( CustomTypeImpl.class ) );
final Property url2Property = entityBinding.getProperty( "url2" );
final BasicValue url2Value = (BasicValue) url2Property.getValue();
final BasicValue.Resolution<?> url2ValueResolution = url2Value.resolve();
assertThat( url2ValueResolution.getLegacyResolvedBasicType(), instanceOf( CustomTypeImpl.class ) );
}
@Test
public void simpleUsageSmokeTest(DomainModelScope scope) {
try ( SessionFactory sessionFactory = scope.getDomainModel().buildSessionFactory()) {
// set up test data
sessionFactory.inTransaction(
session -> {
try {
session.persist(
new Person(
1,
Gender.MALE,
"str1",
"str2",
new URL( "http://url1" ),
new URL( "http://url2" )
)
);
}
catch (MalformedURLException e) {
throw new RuntimeException( e );
}
}
);
// try to read it back
try {
sessionFactory.inTransaction(
session -> {
final Person loaded = session.byId( Person.class ).load( 1 );
assertThat( loaded, notNullValue() );
assertThat( loaded.gender, is( Gender.MALE ) );
assertThat( loaded.string1, is( "str1" ) );
assertThat( loaded.string2, is( "str2" ) );
assertThat( loaded.url1.getHost(), is( "url1" ) );
assertThat( loaded.url2.getHost(), is( "url2" ) );
}
);
}
finally {
sessionFactory.inTransaction(
session -> session.createQuery( "delete Person" ).executeUpdate()
);
}
}
}
public enum Gender { MALE, FEMALE }
@SuppressWarnings("unused")
@Entity( name = "Person" )
@Table( name = "persons" )
@TypeDef( name = "user-type", typeClass = UserTypeImpl.class )
@TypeDef( name = "custom-type", typeClass = CustomTypeImpl.class )
@TypeDef( name = "gender-type", typeClass = GenderType.class, defaultForType = Gender.class )
public static class Person {
@Id
private Integer id;
private Gender gender;
@Type( type = "org.hibernate.orm.test.bootstrap.binding.annotations.basics.CustomTypeResolutionTests$UserTypeImpl" )
private String string1;
@Type( type = "user-type" )
private String string2;
@Type( type = "org.hibernate.orm.test.bootstrap.binding.annotations.basics.CustomTypeResolutionTests$CustomTypeImpl" )
private URL url1;
@Type( type = "custom-type" )
private URL url2;
public Person() {
}
public Person(Integer id, Gender gender, String string1, String string2, URL url1, URL url2) {
this.id = id;
this.gender = gender;
this.string1 = string1;
this.string2 = string2;
this.url1 = url1;
this.url2 = url2;
}
}
public static class UserTypeImpl implements UserType {
@Override
public int[] sqlTypes() {
return new int[] { Types.VARCHAR };
}
@Override
public Class<String> returnedClass() {
return String.class;
}
@Override
public boolean equals(Object x, Object y) throws HibernateException {
return Objects.equals( x, y );
}
@Override
public int hashCode(Object x) throws HibernateException {
return Objects.hashCode( x );
}
@Override
public Object nullSafeGet(
ResultSet rs,
String[] names,
SharedSessionContractImplementor session,
Object owner) throws HibernateException, SQLException {
throw new UnsupportedOperationException();
}
@Override
public void nullSafeSet(
PreparedStatement st,
Object value,
int index,
SharedSessionContractImplementor session) throws HibernateException, SQLException {
if ( value == null ) {
st.setNull( index, Types.VARCHAR );
}
else {
st.setString( index, value.toString() );
}
}
@Override
public Object deepCopy(Object value) throws HibernateException {
return value;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Serializable disassemble(Object value) throws HibernateException {
return (String) value;
}
@Override
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return cached;
}
@Override
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}
}
public static class CustomTypeImpl extends AbstractSingleColumnStandardBasicType<URL> {
public CustomTypeImpl() {
super( VarcharTypeDescriptor.INSTANCE, UrlTypeDescriptor.INSTANCE );
}
@Override
public String getName() {
return "Custom URL mapper";
}
}
public static class GenderJtd implements JavaTypeDescriptor<Gender> {
/**
* Singleton access
*/
public static final GenderJtd INSTANCE = new GenderJtd();
@Override
public SqlTypeDescriptor getJdbcRecommendedSqlType(SqlTypeDescriptorIndicators context) {
return CharTypeDescriptor.INSTANCE;
}
@Override
public Gender fromString(String string) {
if ( StringHelper.isEmpty( string ) ) {
return null;
}
if ( "M".equalsIgnoreCase( string ) ) {
return Gender.MALE;
}
else if ( "F".equalsIgnoreCase( string ) ) {
return Gender.FEMALE;
}
throw new IllegalArgumentException( "Unrecognized Gender code : " + string );
}
@Override
@SuppressWarnings("unchecked")
public <X> X unwrap(Gender value, Class<X> type, WrapperOptions options) {
// only defines support for String conversion as part of unwrap
if ( value == null ) {
return null;
}
if ( type.isInstance( value ) ) {
return (X) value;
}
if ( String.class.equals( type ) ) {
return (X) (value == Gender.MALE ? "M" : "F");
}
throw new IllegalArgumentException();
}
@Override
public <X> Gender wrap(X value, WrapperOptions options) {
if ( value == null ) {
return null;
}
if ( value instanceof String ) {
if ( "F".equalsIgnoreCase( (String) value ) ) {
return Gender.FEMALE;
}
if ( "M".equalsIgnoreCase( (String) value ) ) {
return Gender.MALE;
}
}
throw new IllegalArgumentException();
}
@Override
public Class<Gender> getJavaTypeClass() {
return null;
}
}
public static class GenderType extends AbstractSingleColumnStandardBasicType<Gender> {
public GenderType() {
super( VarcharTypeDescriptor.INSTANCE, GenderJtd.INSTANCE );
}
@Override
public String getName() {
return "Custom URL mapper";
}
}
}

View File

@ -1,17 +1,11 @@
/*
* 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>.
* 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
*/
/*
* 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.orm.test.grammar.importsql;
package org.hibernate.orm.test.bootstrap.schema;
import java.io.BufferedReader;
import java.io.Reader;

View File

@ -9,7 +9,7 @@ package org.hibernate.testing.boot;
import org.hibernate.boot.internal.BootstrapContextImpl;
import org.hibernate.boot.internal.InFlightMetadataCollectorImpl;
import org.hibernate.boot.internal.MetadataBuilderImpl;
import org.hibernate.boot.model.TypeDefinitionRegistry;
import org.hibernate.boot.model.TypeDefinitionRegistryStandardImpl;
import org.hibernate.boot.model.naming.ObjectNameNormalizer;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
@ -29,7 +29,7 @@ public class MetadataBuildingContextTestingImpl implements MetadataBuildingConte
private final InFlightMetadataCollector metadataCollector;
private final BootstrapContext bootstrapContext;
private final ObjectNameNormalizer objectNameNormalizer;
private final TypeDefinitionRegistry typeDefinitionRegistry;
private final TypeDefinitionRegistryStandardImpl typeDefinitionRegistry;
public MetadataBuildingContextTestingImpl() {
this( new StandardServiceRegistryBuilder().build() );
@ -48,7 +48,7 @@ public class MetadataBuildingContextTestingImpl implements MetadataBuildingConte
}
};
this.typeDefinitionRegistry = new TypeDefinitionRegistry();
this.typeDefinitionRegistry = new TypeDefinitionRegistryStandardImpl();
}
@Override
@ -82,7 +82,7 @@ public class MetadataBuildingContextTestingImpl implements MetadataBuildingConte
}
@Override
public TypeDefinitionRegistry getTypeDefinitionRegistry() {
public TypeDefinitionRegistryStandardImpl getTypeDefinitionRegistry() {
return typeDefinitionRegistry;
}
}