From 875e84b930380534180a63ffbf86120a241eff2c Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Fri, 7 Jun 2024 11:27:48 -0500 Subject: [PATCH] HHH-18060 - HbXmlTransformer work * "special" basic type handling --- .../xml/internal/XmlAnnotationHelper.java | 349 +++++++++++++++++- .../attr/BasicIdAttributeProcessing.java | 7 +- .../type/EntityWithElementCollections.java | 25 ++ .../type/SpecialTypeTransformationTests.java | 127 +++++++ .../src/test/resources/hibernate.properties | 2 - .../mappings/models/hbm/type/basics.xml | 28 ++ .../models/hbm/type/element-collections.xml | 47 +++ 7 files changed, 579 insertions(+), 6 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/type/EntityWithElementCollections.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/type/SpecialTypeTransformationTests.java create mode 100644 hibernate-core/src/test/resources/mappings/models/hbm/type/basics.xml create mode 100644 hibernate-core/src/test/resources/mappings/models/hbm/type/element-collections.xml diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java index 4a1c7313cb..d4646a9a2b 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java @@ -10,8 +10,30 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.math.BigDecimal; import java.math.BigInteger; +import java.net.InetAddress; +import java.net.URL; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.NClob; +import java.sql.Timestamp; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.time.Year; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.ArrayList; +import java.util.Calendar; +import java.util.Currency; +import java.util.GregorianCalendar; import java.util.List; +import java.util.Locale; +import java.util.TimeZone; import java.util.UUID; import java.util.function.Consumer; @@ -136,6 +158,41 @@ import org.hibernate.models.spi.MutableClassDetails; import org.hibernate.models.spi.MutableMemberDetails; import org.hibernate.models.spi.SourceModelBuildingContext; import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.java.BasicJavaType; +import org.hibernate.type.descriptor.java.BigDecimalJavaType; +import org.hibernate.type.descriptor.java.BigIntegerJavaType; +import org.hibernate.type.descriptor.java.BlobJavaType; +import org.hibernate.type.descriptor.java.BooleanJavaType; +import org.hibernate.type.descriptor.java.ByteJavaType; +import org.hibernate.type.descriptor.java.CalendarJavaType; +import org.hibernate.type.descriptor.java.CharacterJavaType; +import org.hibernate.type.descriptor.java.ClassJavaType; +import org.hibernate.type.descriptor.java.ClobJavaType; +import org.hibernate.type.descriptor.java.CurrencyJavaType; +import org.hibernate.type.descriptor.java.DateJavaType; +import org.hibernate.type.descriptor.java.DoubleJavaType; +import org.hibernate.type.descriptor.java.DurationJavaType; +import org.hibernate.type.descriptor.java.InetAddressJavaType; +import org.hibernate.type.descriptor.java.InstantJavaType; +import org.hibernate.type.descriptor.java.IntegerJavaType; +import org.hibernate.type.descriptor.java.LocalDateJavaType; +import org.hibernate.type.descriptor.java.LocalDateTimeJavaType; +import org.hibernate.type.descriptor.java.LocalTimeJavaType; +import org.hibernate.type.descriptor.java.LocaleJavaType; +import org.hibernate.type.descriptor.java.LongJavaType; +import org.hibernate.type.descriptor.java.NClobJavaType; +import org.hibernate.type.descriptor.java.OffsetDateTimeJavaType; +import org.hibernate.type.descriptor.java.OffsetTimeJavaType; +import org.hibernate.type.descriptor.java.ShortJavaType; +import org.hibernate.type.descriptor.java.StringJavaType; +import org.hibernate.type.descriptor.java.TimeZoneJavaType; +import org.hibernate.type.descriptor.java.UUIDJavaType; +import org.hibernate.type.descriptor.java.UrlJavaType; +import org.hibernate.type.descriptor.java.YearJavaType; +import org.hibernate.type.descriptor.java.ZoneIdJavaType; +import org.hibernate.type.descriptor.java.ZoneOffsetJavaType; +import org.hibernate.type.descriptor.java.ZonedDateTimeJavaType; +import org.hibernate.usertype.UserType; import jakarta.persistence.AssociationOverride; import jakarta.persistence.AttributeOverride; @@ -146,6 +203,7 @@ import jakarta.persistence.EnumType; import jakarta.persistence.Index; import jakarta.persistence.PrimaryKeyJoinColumn; import jakarta.persistence.SecondaryTable; +import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; import jakarta.persistence.UniqueConstraint; import org.checkerframework.checker.nullness.qual.Nullable; @@ -244,20 +302,305 @@ public class XmlAnnotationHelper { JaxbUserTypeImpl jaxbType, MutableMemberDetails memberDetails, XmlDocumentContext xmlDocumentContext) { - if ( jaxbType == null ) { + if ( jaxbType == null || StringHelper.isEmpty( jaxbType.getValue() ) ) { return; } + final boolean wasSpecialCase = handleSpecialBasicTypeCases( jaxbType, memberDetails, xmlDocumentContext ); + if ( wasSpecialCase ) { + return; + } + + final ClassDetails userTypeImpl = resolveJavaType( jaxbType.getValue(), xmlDocumentContext ); + assert userTypeImpl.isImplementor( UserType.class ); final TypeAnnotation typeAnn = (TypeAnnotation) memberDetails.applyAnnotationUsage( HibernateAnnotations.TYPE, xmlDocumentContext.getModelBuildingContext() ); - - final ClassDetails userTypeImpl = resolveJavaType( jaxbType.getValue(), xmlDocumentContext ); typeAnn.value( userTypeImpl.toJavaClass() ); typeAnn.parameters( collectParameters( jaxbType.getParameters(), xmlDocumentContext ) ); } + private static boolean handleSpecialBasicTypeCases( + JaxbUserTypeImpl jaxbType, + MutableMemberDetails memberDetails, + XmlDocumentContext xmlDocumentContext) { + if ( jaxbType.getValue().equalsIgnoreCase( "char" ) + || jaxbType.getValue().equalsIgnoreCase( "character" ) + || Character.class.getName().equalsIgnoreCase( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, CharacterJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "string" ) + || String.class.getName().equalsIgnoreCase( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, StringJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "byte" ) + || Byte.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, ByteJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "boolean" ) + || Boolean.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, BooleanJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "short" ) + || Short.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, ShortJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "int" ) + || jaxbType.getValue().equalsIgnoreCase( "integer" ) + || Integer.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, IntegerJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "long" ) + || Long.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, LongJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "double" ) + || Double.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, DoubleJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "float" ) + || Float.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, DoubleJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "biginteger" ) + || jaxbType.getValue().equalsIgnoreCase( "big_integer" ) + || BigInteger.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, BigIntegerJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "bigdecimal" ) + || jaxbType.getValue().equalsIgnoreCase( "big_decimal" ) + || BigDecimal.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, BigDecimalJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "uuid" ) + || UUID.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, UUIDJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "url" ) + || URL.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, UrlJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "inet" ) + || jaxbType.getValue().equalsIgnoreCase( "inetaddress" ) + || jaxbType.getValue().equalsIgnoreCase( "inet_address" ) + || InetAddress.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, InetAddressJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "currency" ) + || Currency.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, CurrencyJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "locale" ) + || Locale.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, LocaleJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "class" ) + || Class.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, ClassJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "blob" ) + || Blob.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, BlobJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "clob" ) + || Clob.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, ClobJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "nclob" ) + || NClob.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, NClobJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "instant" ) + || Instant.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, InstantJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "duration" ) + || Duration.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, DurationJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "year" ) + || Year.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, YearJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "localdatetime" ) + || jaxbType.getValue().equalsIgnoreCase( "local_date_time" ) + || LocalDateTime.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, LocalDateTimeJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "localdate" ) + || jaxbType.getValue().equalsIgnoreCase( "local_date" ) + || LocalDate.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, LocalDateJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "localtime" ) + || jaxbType.getValue().equalsIgnoreCase( "local_time" ) + || LocalTime.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, LocalTimeJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "zoneddatetime" ) + || jaxbType.getValue().equalsIgnoreCase( "zoned_date_time" ) + || ZonedDateTime.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, ZonedDateTimeJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "offsetdatetime" ) + || jaxbType.getValue().equalsIgnoreCase( "offset_date_time" ) + || OffsetDateTime.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, OffsetDateTimeJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "offsettime" ) + || jaxbType.getValue().equalsIgnoreCase( "offset_time" ) + || OffsetTime.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, OffsetTimeJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "zoneid" ) + || jaxbType.getValue().equalsIgnoreCase( "zone_id" ) + || ZoneId.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, ZoneIdJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "zoneoffset" ) + || jaxbType.getValue().equalsIgnoreCase( "zone_offset" ) + || ZoneOffset.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, ZoneOffsetJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "timestamp" ) + || jaxbType.getValue().equalsIgnoreCase( "time_stamp" ) + || java.util.Date.class.getName().equals( jaxbType.getValue() ) + || Timestamp.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, DateJavaType.class, xmlDocumentContext ); + applyTemporalPrecision( memberDetails, TemporalType.TIMESTAMP, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "date" ) + || java.sql.Date.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, DateJavaType.class, xmlDocumentContext ); + applyTemporalPrecision( memberDetails, TemporalType.DATE, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "time" ) + || java.sql.Time.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, DateJavaType.class, xmlDocumentContext ); + applyTemporalPrecision( memberDetails, TemporalType.TIME, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "calendar" ) + || jaxbType.getValue().equalsIgnoreCase( "gregoriancalendar" ) + || jaxbType.getValue().equalsIgnoreCase( "gregorian_calendar" ) + || Calendar.class.getName().equals( jaxbType.getValue() ) + || GregorianCalendar.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, CalendarJavaType.class, xmlDocumentContext ); + return true; + } + + if ( jaxbType.getValue().equalsIgnoreCase( "timezone" ) + || jaxbType.getValue().equalsIgnoreCase( "time_zone" ) + || TimeZone.class.getName().equals( jaxbType.getValue() ) ) { + applyJavaTypeAnnotation( memberDetails, TimeZoneJavaType.class, xmlDocumentContext ); + return true; + } + + return false; + } + + private static void applyJavaTypeAnnotation( + MutableMemberDetails memberDetails, + Class> descriptor, + XmlDocumentContext xmlDocumentContext) { + final JavaTypeAnnotation javaTypeAnnotation = (JavaTypeAnnotation) memberDetails.applyAnnotationUsage( + HibernateAnnotations.JAVA_TYPE, + xmlDocumentContext.getModelBuildingContext() + ); + javaTypeAnnotation.value( descriptor ); + } + + private static void applyTemporalPrecision(MutableMemberDetails memberDetails, TemporalType temporalType, XmlDocumentContext xmlDocumentContext) { + final Temporal directUsage = memberDetails.getDirectAnnotationUsage( Temporal.class ); + if ( directUsage != null ) { + // make sure they match + if ( directUsage.value() != temporalType ) { + throw new org.hibernate.MappingException( String.format( + Locale.ROOT, + "Mismatch in expected TemporalType on %s; found %s and %s", + memberDetails, + directUsage.value(), + temporalType + ) ); + } + return; + } + + final TemporalJpaAnnotation temporalAnnotation = (TemporalJpaAnnotation) memberDetails.applyAnnotationUsage( + JpaAnnotations.TEMPORAL, + xmlDocumentContext.getModelBuildingContext() + ); + temporalAnnotation.value( temporalType ); + } + private static final Parameter[] NO_PARAMETERS = new Parameter[0]; public static Parameter[] collectParameters( diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/BasicIdAttributeProcessing.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/BasicIdAttributeProcessing.java index b95734f50e..c702124dc6 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/BasicIdAttributeProcessing.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/BasicIdAttributeProcessing.java @@ -12,6 +12,7 @@ import org.hibernate.boot.models.annotations.internal.BasicJpaAnnotation; import org.hibernate.boot.models.xml.internal.XmlAnnotationHelper; import org.hibernate.boot.models.xml.internal.XmlProcessingHelper; import org.hibernate.boot.models.xml.spi.XmlDocumentContext; +import org.hibernate.internal.util.StringHelper; import org.hibernate.models.spi.MutableClassDetails; import org.hibernate.models.spi.MutableMemberDetails; @@ -33,8 +34,12 @@ public class BasicIdAttributeProcessing { AccessType classAccessType, XmlDocumentContext xmlDocumentContext) { final AccessType accessType = coalesce( jaxbId.getAccess(), classAccessType ); + final String idAttributeName = StringHelper.isNotEmpty( jaxbId.getName() ) + ? jaxbId.getName() + : "id"; + final MutableMemberDetails memberDetails = XmlProcessingHelper.getAttributeMember( - jaxbId.getName(), + idAttributeName, accessType, declarer ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/type/EntityWithElementCollections.java b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/type/EntityWithElementCollections.java new file mode 100644 index 0000000000..5c058fb6e3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/type/EntityWithElementCollections.java @@ -0,0 +1,25 @@ +/* + * 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.boot.models.hbm.type; + +import java.net.URL; +import java.util.List; +import java.util.UUID; + +/** + * @author Steve Ebersole + */ +public class EntityWithElementCollections { + private Integer id; + private String name; + + private List listOfStrings; + private List listOfIntegers; + private List listOfDoubles; + private List listOfUrls; + private List listOfUuids; +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/type/SpecialTypeTransformationTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/type/SpecialTypeTransformationTests.java new file mode 100644 index 0000000000..852f21e450 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/type/SpecialTypeTransformationTests.java @@ -0,0 +1,127 @@ +/* + * 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.boot.models.hbm.type; + +import org.hibernate.cfg.MappingSettings; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.mapping.Collection; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.RootClass; +import org.hibernate.type.BasicType; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.java.BasicJavaType; +import org.hibernate.type.descriptor.java.BooleanJavaType; +import org.hibernate.type.descriptor.java.ClobJavaType; +import org.hibernate.type.descriptor.java.DoubleJavaType; +import org.hibernate.type.descriptor.java.InstantJavaType; +import org.hibernate.type.descriptor.java.IntegerJavaType; +import org.hibernate.type.descriptor.java.JdbcDateJavaType; +import org.hibernate.type.descriptor.java.JdbcTimeJavaType; +import org.hibernate.type.descriptor.java.JdbcTimestampJavaType; +import org.hibernate.type.descriptor.java.ShortJavaType; +import org.hibernate.type.descriptor.java.StringJavaType; +import org.hibernate.type.descriptor.java.UUIDJavaType; +import org.hibernate.type.descriptor.java.UrlJavaType; + +import org.hibernate.testing.orm.domain.gambit.EntityOfBasics; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.DomainModelScope; +import org.hibernate.testing.orm.junit.RequiresDialect; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @implNote Limited to H2 simply because some Dialects will map some of these to + * minor differences in SQL/JDBC types, and largely such differences are unimportant here + * + * @author Steve Ebersole + */ +@SuppressWarnings("JUnitMalformedDeclaration") +@RequiresDialect( H2Dialect.class ) +public class SpecialTypeTransformationTests { + @Test + @ServiceRegistry(settings = { + @Setting(name = MappingSettings.JAVA_TIME_USE_DIRECT_JDBC, value = "true"), + // SqlTypes.INSTANT + @Setting(name = MappingSettings.PREFERRED_INSTANT_JDBC_TYPE, value = "3008") + } ) + @DomainModel(xmlMappings = "mappings/models/hbm/type/basics.xml") + void testBasicsHbmXml(DomainModelScope scope) { + scope.withHierarchy( EntityOfBasics.class, this::verify ); + } + + @Test + @ServiceRegistry(settings = { + @Setting(name = MappingSettings.JAVA_TIME_USE_DIRECT_JDBC, value = "true"), + // SqlTypes.INSTANT + @Setting(name = MappingSettings.PREFERRED_INSTANT_JDBC_TYPE, value = "3008"), + @Setting(name = MappingSettings.TRANSFORM_HBM_XML, value = "true") + } ) + @DomainModel(xmlMappings = "mappings/models/hbm/type/basics.xml") + void testBasicsTransformed(DomainModelScope scope) { + scope.withHierarchy( EntityOfBasics.class, this::verify ); + } + + @Test + @ServiceRegistry + @DomainModel(xmlMappings = "mappings/models/hbm/type/element-collections.xml") + void testElementCollectionsHbmXml(DomainModelScope scope) { + scope.withHierarchy( EntityWithElementCollections.class, this::verifyElementCollections ); + } + + @Test + @ServiceRegistry(settings = @Setting(name = MappingSettings.TRANSFORM_HBM_XML, value = "true")) + @DomainModel(xmlMappings = "mappings/models/hbm/type/element-collections.xml") + void testElementCollectionsTransformed(DomainModelScope scope) { + scope.withHierarchy( EntityWithElementCollections.class, this::verifyElementCollections ); + } + + private void verify(RootClass rootClass) { + verify( (BasicType) rootClass.getIdentifier().getType(), IntegerJavaType.class, SqlTypes.INTEGER ); + verify( rootClass, "theBoolean", BooleanJavaType.class, SqlTypes.BOOLEAN ); + verify( rootClass, "theString", StringJavaType.class, SqlTypes.VARCHAR ); + verify( rootClass, "theInt", IntegerJavaType.class, SqlTypes.INTEGER ); + verify( rootClass, "theInteger", IntegerJavaType.class, SqlTypes.INTEGER ); + verify( rootClass, "theShort", ShortJavaType.class, SqlTypes.SMALLINT ); + verify( rootClass, "theDouble", DoubleJavaType.class, SqlTypes.DOUBLE ); + verify( rootClass, "theUrl", UrlJavaType.class, SqlTypes.VARCHAR ); + verify( rootClass, "theClob", ClobJavaType.class, SqlTypes.CLOB ); + verify( rootClass, "theInstant", InstantJavaType.class, SqlTypes.INSTANT ); + verify( rootClass, "theDate", JdbcDateJavaType.class, SqlTypes.DATE ); + verify( rootClass, "theTime", JdbcTimeJavaType.class, SqlTypes.TIME ); + verify( rootClass, "theTimestamp", JdbcTimestampJavaType.class, SqlTypes.TIMESTAMP ); + } + + private void verify(RootClass rootClass, String attributeName, Class> expectedJavaType, int expectedJdbcTypeCode) { + final Property attribute = rootClass.getProperty( attributeName ); + assertThat( attribute.getType() ).isInstanceOf( BasicType.class ); + verify( (BasicType) attribute.getType(), expectedJavaType, expectedJdbcTypeCode ); + } + + private void verifyElementCollections(RootClass rootClass) { + verifyElementCollection( rootClass, "listOfStrings", StringJavaType.class, SqlTypes.VARCHAR ); + verifyElementCollection( rootClass, "listOfIntegers", IntegerJavaType.class, SqlTypes.INTEGER ); + verifyElementCollection( rootClass, "listOfDoubles", DoubleJavaType.class, SqlTypes.DOUBLE ); + verifyElementCollection( rootClass, "listOfUrls", UrlJavaType.class, SqlTypes.VARCHAR ); + verifyElementCollection( rootClass, "listOfUuids", UUIDJavaType.class, SqlTypes.OTHER ); + } + + private void verifyElementCollection(RootClass rootClass, String name, Class> expectedJavaType, int expectedJdbcTypeCode) { + final Property property = rootClass.getProperty( name ); + final Collection propertyValue = (Collection) property.getValue(); + verify( (BasicType) propertyValue.getElement().getType(), expectedJavaType, expectedJdbcTypeCode ); + } + + private void verify(BasicType type, Class> expectedJavaType, int expectedJdbcTypeCode) { + assertThat( type.getJavaTypeDescriptor().getClass() ).isEqualTo( expectedJavaType ); + assertThat( type.getJdbcType().getJdbcTypeCode() ).isEqualTo( expectedJdbcTypeCode ); + } + +} diff --git a/hibernate-core/src/test/resources/hibernate.properties b/hibernate-core/src/test/resources/hibernate.properties index 1ec4455216..4ab2ce2226 100644 --- a/hibernate-core/src/test/resources/hibernate.properties +++ b/hibernate-core/src/test/resources/hibernate.properties @@ -16,8 +16,6 @@ hibernate.connection.autocommit false hibernate.connection.initial_pool_size 0 hibernate.connection.pool_size 5 -hibernate.transform_hbm_xml.enabled true - hibernate.show_sql true hibernate.format_sql true hibernate.highlight_sql true diff --git a/hibernate-core/src/test/resources/mappings/models/hbm/type/basics.xml b/hibernate-core/src/test/resources/mappings/models/hbm/type/basics.xml new file mode 100644 index 0000000000..f338a50383 --- /dev/null +++ b/hibernate-core/src/test/resources/mappings/models/hbm/type/basics.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hibernate-core/src/test/resources/mappings/models/hbm/type/element-collections.xml b/hibernate-core/src/test/resources/mappings/models/hbm/type/element-collections.xml new file mode 100644 index 0000000000..8c1697c3e6 --- /dev/null +++ b/hibernate-core/src/test/resources/mappings/models/hbm/type/element-collections.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file