From e19727e454debeb33def2d9829c118306dddf2e7 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 21 Dec 2022 04:31:11 +0100 Subject: [PATCH] HHH-15872 Fix some issues with UDT column ordering --- .../dialect/OracleStructJdbcType.java | 86 +++++---- .../dialect/PostgreSQLStructJdbcType.java | 167 ++++++++++++++++-- .../org/hibernate/dialect/StructHelper.java | 63 ++++++- .../hibernate/mapping/UserDefinedType.java | 2 +- .../metamodel/internal/EmbeddableHelper.java | 34 ++++ 5 files changed, 299 insertions(+), 53 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/metamodel/internal/EmbeddableHelper.java diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleStructJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleStructJdbcType.java index a77cb3ac16..a6bd8b4128 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleStructJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleStructJdbcType.java @@ -57,6 +57,7 @@ public class OracleStructJdbcType implements AggregateJdbcType { private final String oracleTypeName; private final int[] orderMapping; + private final int[] inverseOrderMapping; private final EmbeddableMappingType embeddableMappingType; private final ValueExtractor objectArrayExtractor; @@ -69,6 +70,16 @@ public class OracleStructJdbcType implements AggregateJdbcType { this.embeddableMappingType = embeddableMappingType; this.oracleTypeName = typeName == null ? null : typeName.toUpperCase( Locale.ROOT ); this.orderMapping = orderMapping; + if ( orderMapping == null ) { + this.inverseOrderMapping = null; + } + else { + final int[] inverseOrderMapping = new int[orderMapping.length]; + for ( int i = 0; i < orderMapping.length; i++ ) { + inverseOrderMapping[orderMapping[i]] = i; + } + this.inverseOrderMapping = inverseOrderMapping; + } // We cache the extractor for Object[] here // since that is used in AggregateEmbeddableFetchImpl and AggregateEmbeddableResultImpl this.objectArrayExtractor = createBasicExtractor( new UnknownBasicJavaType<>( Object[].class ) ); @@ -128,6 +139,7 @@ public class OracleStructJdbcType implements AggregateJdbcType { public Object createJdbcValue(Object domainValue, WrapperOptions options) throws SQLException { final Object[] jdbcValues = StructHelper.getJdbcValues( embeddableMappingType, + orderMapping, embeddableMappingType.getValues( domainValue ), options ); @@ -141,7 +153,7 @@ public class OracleStructJdbcType implements AggregateJdbcType { @Override public Object[] extractJdbcValues(Object rawJdbcValue, WrapperOptions options) throws SQLException { final Object[] attributes = ( (Struct) rawJdbcValue ).getAttributes(); - wrapRawJdbcValues( embeddableMappingType, orderMapping, attributes, 0, options ); + wrapRawJdbcValues( embeddableMappingType, orderMapping, inverseOrderMapping, attributes, 0, options ); return attributes; } @@ -197,7 +209,7 @@ public class OracleStructJdbcType implements AggregateJdbcType { final Object[] values = struct.getAttributes(); final boolean jdbcRepresentation = getJavaType().getJavaTypeClass() == Object[].class; if ( jdbcRepresentation ) { - wrapRawJdbcValues( embeddableMappingType, orderMapping, values, 0, options ); + wrapRawJdbcValues( embeddableMappingType, orderMapping, inverseOrderMapping, values, 0, options ); //noinspection unchecked return (X) values; } @@ -217,14 +229,14 @@ public class OracleStructJdbcType implements AggregateJdbcType { }; } - public static Object[] getAttributeValues( + private static Object[] getAttributeValues( EmbeddableMappingType embeddableMappingType, int[] orderMapping, Object[] rawJdbcValues, WrapperOptions options) throws SQLException { final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings(); final Object[] attributeValues; - if ( numberOfAttributeMappings != rawJdbcValues.length ) { + if ( numberOfAttributeMappings != rawJdbcValues.length || orderMapping != null ) { attributeValues = new Object[numberOfAttributeMappings]; } else { @@ -232,12 +244,18 @@ public class OracleStructJdbcType implements AggregateJdbcType { } int jdbcIndex = 0; for ( int i = 0; i < numberOfAttributeMappings; i++ ) { - final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( i ); + final int attributeIndex; + if ( orderMapping == null ) { + attributeIndex = i; + } + else { + attributeIndex = orderMapping[i]; + } + final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( attributeIndex ); jdbcIndex += injectAttributeValue( attributeMapping, - orderMapping, attributeValues, - i, + attributeIndex, rawJdbcValues, jdbcIndex, options @@ -248,7 +266,6 @@ public class OracleStructJdbcType implements AggregateJdbcType { private static int injectAttributeValue( AttributeMapping attributeMapping, - int[] orderMapping, Object[] attributeValues, int attributeIndex, Object[] rawJdbcValues, @@ -256,13 +273,7 @@ public class OracleStructJdbcType implements AggregateJdbcType { WrapperOptions options) throws SQLException { final MappingType mappedType = attributeMapping.getMappedType(); final int jdbcValueCount; - final Object rawJdbcValue; - if ( orderMapping == null ) { - rawJdbcValue = rawJdbcValues[jdbcIndex]; - } - else { - rawJdbcValue = rawJdbcValues[orderMapping[jdbcIndex]]; - } + final Object rawJdbcValue = rawJdbcValues[jdbcIndex]; if ( mappedType instanceof EmbeddableMappingType ) { final EmbeddableMappingType embeddableMappingType = (EmbeddableMappingType) mappedType; if ( embeddableMappingType.getAggregateMapping() != null ) { @@ -300,7 +311,7 @@ public class OracleStructJdbcType implements AggregateJdbcType { jdbcValueCount = embeddableMappingType.getJdbcValueCount(); final Object[] jdbcValues = new Object[jdbcValueCount]; System.arraycopy( rawJdbcValues, jdbcIndex, jdbcValues, 0, jdbcValues.length ); - final Object[] subValues = getAttributeValues( embeddableMappingType, orderMapping, jdbcValues, options ); + final Object[] subValues = getAttributeValues( embeddableMappingType, null, jdbcValues, options ); attributeValues[attributeIndex] = embeddableMappingType.getRepresentationStrategy() .getInstantiator() .instantiate( @@ -340,12 +351,26 @@ public class OracleStructJdbcType implements AggregateJdbcType { private static int wrapRawJdbcValues( EmbeddableMappingType embeddableMappingType, int[] orderMapping, + int[] inverseOrderMapping, Object[] jdbcValues, int jdbcIndex, WrapperOptions options) throws SQLException { + final Object[] targetJdbcValues; + if ( orderMapping == null ) { + targetJdbcValues = jdbcValues; + } + else { + targetJdbcValues = jdbcValues.clone(); + } final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings(); for ( int i = 0; i < numberOfAttributeMappings; i++ ) { - final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( i ); + final AttributeMapping attributeMapping; + if ( orderMapping == null ) { + attributeMapping = embeddableMappingType.getAttributeMapping( i ); + } + else { + attributeMapping = embeddableMappingType.getAttributeMapping( orderMapping[i] ); + } final MappingType mappedType = attributeMapping.getMappedType(); if ( mappedType instanceof EmbeddableMappingType ) { @@ -354,46 +379,37 @@ public class OracleStructJdbcType implements AggregateJdbcType { final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) embeddableType.getAggregateMapping() .getJdbcMapping() .getJdbcType(); - final Object rawJdbcValue; - if ( orderMapping == null ) { - rawJdbcValue = jdbcValues[jdbcIndex]; - } - else { - rawJdbcValue = jdbcValues[orderMapping[jdbcIndex]]; - } - jdbcValues[jdbcIndex] = aggregateJdbcType.extractJdbcValues( rawJdbcValue, options ); + final Object rawJdbcValue = targetJdbcValues[jdbcIndex]; + targetJdbcValues[jdbcIndex] = aggregateJdbcType.extractJdbcValues( rawJdbcValue, options ); jdbcIndex++; } else { - jdbcIndex = wrapRawJdbcValues( embeddableType, orderMapping, jdbcValues, jdbcIndex, options ); + jdbcIndex = wrapRawJdbcValues( embeddableType, null, null, targetJdbcValues, jdbcIndex, options ); } } else { assert attributeMapping.getJdbcTypeCount() == 1; - final Object rawJdbcValue; - if ( orderMapping == null ) { - rawJdbcValue = jdbcValues[jdbcIndex]; - } - else { - rawJdbcValue = jdbcValues[orderMapping[jdbcIndex]]; - } + final Object rawJdbcValue = targetJdbcValues[jdbcIndex]; if ( rawJdbcValue != null ) { final JdbcMapping jdbcMapping = attributeMapping.getJdbcMappings().get( 0 ); switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) { case SqlTypes.TIMESTAMP_WITH_TIMEZONE: case SqlTypes.TIMESTAMP_UTC: // Only transform the raw jdbc value if it could be a TIMESTAMPTZ - jdbcValues[jdbcIndex] = jdbcMapping.getJdbcJavaType() + targetJdbcValues[jdbcIndex] = jdbcMapping.getJdbcJavaType() .wrap( transformRawJdbcValue( rawJdbcValue, options ), options ); break; default: - jdbcValues[jdbcIndex] = jdbcMapping.getJdbcJavaType().wrap( rawJdbcValue, options ); + targetJdbcValues[jdbcIndex] = jdbcMapping.getJdbcJavaType().wrap( rawJdbcValue, options ); break; } } jdbcIndex++; } } + if ( orderMapping != null ) { + StructHelper.orderJdbcValues( embeddableMappingType, inverseOrderMapping, targetJdbcValues, jdbcValues ); + } return jdbcIndex; } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLStructJdbcType.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLStructJdbcType.java index bd6770999b..2af8f66a81 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLStructJdbcType.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLStructJdbcType.java @@ -26,6 +26,7 @@ import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; @@ -84,6 +85,7 @@ public class PostgreSQLStructJdbcType extends PostgreSQLPGObjectJdbcType impleme } private final int[] orderMapping; + private final int[] inverseOrderMapping; private final EmbeddableMappingType embeddableMappingType; private final ValueExtractor objectArrayExtractor; @@ -96,6 +98,17 @@ public class PostgreSQLStructJdbcType extends PostgreSQLPGObjectJdbcType impleme super( typeName, SqlTypes.STRUCT ); this.embeddableMappingType = embeddableMappingType; this.orderMapping = orderMapping; + if ( orderMapping == null ) { + this.inverseOrderMapping = null; + } + else { + final int[] inverseOrderMapping = new int[orderMapping.length]; + for ( int i = 0; i < orderMapping.length; i++ ) { + inverseOrderMapping[orderMapping[i]] = i; + } + this.inverseOrderMapping = inverseOrderMapping; + } + // We cache the extractor for Obje // We cache the extractor for Object[] here // since that is used in AggregateEmbeddableFetchImpl and AggregateEmbeddableResultImpl this.objectArrayExtractor = super.getExtractor( new UnknownBasicJavaType<>( Object[].class ) ); @@ -170,13 +183,16 @@ public class PostgreSQLStructJdbcType extends PostgreSQLPGObjectJdbcType impleme } assert end == string.length(); if ( returnEmbeddable ) { - final Object[] attributeValues = StructHelper.getAttributeValues( embeddableMappingType, array, options ); + final Object[] attributeValues = getAttributeValues( embeddableMappingType, array, options ); //noinspection unchecked return (X) embeddableMappingType.getRepresentationStrategy().getInstantiator().instantiate( () -> attributeValues, options.getSessionFactory() ); } + else if ( inverseOrderMapping != null ) { + StructHelper.orderJdbcValues( embeddableMappingType, inverseOrderMapping, array.clone(), array ); + } //noinspection unchecked return (X) array; } @@ -282,8 +298,7 @@ public class PostgreSQLStructJdbcType extends PostgreSQLPGObjectJdbcType impleme continue; } assert repeatsChar( string, i, 1 << quoteLevel, '"' ); - final JdbcMapping jdbcMapping = getJdbcValueSelectable( column ) - .getJdbcMapping(); + final JdbcMapping jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) { case SqlTypes.DATE: values[column] = fromRawObject( @@ -396,8 +411,7 @@ public class PostgreSQLStructJdbcType extends PostgreSQLPGObjectJdbcType impleme i += expectedQuotes - 1; if ( string.charAt( i + 1 ) == '(' ) { // This could be a nested struct - final JdbcMapping jdbcMapping = getJdbcValueSelectable( column ) - .getJdbcMapping(); + final JdbcMapping jdbcMapping = getJdbcValueSelectable( column ).getJdbcMapping(); if ( jdbcMapping.getJdbcType() instanceof PostgreSQLStructJdbcType ) { final PostgreSQLStructJdbcType structJdbcType; structJdbcType = (PostgreSQLStructJdbcType) jdbcMapping.getJdbcType(); @@ -411,7 +425,7 @@ public class PostgreSQLStructJdbcType extends PostgreSQLPGObjectJdbcType impleme options ); if ( returnEmbeddable ) { - final Object[] attributeValues = StructHelper.getAttributeValues( + final Object[] attributeValues = getAttributeValues( structJdbcType.embeddableMappingType, subValues, options @@ -422,6 +436,14 @@ public class PostgreSQLStructJdbcType extends PostgreSQLPGObjectJdbcType impleme values[column] = subValue; } else { + if ( structJdbcType.inverseOrderMapping != null ) { + StructHelper.orderJdbcValues( + structJdbcType.embeddableMappingType, + structJdbcType.inverseOrderMapping, + subValues.clone(), + subValues + ); + } values[column] = subValues; } column++; @@ -500,7 +522,36 @@ public class PostgreSQLStructJdbcType extends PostgreSQLPGObjectJdbcType impleme private SelectableMapping getJdbcValueSelectable(int jdbcValueSelectableIndex) { if ( orderMapping != null ) { - jdbcValueSelectableIndex = orderMapping[jdbcValueSelectableIndex]; + final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings(); + int count = 0; + for ( int i = 0; i < numberOfAttributeMappings; i++ ) { + final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( orderMapping[i] ); + final MappingType mappedType = attributeMapping.getMappedType(); + if ( mappedType instanceof EmbeddableMappingType ) { + final EmbeddableMappingType embeddableMappingType = (EmbeddableMappingType) mappedType; + final SelectableMapping aggregateMapping = embeddableMappingType.getAggregateMapping(); + if ( aggregateMapping == null ) { + final SelectableMapping subSelectable = embeddableMappingType.getJdbcValueSelectable( jdbcValueSelectableIndex - count ); + if ( subSelectable != null ) { + return subSelectable; + } + count += embeddableMappingType.getJdbcValueCount(); + } + else { + if ( count == jdbcValueSelectableIndex ) { + return aggregateMapping; + } + count++; + } + } + else { + if ( count == jdbcValueSelectableIndex ) { + return (SelectableMapping) attributeMapping; + } + count += attributeMapping.getJdbcTypeCount(); + } + } + return null; } return embeddableMappingType.getJdbcValueSelectable( jdbcValueSelectableIndex ); } @@ -602,6 +653,9 @@ public class PostgreSQLStructJdbcType extends PostgreSQLPGObjectJdbcType impleme assert embeddableMappingType != null; final Object[] array = new Object[embeddableMappingType.getJdbcValueCount()]; deserializeStruct( (String) rawJdbcValue, 0, 0, array, true, options ); + if ( inverseOrderMapping != null ) { + StructHelper.orderJdbcValues( embeddableMappingType, inverseOrderMapping, array.clone(), array ); + } return array; } @@ -629,15 +683,24 @@ public class PostgreSQLStructJdbcType extends PostgreSQLPGObjectJdbcType impleme char separator) { final int end = embeddableMappingType.getNumberOfAttributeMappings(); for ( int i = 0; i < end; i++ ) { - final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( i ); + final AttributeMapping attributeMapping; + final Object attributeValue; + if ( orderMapping == null ) { + attributeMapping = embeddableMappingType.getAttributeMapping( i ); + attributeValue = array == null ? null : array[i]; + } + else { + attributeMapping = embeddableMappingType.getAttributeMapping( orderMapping[i] ); + attributeValue = array == null ? null : array[orderMapping[i]]; + } if ( attributeMapping instanceof BasicValuedMapping ) { appender.append( separator ); separator = ','; - if ( array == null || array[i] == null ) { + if ( attributeValue == null ) { continue; } final JdbcMapping jdbcMapping = ( (BasicValuedMapping) attributeMapping ).getJdbcMapping(); - serializeBasicTo( appender, options, jdbcMapping, array[i] ); + serializeBasicTo( appender, options, jdbcMapping, attributeValue ); } else if ( attributeMapping instanceof EmbeddedAttributeMapping ) { final EmbeddableMappingType mappingType = (EmbeddableMappingType) attributeMapping.getMappedType(); @@ -647,7 +710,7 @@ public class PostgreSQLStructJdbcType extends PostgreSQLPGObjectJdbcType impleme appender, options, mappingType, - array == null || array[i] == null ? null : mappingType.getValues( array[i] ), + attributeValue == null ? null : mappingType.getValues( attributeValue ), separator ); separator = ','; @@ -655,13 +718,13 @@ public class PostgreSQLStructJdbcType extends PostgreSQLPGObjectJdbcType impleme else { appender.append( separator ); separator = ','; - if ( array == null || array[i] == null ) { + if ( attributeValue == null ) { continue; } appender.quoteStart(); ( (PostgreSQLStructJdbcType) aggregateMapping.getJdbcMapping().getJdbcType() ).serializeStructTo( appender, - array[i], + attributeValue, options ); appender.quoteEnd(); @@ -767,6 +830,84 @@ public class PostgreSQLStructJdbcType extends PostgreSQLPGObjectJdbcType impleme } } + private Object[] getAttributeValues( + EmbeddableMappingType embeddableMappingType, + Object[] rawJdbcValues, + WrapperOptions options) throws SQLException { + final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings(); + final Object[] attributeValues; + if ( numberOfAttributeMappings != rawJdbcValues.length || orderMapping != null ) { + attributeValues = new Object[numberOfAttributeMappings]; + } + else { + attributeValues = rawJdbcValues; + } + int jdbcIndex = 0; + for ( int i = 0; i < numberOfAttributeMappings; i++ ) { + final int attributeIndex; + if ( orderMapping == null ) { + attributeIndex = i; + } + else { + attributeIndex = orderMapping[i]; + } + final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( attributeIndex ); + jdbcIndex += injectAttributeValue( + attributeMapping, + attributeValues, + attributeIndex, + rawJdbcValues, + jdbcIndex, + options + ); + } + return attributeValues; + } + + private int injectAttributeValue( + AttributeMapping attributeMapping, + Object[] attributeValues, + int attributeIndex, + Object[] rawJdbcValues, + int jdbcIndex, + WrapperOptions options) throws SQLException { + final MappingType mappedType = attributeMapping.getMappedType(); + final int jdbcValueCount; + final Object rawJdbcValue = rawJdbcValues[jdbcIndex]; + if ( mappedType instanceof EmbeddableMappingType ) { + final EmbeddableMappingType embeddableMappingType = (EmbeddableMappingType) mappedType; + if ( embeddableMappingType.getAggregateMapping() != null ) { + jdbcValueCount = 1; + attributeValues[attributeIndex] = rawJdbcValue; + } + else { + jdbcValueCount = embeddableMappingType.getJdbcValueCount(); + final Object[] subJdbcValues = new Object[jdbcValueCount]; + System.arraycopy( rawJdbcValues, jdbcIndex, subJdbcValues, 0, subJdbcValues.length ); + final Object[] subValues = getAttributeValues( embeddableMappingType, subJdbcValues, options ); + attributeValues[attributeIndex] = embeddableMappingType.getRepresentationStrategy() + .getInstantiator() + .instantiate( + () -> subValues, + embeddableMappingType.findContainingEntityMapping() + .getEntityPersister() + .getFactory() + ); + } + } + else { + assert attributeMapping.getJdbcTypeCount() == 1; + jdbcValueCount = 1; + final JdbcMapping jdbcMapping = attributeMapping.getJdbcMappings().get( 0 ); + final Object jdbcValue = jdbcMapping.getJdbcJavaType().wrap( + rawJdbcValue, + options + ); + attributeValues[attributeIndex] = jdbcMapping.convertToDomainValue( jdbcValue ); + } + return jdbcValueCount; + } + private void appendTemporal(SqlAppender appender, JdbcMapping jdbcMapping, Object value, WrapperOptions options) { final TimeZone jdbcTimeZone = getJdbcTimeZone( options ); //noinspection unchecked diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/StructHelper.java b/hibernate-core/src/main/java/org/hibernate/dialect/StructHelper.java index 64c354b08b..696d16b68f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/StructHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/StructHelper.java @@ -90,11 +90,12 @@ public class StructHelper { public static Object[] getJdbcValues( EmbeddableMappingType embeddableMappingType, + int[] orderMapping, Object[] attributeValues, WrapperOptions options) throws SQLException { final int jdbcValueCount = embeddableMappingType.getJdbcValueCount(); final Object[] jdbcValues; - if ( jdbcValueCount != attributeValues.length ) { + if ( jdbcValueCount != attributeValues.length || orderMapping != null ) { jdbcValues = new Object[jdbcValueCount]; } else { @@ -102,11 +103,17 @@ public class StructHelper { } int jdbcIndex = 0; for ( int i = 0; i < attributeValues.length; i++ ) { - final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( i ); + final int attributeIndex; + if ( orderMapping == null ) { + attributeIndex = i; + } + else { + attributeIndex = orderMapping[i]; + } jdbcIndex += injectJdbcValue( - attributeMapping, + embeddableMappingType.getAttributeMapping( attributeIndex ), attributeValues, - i, + attributeIndex, jdbcValues, jdbcIndex, options @@ -185,4 +192,52 @@ public class StructHelper { } return jdbcValueCount; } + + /** + * The sourceJdbcValues array is ordered according to the expected physical order, + * as given through the argument order of @Instantiator. + * The targetJdbcValues array should be ordered according to the Hibernate internal ordering, + * which is based on property name. + * This method copies from sourceJdbcValues to targetJdbcValues according to the ordering. + */ + public static void orderJdbcValues( + EmbeddableMappingType embeddableMappingType, + int[] inverseMapping, + Object[] sourceJdbcValues, + Object[] targetJdbcValues) { + final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings(); + int targetJdbcOffset = 0; + for ( int i = 0; i < numberOfAttributeMappings; i++ ) { + final AttributeMapping attributeMapping = embeddableMappingType.getAttributeMapping( i ); + final MappingType mappedType = attributeMapping.getMappedType(); + final int jdbcValueCount = getJdbcValueCount( mappedType ); + + final int attributeIndex = inverseMapping[i]; + int sourceJdbcIndex = 0; + for ( int j = 0; j < attributeIndex; j++ ) { + sourceJdbcIndex += getJdbcValueCount( embeddableMappingType.getAttributeMapping( j ).getMappedType() ); + } + + for ( int j = 0; j < jdbcValueCount; j++ ) { + targetJdbcValues[targetJdbcOffset++] = sourceJdbcValues[sourceJdbcIndex + j]; + } + } + } + + public static int getJdbcValueCount(MappingType mappedType) { + final int jdbcValueCount; + if ( mappedType instanceof EmbeddableMappingType ) { + final EmbeddableMappingType subMappingType = (EmbeddableMappingType) mappedType; + if ( subMappingType.getAggregateMapping() != null ) { + jdbcValueCount = 1; + } + else { + jdbcValueCount = subMappingType.getJdbcValueCount(); + } + } + else { + jdbcValueCount = 1; + } + return jdbcValueCount; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/UserDefinedType.java b/hibernate-core/src/main/java/org/hibernate/mapping/UserDefinedType.java index 1acaf47418..01dda0ea61 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/UserDefinedType.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/UserDefinedType.java @@ -266,7 +266,7 @@ public class UserDefinedType implements Serializable, ContributableDatabaseObjec orderMapping = new int[columns.size()]; int i = 0; for ( Column column : this.columns.values() ) { - orderMapping[i++] = columns.indexOf( column ); + orderMapping[columns.indexOf( column )] = i++; } this.columns.clear(); for ( Column column : columns ) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EmbeddableHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EmbeddableHelper.java new file mode 100644 index 0000000000..f5d97438f3 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EmbeddableHelper.java @@ -0,0 +1,34 @@ +/* + * 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.metamodel.internal; + +import java.util.Arrays; + +public class EmbeddableHelper { + public static int[] determinePropertyMappingIndex(String[] propertyNames, String[] componentNames) { + final int[] index = new int[propertyNames.length]; + int i = 0; + for ( String componentName : componentNames ) { + final int mappingIndex = Arrays.binarySearch( propertyNames, componentName ); + if ( mappingIndex != -1 ) { + index[i++] = mappingIndex; + } + } + return index; + } + + public static boolean resolveIndex(String[] sortedComponentNames, String[] componentNames, int[] index) { + boolean hasGaps = false; + for ( int i = 0; i < componentNames.length; i++ ) { + final int newIndex = Arrays.binarySearch( sortedComponentNames, componentNames[i] ); + index[i] = newIndex; + hasGaps = hasGaps || newIndex < 0; + } + + return hasGaps; + } +}