HHH-12410 - Cannot use AttributeConverter with spatial types

HHH-12443 - Introduce TypeConfiguration
This commit is contained in:
Steve Ebersole 2018-03-29 23:16:12 -05:00
parent 216ad13e6c
commit c14180ea5b
39 changed files with 1009 additions and 135 deletions

View File

@ -24,6 +24,8 @@ ext {
javassistVersion = '3.22.0-GA'
byteBuddyVersion = '1.8.0' // Now with JDK10 compatibility and preliminary support for JDK11
geolatteVersion = '1.3.0'
// Wildfly version targeted by module ZIP; Arquillian/Shrinkwrap versions used for CDI testing and testing the module ZIP
wildflyVersion = '12.0.0.Final'
arquillianVersion = '1.1.11.Final'
@ -31,6 +33,8 @@ ext {
shrinkwrapDescriptorsVersion = '2.0.0-alpha-8'
wildflyArquillianContainerVersion = '2.0.0.Final'
jodaTimeVersion = '2.3'
libraries = [
// Ant
ant: 'org.apache.ant:ant:1.8.2',
@ -74,6 +78,8 @@ ext {
jaxb2_jaxb: 'org.jvnet.jaxb2_commons:jaxb2-basics-jaxb:2.2.4-1',
jaxb2_jaxb_xjc: 'org.jvnet.jaxb2_commons:jaxb2-basics-jaxb-xjc:2.2.4-1',
geolatte: "org.geolatte:geolatte-geom:${geolatteVersion}",
// Animal Sniffer Ant Task and Java 1.6 API signature file
// not using 1.9 for the time being due to MANIMALSNIFFER-34
animal_sniffer: 'org.codehaus.mojo:animal-sniffer-ant-tasks:1.13',
@ -105,6 +111,8 @@ ext {
db2: 'com.ibm.db2:db2jcc:10.5',
hana: 'com.sap.db.jdbc:ngdbc:2.2.1', // for HANA 1 the minimum required client version is 1.120.20
jodaTime: "joda-time:joda-time:${jodaTimeVersion}",
informix: 'com.ibm.informix:jdbc:4.10.7.20160517',
jboss_jta: "org.jboss.jbossts:jbossjta:4.16.4.Final",
xapool: "com.experlog:xapool:1.5.0",

View File

@ -69,7 +69,7 @@ dependencies {
testCompile( libraries.classmate )
testCompile( libraries.mockito )
testCompile( libraries.mockito_inline )
testCompile( 'joda-time:joda-time:2.3' )
testCompile( libraries.jodaTime )
testCompile( libraries.cdi ) {
// we need to force it to make sure we influence the one coming from arquillian

View File

@ -7,6 +7,8 @@
package org.hibernate.boot.model;
import org.hibernate.type.BasicType;
import org.hibernate.type.StandardBasicTypeTemplate;
import org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
@ -19,34 +21,56 @@ import org.hibernate.usertype.UserType;
* @author Steve Ebersole
*/
public interface TypeContributions {
TypeConfiguration getTypeConfiguration();
/**
* Add the JavaTypeDescriptor to the {@link TypeConfiguration}'s
* {@link JavaTypeDescriptorRegistry}
*/
void contributeJavaTypeDescriptor(JavaTypeDescriptor descriptor);
/**
* Add the JavaTypeDescriptor to the {@link TypeConfiguration}'s
* {@link JavaTypeDescriptorRegistry}
*/
void contributeSqlTypeDescriptor(SqlTypeDescriptor descriptor);
void contributeType(BasicType type);
/**
* @deprecated (since 5.3) It will be replaced by {@link #contributeType(BasicType)}
* but do not move to it before 6.0
* @deprecated (since 5.3) Use {@link #contributeType(BasicType)} instead. Basic
* types will be defined and handled much differently in 6.0 based on a combination
* of {@link JavaTypeDescriptor}, {@link SqlTypeDescriptor} and a concept of a "value
* converter" (a JPA AttributeConverter, an enum value resolver, etc). To get as
* close as possible in 5.3 use existing {@link JavaTypeDescriptor} and
* {@link SqlTypeDescriptor} implementations (or write your own for custom types)
* and use {@link StandardBasicTypeTemplate} to combine those with
* registration keys and call {@link #contributeType(BasicType)} instead
*/
@Deprecated
void contributeType(BasicType type, String... keys);
/**
* @deprecated (since 5.3) It will be replaced by {@link #contributeType(BasicType)}
* but do not move to it before 6.0
* @deprecated (since 5.3) Use {@link #contributeType(BasicType)} instead.
* {@link UserType}, as currently defined, will be done very differently in 6.0.
* In most cases a {@link UserType} can be simply replaced with proper
* {@link JavaTypeDescriptor}. To get as close as possible to 6.0 in 5.3 use
* existing {@link JavaTypeDescriptor} and {@link SqlTypeDescriptor}
* implementations (or write your own for custom impls) and use
* {@link StandardBasicTypeTemplate} to combine those with registration keys
* and call {@link #contributeType(BasicType)} instead
*/
@Deprecated
void contributeType(UserType type, String... keys);
/**
* @deprecated (since 5.3) It will be replaced by {@link #contributeType(BasicType)}
* but do not move to it before 6.0
* @deprecated (since 5.3) Use {@link #contributeType(BasicType)} instead.
* {@link CompositeUserType}, as currently defined, will be done very differently
* in 6.0. {@link CompositeUserType} should be replaced with a normal Hibernate
* component or JPA embeddable (different names, same thing. This embeddable
* may contain, in turn, custom types that should be handled as described on these
* methods
*/
@Deprecated
void contributeType(CompositeUserType type, String... keys);
/*
* Add the JavaTypeDescriptor to the
* @param descriptor
*/
void contributeJavaTypeDescriptor(JavaTypeDescriptor descriptor);
void contributeSqlTypeDescriptor(SqlTypeDescriptor descriptor);
TypeConfiguration getTypeConfiguration();
}

View File

@ -7,7 +7,7 @@
package org.hibernate.boot.model.convert.spi;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry;
import org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry;
/**
* Access to information that implementors of

View File

@ -64,7 +64,7 @@ public class StandardServiceRegistryBuilder {
* @param bootstrapServiceRegistry Provided bootstrap registry to use.
*/
public StandardServiceRegistryBuilder(BootstrapServiceRegistry bootstrapServiceRegistry) {
this( bootstrapServiceRegistry, LoadedConfig.baseline() );
this( bootstrapServiceRegistry, LoadedConfig.baseline() );
}
/**
@ -72,7 +72,9 @@ public class StandardServiceRegistryBuilder {
*
* @param bootstrapServiceRegistry Provided bootstrap registry to use.
*/
public StandardServiceRegistryBuilder(BootstrapServiceRegistry bootstrapServiceRegistry, LoadedConfig loadedConfigBaseline) {
public StandardServiceRegistryBuilder(
BootstrapServiceRegistry bootstrapServiceRegistry,
LoadedConfig loadedConfigBaseline) {
this.settings = Environment.getProperties();
this.bootstrapServiceRegistry = bootstrapServiceRegistry;
this.configLoader = new ConfigLoader( bootstrapServiceRegistry );
@ -103,7 +105,7 @@ public class StandardServiceRegistryBuilder {
/**
* Read settings from a {@link java.util.Properties} file by resource name.
*
* <p>
* Differs from {@link #configure()} and {@link #configure(String)} in that here we expect to read a
* {@link java.util.Properties} file while for {@link #configure} we read the XML variant.
*
@ -114,7 +116,7 @@ public class StandardServiceRegistryBuilder {
* @see #configure()
* @see #configure(String)
*/
@SuppressWarnings( {"unchecked"})
@SuppressWarnings({"unchecked"})
public StandardServiceRegistryBuilder loadProperties(String resourceName) {
settings.putAll( configLoader.loadProperties( resourceName ) );
return this;
@ -122,7 +124,7 @@ public class StandardServiceRegistryBuilder {
/**
* Read settings from a {@link java.util.Properties} file by File reference
*
* <p>
* Differs from {@link #configure()} and {@link #configure(String)} in that here we expect to read a
* {@link java.util.Properties} file while for {@link #configure} we read the XML variant.
*
@ -133,7 +135,7 @@ public class StandardServiceRegistryBuilder {
* @see #configure()
* @see #configure(String)
*/
@SuppressWarnings( {"unchecked"})
@SuppressWarnings({"unchecked"})
public StandardServiceRegistryBuilder loadProperties(File file) {
settings.putAll( configLoader.loadProperties( file ) );
return this;
@ -171,7 +173,7 @@ public class StandardServiceRegistryBuilder {
return configure( configLoader.loadConfigXmlUrl( url ) );
}
@SuppressWarnings( {"unchecked"})
@SuppressWarnings({"unchecked"})
public StandardServiceRegistryBuilder configure(LoadedConfig loadedConfig) {
aggregatedCfgXml.merge( loadedConfig );
settings.putAll( loadedConfig.getConfigurationValues() );
@ -187,7 +189,7 @@ public class StandardServiceRegistryBuilder {
*
* @return this, for method chaining
*/
@SuppressWarnings( {"unchecked", "UnusedDeclaration"})
@SuppressWarnings({"unchecked", "UnusedDeclaration"})
public StandardServiceRegistryBuilder applySetting(String settingName, Object value) {
settings.put( settingName, value );
return this;
@ -200,7 +202,7 @@ public class StandardServiceRegistryBuilder {
*
* @return this, for method chaining
*/
@SuppressWarnings( {"unchecked", "UnusedDeclaration"})
@SuppressWarnings({"unchecked", "UnusedDeclaration"})
public StandardServiceRegistryBuilder applySettings(Map settings) {
this.settings.putAll( settings );
return this;
@ -213,7 +215,7 @@ public class StandardServiceRegistryBuilder {
*
* @return this, for method chaining
*/
@SuppressWarnings( {"UnusedDeclaration"})
@SuppressWarnings({"UnusedDeclaration"})
public StandardServiceRegistryBuilder addInitiator(StandardServiceInitiator initiator) {
initiators.add( initiator );
return this;
@ -227,7 +229,7 @@ public class StandardServiceRegistryBuilder {
*
* @return this, for method chaining
*/
@SuppressWarnings( {"unchecked"})
@SuppressWarnings({"unchecked"})
public StandardServiceRegistryBuilder addService(final Class serviceRole, final Service service) {
providedServices.add( new ProvidedService( serviceRole, service ) );
return this;
@ -289,9 +291,11 @@ public class StandardServiceRegistryBuilder {
@SuppressWarnings("deprecation")
private void applyServiceContributingIntegrators() {
for ( Integrator integrator : bootstrapServiceRegistry.getService( IntegratorService.class ).getIntegrators() ) {
for ( Integrator integrator : bootstrapServiceRegistry.getService( IntegratorService.class )
.getIntegrators() ) {
if ( org.hibernate.integrator.spi.ServiceContributingIntegrator.class.isInstance( integrator ) ) {
org.hibernate.integrator.spi.ServiceContributingIntegrator.class.cast( integrator ).prepareServices( this );
org.hibernate.integrator.spi.ServiceContributingIntegrator.class.cast( integrator ).prepareServices(
this );
}
}
}
@ -332,4 +336,4 @@ public class StandardServiceRegistryBuilder {
( (StandardServiceRegistryImpl) serviceRegistry ).destroy();
}
}
}

View File

@ -50,13 +50,13 @@ import org.hibernate.type.Type;
import org.hibernate.type.descriptor.JdbcTypeNameMapper;
import org.hibernate.type.descriptor.converter.AttributeConverterSqlTypeDescriptorAdapter;
import org.hibernate.type.descriptor.converter.AttributeConverterTypeAdapter;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry;
import org.hibernate.type.descriptor.spi.JdbcRecommendedSqlTypeMappingContext;
import org.hibernate.type.descriptor.sql.JdbcTypeJavaClassMappings;
import org.hibernate.type.descriptor.sql.LobTypeMappings;
import org.hibernate.type.descriptor.sql.NationalizedTypeMappings;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorRegistry;
import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.usertype.DynamicParameterizedType;
@ -576,13 +576,13 @@ public class SimpleValue implements KeyValue {
}
@Override
public JavaTypeDescriptorRegistry getJavaTypeDescriptorRegistry() {
public org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry getJavaTypeDescriptorRegistry() {
return metadata.getTypeConfiguration().getJavaTypeDescriptorRegistry();
}
}
);
final JavaTypeDescriptor entityAttributeJavaTypeDescriptor = jpaAttributeConverter.getDomainJavaTypeDescriptor();
final BasicJavaDescriptor entityAttributeJavaTypeDescriptor = jpaAttributeConverter.getDomainJavaTypeDescriptor();
// build the SqlTypeDescriptor adapter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -591,7 +591,11 @@ public class SimpleValue implements KeyValue {
// corresponding to the AttributeConverter's declared "databaseColumnJavaType" (how we read that value out
// of ResultSets). See JdbcTypeJavaClassMappings for details. Again, given example, this should return
// VARCHAR/CHAR
int jdbcTypeCode = JdbcTypeJavaClassMappings.INSTANCE.determineJdbcTypeCodeForJavaClass( jpaAttributeConverter.getRelationalJavaTypeDescriptor().getJavaType() );
final SqlTypeDescriptor recommendedSqlType = jpaAttributeConverter.getRelationalJavaTypeDescriptor().getJdbcRecommendedSqlType(
// todo (6.0) : handle the other JdbcRecommendedSqlTypeMappingContext methods
metadata::getTypeConfiguration
);
int jdbcTypeCode = recommendedSqlType.getSqlType();
if ( isLob() ) {
if ( LobTypeMappings.INSTANCE.hasCorrespondingLobCode( jdbcTypeCode ) ) {
jdbcTypeCode = LobTypeMappings.INSTANCE.getCorrespondingLobCode( jdbcTypeCode );

View File

@ -10,6 +10,7 @@ import javax.persistence.AttributeConverter;
import org.hibernate.metamodel.model.convert.spi.JpaAttributeConverter;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/**
@ -20,8 +21,8 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
public class JpaAttributeConverterImpl<O,R> implements JpaAttributeConverter<O,R> {
private final ManagedBean<AttributeConverter<O,R>> attributeConverterBean;
private final JavaTypeDescriptor<AttributeConverter<O, R>> converterJavaTypeDescriptor;
private final JavaTypeDescriptor<O> domainJavaTypeDescriptor;
private final JavaTypeDescriptor<R> relationalJavaTypeDescriptor;
private final BasicJavaDescriptor<O> domainJavaTypeDescriptor;
private final BasicJavaDescriptor<R> relationalJavaTypeDescriptor;
public JpaAttributeConverterImpl(
ManagedBean<AttributeConverter<O, R>> attributeConverterBean,
@ -30,8 +31,13 @@ public class JpaAttributeConverterImpl<O,R> implements JpaAttributeConverter<O,R
JavaTypeDescriptor<R> relationalJavaTypeDescriptor) {
this.attributeConverterBean = attributeConverterBean;
this.converterJavaTypeDescriptor = converterJavaTypeDescriptor;
this.domainJavaTypeDescriptor = domainJavaTypeDescriptor;
this.relationalJavaTypeDescriptor = relationalJavaTypeDescriptor;
this.domainJavaTypeDescriptor = (BasicJavaDescriptor<O>) domainJavaTypeDescriptor;
this.relationalJavaTypeDescriptor = (BasicJavaDescriptor<R>) relationalJavaTypeDescriptor;
}
@Override
public ManagedBean<AttributeConverter<O, R>> getConverterBean() {
return attributeConverterBean;
}
@Override
@ -50,12 +56,12 @@ public class JpaAttributeConverterImpl<O,R> implements JpaAttributeConverter<O,R
}
@Override
public JavaTypeDescriptor<O> getDomainJavaTypeDescriptor() {
public BasicJavaDescriptor<O> getDomainJavaTypeDescriptor() {
return domainJavaTypeDescriptor;
}
@Override
public JavaTypeDescriptor<R> getRelationalJavaTypeDescriptor() {
public BasicJavaDescriptor<R> getRelationalJavaTypeDescriptor() {
return relationalJavaTypeDescriptor;
}
}

View File

@ -8,6 +8,8 @@ package org.hibernate.metamodel.model.convert.spi;
import javax.persistence.AttributeConverter;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
/**
@ -17,6 +19,9 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
*/
public interface JpaAttributeConverter<O,R> extends BasicValueConverter<O,R> {
JavaTypeDescriptor<AttributeConverter<O,R>> getConverterJavaTypeDescriptor();
JavaTypeDescriptor<O> getDomainJavaTypeDescriptor();
JavaTypeDescriptor<R> getRelationalJavaTypeDescriptor();
ManagedBean<AttributeConverter<O,R>> getConverterBean();
BasicJavaDescriptor<O> getDomainJavaTypeDescriptor();
BasicJavaDescriptor<R> getRelationalJavaTypeDescriptor();
}

View File

@ -11,7 +11,7 @@ package org.hibernate.service;
*
* @author Steve Ebersole
*/
public interface ServiceRegistry {
public interface ServiceRegistry extends AutoCloseable {
/**
* Retrieve this registry's parent registry.
*
@ -56,4 +56,6 @@ public interface ServiceRegistry {
return service;
}
@Override
void close();
}

View File

@ -25,6 +25,11 @@ public interface ServiceRegistryImplementor extends ServiceRegistry {
*/
<R extends Service> ServiceBinding<R> locateServiceBinding(Class<R> serviceRole);
@Override
default void close() {
destroy();
}
/**
* Release resources
*/

View File

@ -91,7 +91,7 @@ public abstract class AbstractStandardBasicType<T>
@Override
public String[] getRegistrationKeys() {
return registerUnderJavaType()
? new String[] { getName(), javaTypeDescriptor.getJavaTypeClass().getName() }
? new String[] { getName(), javaTypeDescriptor.getJavaType().getName() }
: new String[] { getName() };
}
@ -128,7 +128,7 @@ public abstract class AbstractStandardBasicType<T>
@Override
public final Class getReturnedClass() {
return javaTypeDescriptor.getJavaTypeClass();
return javaTypeDescriptor.getJavaType();
}
@Override

View File

@ -169,6 +169,12 @@ public class BasicTypeRegistry implements Serializable {
register( new CompositeCustomType( type, keys ) );
}
public void unregister(String... keys) {
for ( String key : keys ) {
registry.remove( key );
}
}
public BasicType getRegisteredType(String key) {
return registry.get( key );
}

View File

@ -16,11 +16,13 @@ import java.util.UUID;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.UUIDTypeDescriptor;
import org.hibernate.type.descriptor.sql.BasicBinder;
import org.hibernate.type.descriptor.sql.BasicExtractor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Specialized type mapping for {@link UUID} and the Postgres UUID data type (which is mapped as OTHER in its
@ -59,6 +61,12 @@ public class PostgresUUIDType extends AbstractSingleColumnStandardBasicType<UUID
return true;
}
@Override
@SuppressWarnings("unchecked")
public BasicJavaDescriptor getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
return (BasicJavaDescriptor) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( UUID.class );
}
public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override

View File

@ -16,9 +16,14 @@ import org.hibernate.internal.util.compare.EqualsHelper;
/**
* Abstract adapter for Java type descriptors.
*
* @apiNote This abstract descriptor implements BasicJavaDescriptor
* because we currently only categorize "basic" JavaTypeDescriptors,
* as in the {@link javax.persistence.metamodel.Type.PersistenceType#BASIC}
* sense
*
* @author Steve Ebersole
*/
public abstract class AbstractTypeDescriptor<T> implements JavaTypeDescriptor<T>, Serializable {
public abstract class AbstractTypeDescriptor<T> implements BasicJavaDescriptor<T>, Serializable {
private final Class<T> type;
private final MutabilityPlan<T> mutabilityPlan;
private final Comparator<T> comparator;

View File

@ -0,0 +1,36 @@
/*
* 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.type.descriptor.java;
import org.hibernate.type.descriptor.spi.JdbcRecommendedSqlTypeMappingContext;
import org.hibernate.type.descriptor.sql.JdbcTypeJavaClassMappings;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/**
* @apiNote Currently this is the only high-level categorization of
* JavaTypeDescriptor, but 6.0 will have specific JavaTypeDescriptor
* categorizations for managed-type, mapped-superclass, identifiable-type, entity, embeddable,
* collections.
*
* @author Steve Ebersole
*/
public interface BasicJavaDescriptor<T> extends JavaTypeDescriptor<T> {
/**
* Obtain the "recommended" SQL type descriptor for this Java type. The recommended
* aspect comes from the JDBC spec (mostly).
*
* @param context Contextual information
*
* @return The recommended SQL type descriptor
*/
default SqlTypeDescriptor getJdbcRecommendedSqlType(JdbcRecommendedSqlTypeMappingContext context) {
// match legacy behavior
return context.getTypeConfiguration().getSqlTypeDescriptorRegistry().getDescriptor(
JdbcTypeJavaClassMappings.INSTANCE.determineJdbcTypeCodeForJavaClass( getJavaType() )
);
}
}

View File

@ -15,7 +15,7 @@ import org.hibernate.type.descriptor.WrapperOptions;
*/
public class EnumJavaTypeDescriptor<T extends Enum> extends AbstractTypeDescriptor<T> {
@SuppressWarnings("unchecked")
protected EnumJavaTypeDescriptor(Class<T> type) {
public EnumJavaTypeDescriptor(Class<T> type) {
super( type, ImmutableMutabilityPlan.INSTANCE );
//JavaTypeDescriptorRegistry.INSTANCE.addDescriptor( this );
}

View File

@ -9,6 +9,8 @@ package org.hibernate.type.descriptor.java;
import java.io.Serializable;
import java.util.Comparator;
import org.hibernate.internal.util.compare.ComparableComparator;
import org.hibernate.internal.util.compare.EqualsHelper;
import org.hibernate.type.descriptor.WrapperOptions;
/**
@ -37,17 +39,18 @@ public interface JavaTypeDescriptor<T> extends Serializable {
/**
* Retrieve the mutability plan for this Java type.
*
* @return The mutability plan
*/
public MutabilityPlan<T> getMutabilityPlan();
@SuppressWarnings("unchecked")
default MutabilityPlan<T> getMutabilityPlan() {
return ImmutableMutabilityPlan.INSTANCE;
}
/**
* Retrieve the natural comparator for this type.
*
* @return The natural comparator.
*/
public Comparator<T> getComparator();
default Comparator<T> getComparator() {
return Comparable.class.isAssignableFrom( Comparable.class ) ? ComparableComparator.INSTANCE : null;
}
/**
* Extract a proper hash code for this value.
@ -56,7 +59,12 @@ public interface JavaTypeDescriptor<T> extends Serializable {
*
* @return The extracted hash code.
*/
public int extractHashCode(T value);
default int extractHashCode(T value) {
if ( value == null ) {
throw new IllegalArgumentException( "Value to extract hashCode from cannot be null" );
}
return value.hashCode();
}
/**
* Determine if two instances are equal
@ -66,7 +74,9 @@ public interface JavaTypeDescriptor<T> extends Serializable {
*
* @return True if the two are considered equal; false otherwise.
*/
public boolean areEqual(T one, T another);
default boolean areEqual(T one, T another) {
return EqualsHelper.areEqual( one, another );
}
/**
* Extract a loggable representation of the value.
@ -75,11 +85,15 @@ public interface JavaTypeDescriptor<T> extends Serializable {
*
* @return The loggable representation
*/
public String extractLoggableRepresentation(T value);
default String extractLoggableRepresentation(T value) {
return toString( value );
}
public String toString(T value);
default String toString(T value) {
return value == null ? "null" : value.toString();
}
public T fromString(String string);
T fromString(String string);
/**
* Unwrap an instance of our handled Java type into the requested type.
@ -97,7 +111,7 @@ public interface JavaTypeDescriptor<T> extends Serializable {
*
* @return The unwrapped value.
*/
public <X> X unwrap(T value, Class<X> type, WrapperOptions options);
<X> X unwrap(T value, Class<X> type, WrapperOptions options);
/**
* Wrap a value as our handled Java type.
@ -110,5 +124,5 @@ public interface JavaTypeDescriptor<T> extends Serializable {
*
* @return The wrapped value.
*/
public <X> T wrap(X value, WrapperOptions options);
<X> T wrap(X value, WrapperOptions options);
}

View File

@ -7,7 +7,6 @@
package org.hibernate.type.descriptor.java;
import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.HibernateException;
@ -16,6 +15,7 @@ import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.spi.RegistryHelper;
import org.hibernate.type.spi.TypeConfiguration;
/**
@ -84,8 +84,7 @@ public class JavaTypeDescriptorRegistry implements Serializable {
}
private JavaTypeDescriptor addDescriptorInternal(JavaTypeDescriptor descriptor) {
JavaTypeDescriptor javaTypeDescriptor = descriptorsByClass.put( descriptor.getJavaType(), descriptor );
return javaTypeDescriptor;
return descriptorsByClass.put( descriptor.getJavaType(), descriptor );
}
/**
@ -113,43 +112,26 @@ public class JavaTypeDescriptorRegistry implements Serializable {
*/
@Deprecated
@SuppressWarnings("unchecked")
public <T> JavaTypeDescriptor<T> getDescriptor(Class<T> cls) {
if ( cls == null ) {
throw new IllegalArgumentException( "Class passed to locate Java type descriptor cannot be null" );
}
public <J> JavaTypeDescriptor<J> getDescriptor(Class<J> cls) {
return RegistryHelper.INSTANCE.resolveDescriptor(
descriptorsByClass,
cls,
() -> {
if ( Serializable.class.isAssignableFrom( cls ) ) {
return new SerializableTypeDescriptor( cls );
}
JavaTypeDescriptor<T> descriptor = descriptorsByClass.get( cls );
if ( descriptor != null ) {
return descriptor;
}
log.debugf(
"Could not find matching JavaTypeDescriptor for requested Java class [%s]; using fallback. " +
"This means Hibernate does not know how to perform certain basic operations in relation to this Java type." +
"",
cls.getName()
);
checkEqualsAndHashCode( cls );
if ( cls.isEnum() ) {
descriptor = new EnumJavaTypeDescriptor( cls );
descriptorsByClass.put( cls, descriptor );
return descriptor;
}
// find the first "assignable" match
for ( Map.Entry<Class, JavaTypeDescriptor> entry : descriptorsByClass.entrySet() ) {
if ( entry.getKey().isAssignableFrom( cls ) ) {
log.debugf( "Using cached JavaTypeDescriptor instance for Java class [%s]", cls.getName() );
return entry.getValue();
}
}
if ( Serializable.class.isAssignableFrom( cls ) ) {
return new SerializableTypeDescriptor( cls );
}
log.debugf(
"Could not find matching JavaTypeDescriptor for requested Java class [%s]; using fallback. " +
"This means Hibernate does not know how to perform certain basic operations in relation to this Java type." +
"",
cls.getName()
return new FallbackJavaTypeDescriptor<>( cls );
}
);
checkEqualsAndHashCode( cls );
return new FallbackJavaTypeDescriptor<>( cls );
}
@SuppressWarnings("unchecked")

View File

@ -29,11 +29,9 @@ public class SerializableTypeDescriptor<T extends Serializable> extends Abstract
// unfortunately the param types cannot be the same so use something other than 'T' here to make that obvious
public static class SerializableMutabilityPlan<S extends Serializable> extends MutableMutabilityPlan<S> {
public static final SerializableMutabilityPlan<Serializable> INSTANCE = new SerializableMutabilityPlan<>();
public static final SerializableMutabilityPlan<Serializable> INSTANCE
= new SerializableMutabilityPlan<Serializable>( );
public SerializableMutabilityPlan() {
private SerializableMutabilityPlan() {
}
@Override
@ -124,7 +122,7 @@ public class SerializableTypeDescriptor<T extends Serializable> extends Abstract
throw new HibernateException( e );
}
}
else if ( getJavaTypeClass().isInstance( value ) ) {
else if ( getJavaType().isInstance( value ) ) {
return (T) value;
}
throw unknownWrap( value.getClass() );
@ -136,6 +134,6 @@ public class SerializableTypeDescriptor<T extends Serializable> extends Abstract
@SuppressWarnings({ "unchecked" })
protected T fromBytes(byte[] bytes) {
return (T) SerializationHelper.deserialize( bytes, getJavaTypeClass().getClassLoader() );
return (T) SerializationHelper.deserialize( bytes, getJavaType().getClassLoader() );
}
}

View File

@ -7,10 +7,13 @@
package org.hibernate.type.descriptor.java.spi;
import java.io.Serializable;
import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger;
/**
* Basically a map from {@link Class} -> {@link JavaTypeDescriptor}
*
@ -19,25 +22,41 @@ import org.hibernate.type.spi.TypeConfiguration;
*
* @since 5.3
*/
public class JavaTypeDescriptorRegistry
extends org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry
implements Serializable {
public class JavaTypeDescriptorRegistry implements Serializable {
private static final Logger log = Logger.getLogger( JavaTypeDescriptorRegistry.class );
private final TypeConfiguration typeConfiguration;
private final org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry javaTypeDescriptorRegistry;
private ConcurrentHashMap<Class, JavaTypeDescriptor> descriptorsByClass = new ConcurrentHashMap<>();
@SuppressWarnings("unused")
public JavaTypeDescriptorRegistry(TypeConfiguration typeConfiguration) {
this.typeConfiguration = typeConfiguration;
javaTypeDescriptorRegistry = org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry.INSTANCE;
}
@Override
public <T> JavaTypeDescriptor<T> getDescriptor(Class<T> javaType) {
return javaTypeDescriptorRegistry.getDescriptor( javaType );
return RegistryHelper.INSTANCE.resolveDescriptor(
descriptorsByClass,
javaType,
() -> {
log.debugf(
"Could not find matching scoped JavaTypeDescriptor for requested Java class [%s]; " +
"falling back to static registry",
javaType.getName()
);
return org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry.INSTANCE.getDescriptor( javaType );
}
);
}
@Override
public void addDescriptor(JavaTypeDescriptor descriptor) {
javaTypeDescriptorRegistry.addDescriptor( descriptor );
JavaTypeDescriptor old = descriptorsByClass.put( descriptor.getJavaType(), descriptor );
if ( old != null ) {
log.debugf(
"JavaTypeDescriptorRegistry entry replaced : %s -> %s (was %s)",
descriptor.getJavaType(),
descriptor,
old
);
}
}
}

View File

@ -0,0 +1,63 @@
/*
* 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.type.descriptor.java.spi;
import java.io.Serializable;
import java.util.Map;
import java.util.function.Supplier;
import org.hibernate.type.descriptor.java.EnumJavaTypeDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.SerializableTypeDescriptor;
import org.jboss.logging.Logger;
/**
* @author Steve Ebersole
*/
public class RegistryHelper {
private static final Logger log = Logger.getLogger( RegistryHelper.class );
/**
* Singleton access
*/
public static final RegistryHelper INSTANCE = new RegistryHelper();
private RegistryHelper() {
}
@SuppressWarnings("unchecked")
public <J> JavaTypeDescriptor<J> resolveDescriptor(
Map<Class,JavaTypeDescriptor> descriptorsByClass,
Class<J> cls,
Supplier<JavaTypeDescriptor<J>> defaultValueSupplier) {
if ( cls == null ) {
throw new IllegalArgumentException( "Class passed to locate JavaTypeDescriptor cannot be null" );
}
JavaTypeDescriptor<J> descriptor = descriptorsByClass.get( cls );
if ( descriptor != null ) {
return descriptor;
}
if ( cls.isEnum() ) {
descriptor = new EnumJavaTypeDescriptor( cls );
descriptorsByClass.put( cls, descriptor );
return descriptor;
}
// find the first "assignable" match
for ( Map.Entry<Class, JavaTypeDescriptor> entry : descriptorsByClass.entrySet() ) {
if ( entry.getKey().isAssignableFrom( cls ) ) {
log.debugf( "Using cached JavaTypeDescriptor instance for Java class [%s]", cls.getName() );
return entry.getValue();
}
}
return defaultValueSupplier.get();
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.type.descriptor.spi;
import java.sql.Types;
import javax.persistence.EnumType;
import org.hibernate.type.spi.TypeConfiguration;
/**
* More-or-less a parameter-object intended for use in determining the SQL/JDBC type recommended
* by the JDBC spec (explicitly or implicitly) for a given Java type.
*
* @see org.hibernate.type.descriptor.java.BasicJavaDescriptor#getJdbcRecommendedSqlType
*
* @author Steve Ebersole
*/
public interface JdbcRecommendedSqlTypeMappingContext {
/**
* Was nationalized character datatype requested for the given Java type?
*
* @return {@code true} if nationalized character datatype should be used; {@code false} otherwise.
*/
default boolean isNationalized() {
return false;
}
/**
* Was LOB datatype requested for the given Java type?
*
* @return {@code true} if LOB datatype should be used; {@code false} otherwise.
*/
default boolean isLob() {
return false;
}
/**
* For enum mappings, what style of storage was requested (name vs. ordinal)?
*
* @return The enum type.
*/
default EnumType getEnumeratedType() {
return EnumType.ORDINAL;
}
/**
* When mapping a boolean type to the database what is the preferred SQL type code to use?
* <p/>
* Specifically names the key into the
* {@link org.hibernate.type.descriptor.sql.spi.SqlTypeDescriptorRegistry}.
*/
default int getPreferredSqlTypeCodeForBoolean() {
return Types.BOOLEAN;
}
/**
* Provides access to the TypeConfiguration for access to various type-system registries.
*/
TypeConfiguration getTypeConfiguration();
}

View File

@ -10,7 +10,9 @@ import java.io.Serializable;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Descriptor for the <tt>SQL</tt>/<tt>JDBC</tt> side of a value mapping.
@ -38,6 +40,15 @@ public interface SqlTypeDescriptor extends Serializable {
*/
boolean canBeRemapped();
@SuppressWarnings("unchecked")
default <T> BasicJavaDescriptor<T> getJdbcRecommendedJavaTypeMapping(TypeConfiguration typeConfiguration) {
// match legacy behavior
return (BasicJavaDescriptor<T>) typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor(
JdbcTypeJavaClassMappings.INSTANCE.determineJavaClassForJdbcTypeCode( getSqlType() )
);
}
/**
* Get the binder (setting JDBC in-going parameter values) capable of handling values of the type described by the
* passed descriptor.

View File

@ -151,7 +151,7 @@ public class SqlTypeDescriptorRegistry implements Serializable {
@Override
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
if ( Serializable.class.isAssignableFrom( javaTypeDescriptor.getJavaTypeClass() ) ) {
if ( Serializable.class.isAssignableFrom( javaTypeDescriptor.getJavaType() ) ) {
return VarbinaryTypeDescriptor.INSTANCE.getBinder( javaTypeDescriptor );
}
@ -173,7 +173,7 @@ public class SqlTypeDescriptorRegistry implements Serializable {
@Override
@SuppressWarnings("unchecked")
public ValueExtractor getExtractor(JavaTypeDescriptor javaTypeDescriptor) {
if ( Serializable.class.isAssignableFrom( javaTypeDescriptor.getJavaTypeClass() ) ) {
if ( Serializable.class.isAssignableFrom( javaTypeDescriptor.getJavaType() ) ) {
return VarbinaryTypeDescriptor.INSTANCE.getExtractor( javaTypeDescriptor );
}

View File

@ -0,0 +1,95 @@
/*
* 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.converter.custom;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataBuilderImplementor;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.tool.schema.Action;
import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author Steve Ebersole
*/
public class CustomTypeConverterTest extends BaseUnitTestCase {
@Test
public void testConverterAppliedStaticRegistration() {
// this is how we told users to do it previously using the static reference -
// make sure it still works for now
org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry.INSTANCE.addDescriptor( MyCustomJavaTypeDescriptor.INSTANCE );
org.hibernate.type.descriptor.sql.SqlTypeDescriptorRegistry.INSTANCE.addDescriptor( MyCustomSqlTypeDescriptor.INSTANCE );
try ( final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
.applySetting( AvailableSettings.HBM2DDL_AUTO, Action.CREATE_DROP )
.build() ) {
final MetadataSources metadataSources = new MetadataSources( ssr )
.addAnnotatedClass( MyCustomConverter.class )
.addAnnotatedClass( MyEntity.class );
final MetadataBuilderImplementor metadataBuilder = (MetadataBuilderImplementor) metadataSources.getMetadataBuilder();
final TypeConfiguration bootTypeConfiguration = metadataBuilder.getBootstrapContext().getTypeConfiguration();
performAssertions( metadataBuilder, bootTypeConfiguration );
}
}
@Test
public void testConverterAppliedScopedRegistration() {
try ( final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
.applySetting( AvailableSettings.HBM2DDL_AUTO, Action.CREATE_DROP )
.build() ) {
final MetadataSources metadataSources = new MetadataSources( ssr )
.addAnnotatedClass( MyCustomConverter.class )
.addAnnotatedClass( MyEntity.class );
final MetadataBuilderImplementor metadataBuilder = (MetadataBuilderImplementor) metadataSources.getMetadataBuilder();
// now the new scoped way
final TypeConfiguration bootTypeConfiguration = metadataBuilder.getBootstrapContext().getTypeConfiguration();
bootTypeConfiguration.getJavaTypeDescriptorRegistry()
.addDescriptor( MyCustomJavaTypeDescriptor.INSTANCE );
bootTypeConfiguration.getSqlTypeDescriptorRegistry()
.addDescriptor( MyCustomSqlTypeDescriptor.INSTANCE );
performAssertions( metadataBuilder, bootTypeConfiguration );
}
}
protected void performAssertions(
MetadataBuilderImplementor metadataBuilder,
TypeConfiguration bootTypeConfiguration) {
try ( final SessionFactoryImplementor sessionFactory = (SessionFactoryImplementor) metadataBuilder.build().buildSessionFactory()) {
assertThat(
sessionFactory.getMetamodel().getTypeConfiguration(),
sameInstance( bootTypeConfiguration )
);
assertThat(
bootTypeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( MyCustomJavaType.class ),
sameInstance( MyCustomJavaTypeDescriptor.INSTANCE )
);
assertThat(
bootTypeConfiguration.getSqlTypeDescriptorRegistry().getDescriptor( MyCustomSqlTypeDescriptor.INSTANCE.getSqlType() ),
sameInstance( MyCustomSqlTypeDescriptor.INSTANCE )
);
final EntityPersister entityPersister = sessionFactory.getMetamodel().entityPersister( MyEntity.class );
entityPersister.getPropertyType( "customType" );
}
}
}

View File

@ -0,0 +1,26 @@
/*
* 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.converter.custom;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
/**
* @author Steve Ebersole
*/
@Converter( autoApply = true )
public class MyCustomConverter implements AttributeConverter<MyCustomJavaType, String> {
@Override
public String convertToDatabaseColumn(MyCustomJavaType attribute) {
return null;
}
@Override
public MyCustomJavaType convertToEntityAttribute(String dbData) {
return null;
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.converter.custom;
/**
* @author Steve Ebersole
*/
public class MyCustomJavaType implements Comparable<MyCustomJavaType> {
private String payload;
public MyCustomJavaType() {
this( null );
}
public MyCustomJavaType(String payload) {
this.payload = payload;
}
public String getPayload() {
return payload;
}
public void setPayload(String payload) {
this.payload = payload;
}
@Override
public int compareTo(MyCustomJavaType other) {
if ( getPayload() == null ) {
return -1;
}
if ( other == null || other.getPayload() == null ) {
return 1;
}
return getPayload().compareTo( other.getPayload() );
}
}

View File

@ -0,0 +1,77 @@
/*
* 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.converter.custom;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicJavaDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.descriptor.java.MutableMutabilityPlan;
import org.hibernate.type.descriptor.spi.JdbcRecommendedSqlTypeMappingContext;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/**
* @author Steve Ebersole
*/
public class MyCustomJavaTypeDescriptor implements BasicJavaDescriptor<MyCustomJavaType> {
/**
* Singleton access
*/
public static final MyCustomJavaTypeDescriptor INSTANCE = new MyCustomJavaTypeDescriptor();
private final MutableMutabilityPlan<MyCustomJavaType> mutabilityPlan = new MutableMutabilityPlan<MyCustomJavaType>() {
@Override
protected MyCustomJavaType deepCopyNotNull(MyCustomJavaType value) {
return value == null ? null : new MyCustomJavaType( value.getPayload() );
}
};
private MyCustomJavaTypeDescriptor() {
}
@Override
public Class getJavaTypeClass() {
return MyCustomJavaType.class;
}
@Override
public SqlTypeDescriptor getJdbcRecommendedSqlType(JdbcRecommendedSqlTypeMappingContext context) {
return MyCustomSqlTypeDescriptor.INSTANCE;
}
@Override
public MutabilityPlan getMutabilityPlan() {
return mutabilityPlan;
}
@Override
public MyCustomJavaType fromString(String string) {
return StringHelper.isEmpty( string ) ? null : new MyCustomJavaType( string );
}
@Override
public MyCustomJavaType wrap(Object value, WrapperOptions options) {
if ( value == null ) {
return null;
}
else if ( String.class.isInstance( value ) ) {
return new MyCustomJavaType( (String) value );
}
throw new UnsupportedOperationException( "Wrapping value as MyCustomJavaType only supported for String or MyCustomJdbcType : " + value );
}
@Override
public Object unwrap(MyCustomJavaType value, Class type, WrapperOptions options) {
if ( String.class.isAssignableFrom( type ) ) {
return value.getPayload();
}
throw new UnsupportedOperationException( "Unwrapping MyCustomJavaType value only supported for String or MyCustomJdbcType : " + value );
}
}

View File

@ -0,0 +1,83 @@
/*
* 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.converter.custom;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.sql.BasicBinder;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.VarcharTypeDescriptor;
/**
* A custom SqlTypeDescriptor. For example, this might be used to provide support
* for a "non-standard" SQL type or to provide some special handling of values (e.g.
* Oracle's dodgy handling of `""` as `null` but only in certain uses).
*
* This descriptor shows an example of replacing how VARCHAR values are handled.
*
* @author Steve Ebersole
*/
public class MyCustomSqlTypeDescriptor implements SqlTypeDescriptor {
/**
* Singleton access
*/
public static final MyCustomSqlTypeDescriptor INSTANCE = new MyCustomSqlTypeDescriptor();
private MyCustomSqlTypeDescriptor() {
}
@Override
public int getSqlType() {
// given the Oracle example above we might want to replace the
// handling of VARCHAR
return Types.VARCHAR;
}
@Override
public boolean canBeRemapped() {
return false;
}
@Override
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>( javaTypeDescriptor, this ) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
final String valueStr = javaTypeDescriptor.unwrap( value, String.class, options );
if ( valueStr == null || valueStr.trim().isEmpty() ) {
st.setNull( index, getSqlType() );
}
else {
st.setString( index, valueStr );
}
}
@Override
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) throws SQLException {
final String valueStr = javaTypeDescriptor.unwrap( value, String.class, options );
if ( valueStr == null || valueStr.trim().isEmpty() ) {
st.setNull( name, getSqlType() );
}
else {
st.setString( name, valueStr );
}
}
};
}
@Override
public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor) {
return VarcharTypeDescriptor.INSTANCE.getExtractor( javaTypeDescriptor );
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.converter.custom;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* @author Steve Ebersole
*/
@Entity
@Table( name = "CUST_TYPE_CONV_ENTITY")
public class MyEntity {
private Integer id;
private MyCustomJavaType customType;
public MyEntity() {
}
public MyEntity(Integer id, MyCustomJavaType customType) {
this.id = id;
this.customType = customType;
}
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
// NOTE : this AttributeConverter should be auto-applied here
@Basic
public MyCustomJavaType getCustomType() {
return customType;
}
public void setCustomType(MyCustomJavaType customType) {
this.customType = customType;
}
}

View File

@ -0,0 +1,12 @@
/*
* 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
*/
/**
* Test illustrating use of an AttributeConverter over custom
* java / sql type descriptors
*/
package org.hibernate.test.converter.custom;

View File

@ -83,7 +83,7 @@ public class Java8DateTimeTests extends BaseNonConfigCoreFunctionalTestCase {
String.format(
"%s (%s) -> %s",
propertyBinding.getName(),
javaTypeDescriptor.getJavaTypeClass().getSimpleName(),
javaTypeDescriptor.getJavaType().getSimpleName(),
javaTypeDescriptor.toString( propertyBinding.getGetter( TheEntity.class ).get( theEntity ) )
)
);

View File

@ -1,3 +1,5 @@
import org.apache.tools.ant.filters.ReplaceTokens
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
@ -8,13 +10,27 @@
apply from: rootProject.file( 'gradle/published-java-module.gradle' )
apply plugin: 'hibernate-matrix-testing'
ext {
db = project.hasProperty('db') ? project.getProperty('db') : 'h2'
dbBundle = [
h2 : [
'db.dialect' : 'org.hibernate.spatial.dialect.h2geodb.GeoDBDialect',
'jdbc.driver': 'org.h2.Driver',
'jdbc.user' : 'sa',
'jdbc.pass' : '',
'jdbc.url' : 'jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;LOCK_TIMEOUT=10000',
]
]
}
description = 'Integrate support for Spatial/GIS data into Hibernate O/RM'
dependencies {
compile(project(':hibernate-core'))
compile( project(':hibernate-core') )
compile( libraries.geolatte )
compile( 'org.postgresql:postgresql:42.1.4' )
compile('org.postgresql:postgresql:42.1.4')
compile([group: 'org.geolatte', name: 'geolatte-geom', version: '1.3.0'])
compile(libraries.dom4j) {
transitive = false
}
@ -40,3 +56,25 @@ sourceSets.test.resources {
setSrcDirs(['src/test/java', 'src/test/resources'])
}
processTestResources {
doLast {
copy {
from( sourceSets.test.java.srcDirs ) {
include '**/*.properties'
include '**/*.xml'
}
into sourceSets.test.java.outputDir
}
copy {
from file( 'src/test/resources' )
into file( "${buildDir}/resources/test" )
exclude 'src/test/resources/arquillian.xml'
exclude 'src/test/resources/hibernate.properties'
}
copy {
from file( 'src/test/resources/hibernate.properties' )
into file( "${buildDir}/resources/test" )
filter( ReplaceTokens, tokens: dbBundle[db] )
}
}
}

View File

@ -7,9 +7,12 @@
package org.hibernate.spatial;
import java.sql.Types;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.AbstractTypeDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry;
import org.hibernate.type.descriptor.spi.JdbcRecommendedSqlTypeMappingContext;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.geolatte.geom.Geometry;
import org.geolatte.geom.codec.Wkt;
@ -35,6 +38,11 @@ public class GeolatteGeometryJavaTypeDescriptor extends AbstractTypeDescriptor<G
super( Geometry.class );
}
@Override
public SqlTypeDescriptor getJdbcRecommendedSqlType(JdbcRecommendedSqlTypeMappingContext context) {
return context.getTypeConfiguration().getSqlTypeDescriptorRegistry().getDescriptor( Types.ARRAY );
}
@Override
public String toString(Geometry value) {
return value.toString();

View File

@ -27,6 +27,18 @@ import org.geolatte.geom.Polygon;
*/
public class GeolatteGeometryType extends AbstractSingleColumnStandardBasicType<Geometry> implements Spatial {
public static final String[] REG_KEYS = {
Geometry.class.getCanonicalName(),
Point.class.getCanonicalName(),
Polygon.class.getCanonicalName(),
MultiPolygon.class.getCanonicalName(),
LineString.class.getCanonicalName(),
MultiLineString.class.getCanonicalName(),
MultiPoint.class.getCanonicalName(),
GeometryCollection.class.getCanonicalName(),
"geolatte_geometry"
};
/**
* Constructs an instance with the specified {@code SqlTypeDescriptor}
*
@ -38,17 +50,7 @@ public class GeolatteGeometryType extends AbstractSingleColumnStandardBasicType<
@Override
public String[] getRegistrationKeys() {
return new String[] {
Geometry.class.getCanonicalName(),
Point.class.getCanonicalName(),
Polygon.class.getCanonicalName(),
MultiPolygon.class.getCanonicalName(),
LineString.class.getCanonicalName(),
MultiLineString.class.getCanonicalName(),
MultiPoint.class.getCanonicalName(),
GeometryCollection.class.getCanonicalName(),
"geolatte_geometry"
};
return REG_KEYS;
}
@Override

View File

@ -0,0 +1,36 @@
/*
* 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.spatial.testing.converter;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import org.hibernate.spatial.dialect.h2geodb.GeoDbWkb;
import org.geolatte.geom.Geometry;
/**
* @author Steve Ebersole
*/
@Converter( autoApply = true )
public class GeometryConverter implements AttributeConverter<Geometry,byte[]> {
@Override
public byte[] convertToDatabaseColumn(Geometry attribute) {
if ( attribute == null ) {
return null;
}
return GeoDbWkb.to( attribute );
}
@Override
public Geometry convertToEntityAttribute(byte[] dbData) {
if ( dbData == null ) {
return null;
}
return GeoDbWkb.from( dbData );
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.spatial.testing.converter;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.boot.spi.MetadataBuilderImplementor;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.model.convert.spi.JpaAttributeConverter;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.spatial.GeolatteGeometryJavaTypeDescriptor;
import org.hibernate.spatial.dialect.h2geodb.GeoDBDialect;
import org.hibernate.tool.schema.Action;
import org.hibernate.type.descriptor.converter.AttributeConverterTypeAdapter;
import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.Test;
import org.geolatte.geom.Geometry;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
/**
* @author Steve Ebersole
*/
public class GeometryConverterTest extends BaseUnitTestCase {
@Test
public void testConverterUsage() {
try ( final StandardServiceRegistry ssr = new StandardServiceRegistryBuilder()
.applySetting( AvailableSettings.DIALECT, GeoDBDialect.class )
.applySetting( AvailableSettings.HBM2DDL_AUTO, Action.CREATE_DROP )
.build() ) {
final MetadataSources metadataSources = new MetadataSources( ssr )
.addAnnotatedClass( GeometryConverter.class )
.addAnnotatedClass( MyEntity.class );
final MetadataBuilderImplementor metadataBuilder = (MetadataBuilderImplementor) metadataSources.getMetadataBuilder();
try ( final SessionFactoryImplementor sessionFactory =
(SessionFactoryImplementor) metadataBuilder.build().buildSessionFactory() ) {
final TypeConfiguration typeConfiguration = sessionFactory.getMetamodel().getTypeConfiguration();
assertThat(
typeConfiguration.getJavaTypeDescriptorRegistry().getDescriptor( Geometry.class ),
sameInstance( GeolatteGeometryJavaTypeDescriptor.INSTANCE )
);
// todo (5.3) : what to assert wrt to SqlTypeDescriptor? Anything?
final EntityPersister entityPersister = sessionFactory.getMetamodel().entityPersister( MyEntity.class );
final AttributeConverterTypeAdapter geometryAttributeType = assertTyping(
AttributeConverterTypeAdapter.class,
entityPersister.getPropertyType( "geometry" )
);
final JpaAttributeConverter converter = assertTyping(
JpaAttributeConverter.class,
geometryAttributeType.getAttributeConverter()
);
assert GeometryConverter.class.equals( converter.getConverterBean().getBeanClass() );
}
}
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.spatial.testing.converter;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.geolatte.geom.Geometry;
/**
* @author Steve Ebersole
*/
@Entity
@Table( name = "SP_CUST_TYPE_CONV_ENTITY")
public class MyEntity {
private Integer id;
private Geometry geometry;
public MyEntity() {
}
public MyEntity(Integer id, Geometry geometry) {
this.id = id;
this.geometry = geometry;
}
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
// NOTE : the AttributeConverter should be auto-applied here
@Basic
public Geometry getGeometry() {
return geometry;
}
public void setGeometry(Geometry geometry) {
this.geometry = geometry;
}
}

View File

@ -0,0 +1,12 @@
/*
* 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
*/
/**
* Make sure that JPA {@link javax.persistence.AttributeConverter}
* implementations using Geolatte types work with hibernate-spatial
*/
package org.hibernate.spatial.testing.converter;