HHH-15831 Support non-basic values in aggregate components

This commit is contained in:
Christian Beikov 2024-05-15 18:42:30 +02:00
parent 6247dea9a6
commit ed2fdce0a6
88 changed files with 2651 additions and 594 deletions

View File

@ -407,6 +407,8 @@ public class CockroachLegacyDialect extends Dialect {
.getDescriptor( Object.class ) .getDescriptor( Object.class )
) )
); );
jdbcTypeRegistry.addTypeConstructor( PostgreSQLArrayJdbcTypeConstructor.INSTANCE );
} }
@Override @Override

View File

@ -1441,6 +1441,8 @@ public class PostgreSQLLegacyDialect extends Dialect {
.getDescriptor( Object.class ) .getDescriptor( Object.class )
) )
); );
jdbcTypeRegistry.addTypeConstructor( PostgreSQLArrayJdbcTypeConstructor.INSTANCE );
} }
@Override @Override

View File

@ -47,6 +47,16 @@ public @interface Struct {
*/ */
String name(); String name();
/** (Optional) The catalog of the UDT.
* <p> Defaults to the default catalog.
*/
String catalog() default "";
/** (Optional) The schema of the UDT.
* <p> Defaults to the default schema for user.
*/
String schema() default "";
/** /**
* The ordered set of attributes of the UDT, as they appear physically in the DDL. * The ordered set of attributes of the UDT, as they appear physically in the DDL.
* It is important to specify the attributes in the same order for JDBC interactions to work correctly. * It is important to specify the attributes in the same order for JDBC interactions to work correctly.

View File

@ -1778,7 +1778,6 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
processSecondPasses( idGeneratorResolverSecondPassList ); processSecondPasses( idGeneratorResolverSecondPassList );
processSecondPasses( implicitColumnNamingSecondPassList ); processSecondPasses( implicitColumnNamingSecondPassList );
processSecondPasses( setBasicValueTypeSecondPassList ); processSecondPasses( setBasicValueTypeSecondPassList );
processSecondPasses( aggregateComponentSecondPassList );
processSecondPasses( toOneJoinTableSecondPassList ); processSecondPasses( toOneJoinTableSecondPassList );
composites.forEach( Component::sortProperties ); composites.forEach( Component::sortProperties );
@ -1794,6 +1793,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
processPropertyReferences(); processPropertyReferences();
processSecondPasses( aggregateComponentSecondPassList );
secondPassCompileForeignKeys( buildingContext ); secondPassCompileForeignKeys( buildingContext );
processNaturalIdUniqueKeyBinders(); processNaturalIdUniqueKeyBinders();

View File

@ -8,20 +8,20 @@ package org.hibernate.boot.model.internal;
import java.util.List; import java.util.List;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.Struct; import org.hibernate.annotations.Struct;
import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty; import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.relational.QualifiedName;
import org.hibernate.boot.model.relational.QualifiedNameImpl;
import org.hibernate.boot.model.relational.QualifiedNameParser;
import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData; import org.hibernate.boot.spi.PropertyData;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.AggregateColumn; import org.hibernate.mapping.AggregateColumn;
import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;
import org.hibernate.type.SqlTypes; import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.java.spi.EmbeddableAggregateJavaType; import org.hibernate.type.descriptor.java.spi.EmbeddableAggregateJavaType;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
@ -42,19 +42,18 @@ public final class AggregateComponentBinder {
AnnotatedColumns columns, AnnotatedColumns columns,
MetadataBuildingContext context) { MetadataBuildingContext context) {
if ( isAggregate( inferredData.getProperty(), componentXClass ) ) { if ( isAggregate( inferredData.getProperty(), componentXClass ) ) {
validateComponent( component, BinderHelper.getPath( propertyHolder, inferredData ) );
final InFlightMetadataCollector metadataCollector = context.getMetadataCollector(); final InFlightMetadataCollector metadataCollector = context.getMetadataCollector();
final TypeConfiguration typeConfiguration = metadataCollector.getTypeConfiguration(); final TypeConfiguration typeConfiguration = metadataCollector.getTypeConfiguration();
// Determine a struct name if this is a struct through some means // Determine a struct name if this is a struct through some means
final String structName = determineStructName( columns, inferredData, componentXClass ); final QualifiedName structQualifiedName = determineStructName( columns, inferredData, componentXClass, context );
final String structName = structQualifiedName == null ? null : structQualifiedName.render();
// We must register a special JavaType for the embeddable which can provide a recommended JdbcType // We must register a special JavaType for the embeddable which can provide a recommended JdbcType
typeConfiguration.getJavaTypeRegistry().resolveDescriptor( typeConfiguration.getJavaTypeRegistry().resolveDescriptor(
component.getComponentClass(), component.getComponentClass(),
() -> new EmbeddableAggregateJavaType<>( component.getComponentClass(), structName ) () -> new EmbeddableAggregateJavaType<>( component.getComponentClass(), structName )
); );
component.setStructName( structName ); component.setStructName( structQualifiedName );
component.setStructColumnNames( determineStructAttributeNames( inferredData, componentXClass ) ); component.setStructColumnNames( determineStructAttributeNames( inferredData, componentXClass ) );
// Determine the aggregate column // Determine the aggregate column
@ -97,6 +96,7 @@ public final class AggregateComponentBinder {
propertyHolder, propertyHolder,
component, component,
componentXClass, componentXClass,
inferredData.getPropertyName(),
context context
) )
); );
@ -111,60 +111,59 @@ public final class AggregateComponentBinder {
case SqlTypes.TABLE: case SqlTypes.TABLE:
return SqlTypes.STRUCT_TABLE; return SqlTypes.STRUCT_TABLE;
default: default:
throw new IllegalArgumentException( "Unsupported array type code: " + arrayTypeCode ); throw new UnsupportedOperationException( "Dialect does not support structured array types: " + context.getMetadataCollector()
.getDatabase()
.getDialect()
.getClass()
.getName() );
} }
} }
private static void validateComponent(Component component, String basePath) { private static QualifiedName determineStructName(
for ( Property property : component.getProperties() ) {
final Value value = property.getValue();
if ( !( value instanceof BasicValue ) && !( value instanceof Component ) ) {
// todo: see HHH-15831
throw new AnnotationException(
"Property '" + StringHelper.qualify( basePath, property.getName() )
+ "' uses not yet supported mapping type '"
+ value.getClass().getName()
+ "' in component class '"
+ component.getComponentClassName()
+ "'. Aggregate components currently may only contain basic values and components of basic values."
);
}
if ( value instanceof Component ) {
final Component c = (Component) value;
if ( c.getAggregateColumn() == null ) {
validateComponent( c, StringHelper.qualify( basePath, property.getName() ) );
}
}
}
}
private static String determineStructName(
AnnotatedColumns columns, AnnotatedColumns columns,
PropertyData inferredData, PropertyData inferredData,
XClass returnedClassOrElement) { XClass returnedClassOrElement,
MetadataBuildingContext context) {
final XProperty property = inferredData.getProperty(); final XProperty property = inferredData.getProperty();
if ( property != null ) { if ( property != null ) {
final Struct struct = property.getAnnotation( Struct.class ); final Struct struct = property.getAnnotation( Struct.class );
if ( struct != null ) { if ( struct != null ) {
return struct.name(); return toQualifiedName( struct, context );
} }
final JdbcTypeCode jdbcTypeCode = property.getAnnotation( JdbcTypeCode.class ); final JdbcTypeCode jdbcTypeCode = property.getAnnotation( JdbcTypeCode.class );
if ( jdbcTypeCode != null if ( jdbcTypeCode != null
&& ( jdbcTypeCode.value() == SqlTypes.STRUCT || jdbcTypeCode.value() == SqlTypes.STRUCT_ARRAY || jdbcTypeCode.value() == SqlTypes.STRUCT_TABLE ) && ( jdbcTypeCode.value() == SqlTypes.STRUCT || jdbcTypeCode.value() == SqlTypes.STRUCT_ARRAY || jdbcTypeCode.value() == SqlTypes.STRUCT_TABLE )
&& columns != null ) { && columns != null ) {
final List<AnnotatedColumn> columnList = columns.getColumns(); final List<AnnotatedColumn> columnList = columns.getColumns();
if ( columnList.size() == 1 && columnList.get( 0 ).getSqlType() != null ) { final String sqlType;
return columnList.get( 0 ).getSqlType(); if ( columnList.size() == 1 && ( sqlType = columnList.get( 0 ).getSqlType() ) != null ) {
if ( sqlType.contains( "." ) ) {
return QualifiedNameParser.INSTANCE.parse( sqlType );
}
return new QualifiedNameParser.NameParts(
null,
null,
context.getMetadataCollector().getDatabase().toIdentifier( sqlType )
);
} }
} }
} }
final Struct struct = returnedClassOrElement.getAnnotation( Struct.class ); final Struct struct = returnedClassOrElement.getAnnotation( Struct.class );
if ( struct != null ) { if ( struct != null ) {
return struct.name(); return toQualifiedName( struct, context );
} }
return null; return null;
} }
private static QualifiedName toQualifiedName(Struct struct, MetadataBuildingContext context) {
final Database database = context.getMetadataCollector().getDatabase();
return new QualifiedNameImpl(
database.toIdentifier( struct.catalog() ),
database.toIdentifier( struct.schema() ),
database.toIdentifier( struct.name() )
);
}
private static String[] determineStructAttributeNames(PropertyData inferredData, XClass returnedClassOrElement) { private static String[] determineStructAttributeNames(PropertyData inferredData, XClass returnedClassOrElement) {
final XProperty property = inferredData.getProperty(); final XProperty property = inferredData.getProperty();
if ( property != null ) { if ( property != null ) {

View File

@ -11,25 +11,27 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeSet; import java.util.TreeSet;
import org.hibernate.AnnotationException;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.annotations.Comment; import org.hibernate.annotations.Comment;
import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.relational.Namespace; import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.boot.model.relational.QualifiedName;
import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.SecondPass; import org.hibernate.boot.spi.SecondPass;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.aggregate.AggregateSupport; import org.hibernate.dialect.aggregate.AggregateSupport;
import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.AggregateColumn; import org.hibernate.mapping.AggregateColumn;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column; import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable; import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.UserDefinedObjectType; import org.hibernate.mapping.UserDefinedObjectType;
import org.hibernate.mapping.Value; import org.hibernate.mapping.Value;
import org.hibernate.metamodel.internal.EmbeddableHelper; import org.hibernate.metamodel.internal.EmbeddableHelper;
@ -37,6 +39,8 @@ import org.hibernate.sql.Template;
import org.hibernate.type.SqlTypes; import org.hibernate.type.SqlTypes;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.internal.util.StringHelper.qualify;
/** /**
* @author Christian Beikov * @author Christian Beikov
*/ */
@ -45,20 +49,26 @@ public class AggregateComponentSecondPass implements SecondPass {
private final PropertyHolder propertyHolder; private final PropertyHolder propertyHolder;
private final Component component; private final Component component;
private final XClass componentXClass; private final XClass componentXClass;
private final String propertyName;
private final MetadataBuildingContext context; private final MetadataBuildingContext context;
public AggregateComponentSecondPass( public AggregateComponentSecondPass(
PropertyHolder propertyHolder, PropertyHolder propertyHolder,
Component component, Component component,
XClass componentXClass, XClass componentXClass,
String propertyName,
MetadataBuildingContext context) { MetadataBuildingContext context) {
this.propertyHolder = propertyHolder; this.propertyHolder = propertyHolder;
this.component = component; this.component = component;
this.componentXClass = componentXClass; this.componentXClass = componentXClass;
this.propertyName = propertyName;
this.context = context; this.context = context;
} }
@Override
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException { public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
validateComponent( component, qualify( propertyHolder.getPath(), propertyName ), isAggregateArray() );
final InFlightMetadataCollector metadataCollector = context.getMetadataCollector(); final InFlightMetadataCollector metadataCollector = context.getMetadataCollector();
final TypeConfiguration typeConfiguration = metadataCollector.getTypeConfiguration(); final TypeConfiguration typeConfiguration = metadataCollector.getTypeConfiguration();
final Database database = metadataCollector.getDatabase(); final Database database = metadataCollector.getDatabase();
@ -72,21 +82,17 @@ public class AggregateComponentSecondPass implements SecondPass {
final List<Column> aggregatedColumns = component.getAggregatedColumns(); final List<Column> aggregatedColumns = component.getAggregatedColumns();
final AggregateColumn aggregateColumn = component.getAggregateColumn(); final AggregateColumn aggregateColumn = component.getAggregateColumn();
ensureInitialized( metadataCollector, typeConfiguration, dialect, aggregateColumn ); ensureInitialized( metadataCollector, aggregateColumn );
validateSupportedColumnTypes( propertyHolder.getPath(), component ); validateSupportedColumnTypes( propertyHolder.getPath(), component );
for ( org.hibernate.mapping.Column aggregatedColumn : aggregatedColumns ) { final QualifiedName structName = component.getStructName();
// Make sure this state is initialized
aggregatedColumn.getSqlTypeCode( metadataCollector );
aggregatedColumn.getSqlType( metadataCollector );
}
final String structName = component.getStructName();
final boolean addAuxiliaryObjects; final boolean addAuxiliaryObjects;
if ( structName != null ) { if ( structName != null ) {
final Namespace defaultNamespace = database.getDefaultNamespace(); final Namespace namespace = database.locateNamespace(
final Identifier udtName = Identifier.toIdentifier( structName ); structName.getCatalogName(),
final UserDefinedObjectType udt = new UserDefinedObjectType( "orm", defaultNamespace, udtName ); structName.getSchemaName()
);
final UserDefinedObjectType udt = new UserDefinedObjectType( "orm", namespace, structName.getObjectName() );
final Comment comment = componentXClass.getAnnotation( Comment.class ); final Comment comment = componentXClass.getAnnotation( Comment.class );
if ( comment != null ) { if ( comment != null ) {
udt.setComment( comment.value() ); udt.setComment( comment.value() );
@ -94,8 +100,8 @@ public class AggregateComponentSecondPass implements SecondPass {
for ( org.hibernate.mapping.Column aggregatedColumn : aggregatedColumns ) { for ( org.hibernate.mapping.Column aggregatedColumn : aggregatedColumns ) {
udt.addColumn( aggregatedColumn ); udt.addColumn( aggregatedColumn );
} }
final UserDefinedObjectType registeredUdt = defaultNamespace.createUserDefinedType( final UserDefinedObjectType registeredUdt = namespace.createUserDefinedType(
udtName, structName.getObjectName(),
name -> udt name -> udt
); );
if ( registeredUdt == udt ) { if ( registeredUdt == udt ) {
@ -187,6 +193,66 @@ public class AggregateComponentSecondPass implements SecondPass {
propertyHolder.getTable().getColumns().removeAll( aggregatedColumns ); propertyHolder.getTable().getColumns().removeAll( aggregatedColumns );
} }
private static void validateComponent(Component component, String basePath, boolean inArray) {
for ( Property property : component.getProperties() ) {
final Value value = property.getValue();
if ( value instanceof Component ) {
final Component c = (Component) value;
validateComponent( c, qualify( basePath, property.getName() ), inArray );
}
else if ( value instanceof ToOne ) {
final ToOne toOne = (ToOne) value;
if ( inArray && toOne.getReferencedPropertyName() != null ) {
throw new AnnotationException(
"Property '" + qualify( basePath, property.getName() )
+ "' uses one-to-one mapping with mappedBy '"
+ toOne.getReferencedPropertyName()
+ "' in the aggregate component class '"
+ component.getComponentClassName()
+ "' within an array property, which is not allowed."
);
}
}
else if ( value instanceof Collection ) {
final Collection collection = (Collection) value;
if ( inArray && collection.getMappedByProperty() != null ) {
throw new AnnotationException(
"Property '" + qualify( basePath, property.getName() )
+ "' uses *-to-many mapping with mappedBy '"
+ collection.getMappedByProperty()
+ "' in the aggregate component class '"
+ component.getComponentClassName()
+ "' within an array property, which is not allowed."
);
}
if ( inArray && collection.getCollectionTable() != null ) {
throw new AnnotationException(
"Property '" + qualify( basePath, property.getName() )
+ "' defines a collection table '"
+ collection.getCollectionTable()
+ "' in the aggregate component class '"
+ component.getComponentClassName()
+ "' within an array property, which is not allowed."
);
}
}
}
}
private boolean isAggregateArray() {
switch ( component.getAggregateColumn().getSqlTypeCode( context.getMetadataCollector() ) ) {
case SqlTypes.STRUCT_ARRAY:
case SqlTypes.STRUCT_TABLE:
case SqlTypes.JSON_ARRAY:
case SqlTypes.XML_ARRAY:
case SqlTypes.ARRAY:
case SqlTypes.TABLE:
return true;
default:
return false;
}
}
private void orderColumns(UserDefinedObjectType userDefinedType, int[] originalOrder) { private void orderColumns(UserDefinedObjectType userDefinedType, int[] originalOrder) {
final Class<?> componentClass = component.getComponentClass(); final Class<?> componentClass = component.getComponentClass();
final String[] structColumnNames = component.getStructColumnNames(); final String[] structColumnNames = component.getStructColumnNames();
@ -303,7 +369,7 @@ public class AggregateComponentSecondPass implements SecondPass {
if ( value instanceof Component ) { if ( value instanceof Component ) {
final Component subComponent = (Component) value; final Component subComponent = (Component) value;
if ( subComponent.getAggregateColumn() == null ) { if ( subComponent.getAggregateColumn() == null ) {
validateSupportedColumnTypes( StringHelper.qualify( basePath, property.getName() ), subComponent ); validateSupportedColumnTypes( qualify( basePath, property.getName() ), subComponent );
} }
} }
} }
@ -311,8 +377,27 @@ public class AggregateComponentSecondPass implements SecondPass {
private static void ensureInitialized( private static void ensureInitialized(
InFlightMetadataCollector metadataCollector, InFlightMetadataCollector metadataCollector,
TypeConfiguration typeConfiguration, AggregateColumn aggregateColumn) {
Dialect dialect, ensureParentInitialized( metadataCollector, aggregateColumn );
ensureChildrenInitialized( metadataCollector, aggregateColumn );
}
private static void ensureChildrenInitialized(
InFlightMetadataCollector metadataCollector,
AggregateColumn aggregateColumn) {
for ( Column aggregatedColumn : aggregateColumn.getComponent().getAggregatedColumns() ) {
// Make sure this state is initialized
aggregatedColumn.getSqlTypeCode( metadataCollector );
aggregatedColumn.getSqlType( metadataCollector );
if ( aggregatedColumn instanceof AggregateColumn ) {
ensureChildrenInitialized( metadataCollector, (AggregateColumn) aggregatedColumn );
}
}
}
private static void ensureParentInitialized(
InFlightMetadataCollector metadataCollector,
AggregateColumn aggregateColumn) { AggregateColumn aggregateColumn) {
do { do {
// Trigger resolving of the value so that the column gets properly filled // Trigger resolving of the value so that the column gets properly filled

View File

@ -1224,6 +1224,11 @@ public class BasicValueBinder implements JdbcTypeIndicators {
basicValue = new BasicValue( buildingContext, table ); basicValue = new BasicValue( buildingContext, table );
if ( columns.getPropertyHolder().isComponent() ) {
final ComponentPropertyHolder propertyHolder = (ComponentPropertyHolder) columns.getPropertyHolder();
basicValue.setAggregateColumn( propertyHolder.getAggregateColumn() );
}
if ( isNationalized() ) { if ( isNationalized() ) {
basicValue.makeNationalized(); basicValue.makeNationalized();
} }

View File

@ -14,6 +14,7 @@ import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty; import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData; import org.hibernate.boot.spi.PropertyData;
import org.hibernate.mapping.AggregateColumn;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.Join; import org.hibernate.mapping.Join;
import org.hibernate.mapping.KeyValue; import org.hibernate.mapping.KeyValue;
@ -265,7 +266,7 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
// if a property is set already the core cannot support that // if a property is set already the core cannot support that
if ( columns != null ) { if ( columns != null ) {
final Table table = columns.getTable(); final Table table = columns.getTable();
if ( !table.equals( component.getTable() ) ) { if ( !table.equals( getTable() ) ) {
if ( component.getPropertySpan() == 0 ) { if ( component.getPropertySpan() == 0 ) {
component.setTable( table ); component.setTable( table );
} }
@ -301,6 +302,11 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
return component.getOwner().getClassName(); return component.getOwner().getClassName();
} }
public AggregateColumn getAggregateColumn() {
final AggregateColumn aggregateColumn = component.getAggregateColumn();
return aggregateColumn != null ? aggregateColumn : component.getParentAggregateColumn();
}
@Override @Override
public Table getTable() { public Table getTable() {
return component.getTable(); return component.getTable();

View File

@ -28,6 +28,7 @@ import org.hibernate.boot.spi.AccessType;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData; import org.hibernate.boot.spi.PropertyData;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.mapping.AggregateColumn;
import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
@ -371,6 +372,14 @@ public class EmbeddableBinder {
returnedClassOrElement = context.getBootstrapContext().getReflectionManager() returnedClassOrElement = context.getBootstrapContext().getReflectionManager()
.toXClass( compositeUserType.embeddable() ); .toXClass( compositeUserType.embeddable() );
} }
AggregateComponentBinder.processAggregate(
component,
propertyHolder,
inferredData,
returnedClassOrElement,
columns,
context
);
final XClass annotatedClass = inferredData.getPropertyClass(); final XClass annotatedClass = inferredData.getPropertyClass();
final List<PropertyData> classElements = final List<PropertyData> classElements =
@ -447,14 +456,6 @@ public class EmbeddableBinder {
processCompositeUserType( component, compositeUserType ); processCompositeUserType( component, compositeUserType );
} }
AggregateComponentBinder.processAggregate(
component,
propertyHolder,
inferredData,
returnedClassOrElement,
columns,
context
);
return component; return component;
} }
@ -563,6 +564,7 @@ public class EmbeddableBinder {
columns.setBuildingContext( context ); columns.setBuildingContext( context );
discriminatorColumn.setParent( columns ); discriminatorColumn.setParent( columns );
final BasicValue discriminatorColumnBinding = new BasicValue( context, component.getTable() ); final BasicValue discriminatorColumnBinding = new BasicValue( context, component.getTable() );
discriminatorColumnBinding.setAggregateColumn( component.getAggregateColumn() );
component.setDiscriminator( discriminatorColumnBinding ); component.setDiscriminator( discriminatorColumnBinding );
discriminatorColumn.linkWithValue( discriminatorColumnBinding ); discriminatorColumn.linkWithValue( discriminatorColumnBinding );
discriminatorColumnBinding.setTypeName( discriminatorColumn.getDiscriminatorTypeName() ); discriminatorColumnBinding.setTypeName( discriminatorColumn.getDiscriminatorTypeName() );
@ -858,6 +860,10 @@ public class EmbeddableBinder {
if ( constructor != null ) { if ( constructor != null ) {
component.setInstantiator( constructor, constructor.getAnnotation( Instantiator.class ).value() ); component.setInstantiator( constructor, constructor.getAnnotation( Instantiator.class ).value() );
} }
if ( propertyHolder.isComponent() ) {
final ComponentPropertyHolder componentPropertyHolder = (ComponentPropertyHolder) propertyHolder;
component.setParentAggregateColumn( componentPropertyHolder.getAggregateColumn() );
}
return component; return component;
} }

View File

@ -34,6 +34,7 @@ import org.hibernate.mapping.UserDefinedArrayType;
import org.hibernate.mapping.UserDefinedObjectType; import org.hibernate.mapping.UserDefinedObjectType;
import org.hibernate.mapping.UserDefinedType; import org.hibernate.mapping.UserDefinedType;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType; import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.SqlTypedJdbcType; import org.hibernate.type.descriptor.jdbc.SqlTypedJdbcType;
@ -183,14 +184,17 @@ public class Namespace {
final UserDefinedType udt = entry.getValue(); final UserDefinedType udt = entry.getValue();
if ( udt instanceof UserDefinedObjectType ) { if ( udt instanceof UserDefinedObjectType ) {
for ( Column udtColumn : ( (UserDefinedObjectType) udt ).getColumns() ) { for ( Column udtColumn : ( (UserDefinedObjectType) udt ).getColumns() ) {
final JdbcType jdbcType = ( (BasicType<?>) udtColumn.getValue().getType() ).getJdbcType(); final Type udtColumnType = udtColumn.getValue().getType();
if ( jdbcType instanceof SqlTypedJdbcType ) { if ( udtColumnType instanceof BasicType<?> ) {
dependencies.add( Identifier.toIdentifier( ( (SqlTypedJdbcType) jdbcType ).getSqlTypeName() ) ); final JdbcType jdbcType = ( (BasicType<?>) udtColumnType ).getJdbcType();
} if ( jdbcType instanceof SqlTypedJdbcType ) {
else if ( jdbcType instanceof ArrayJdbcType ) { dependencies.add( Identifier.toIdentifier( ( (SqlTypedJdbcType) jdbcType ).getSqlTypeName() ) );
final JdbcType elementJdbcType = ( (ArrayJdbcType) jdbcType ).getElementJdbcType(); }
if ( elementJdbcType instanceof SqlTypedJdbcType ) { else if ( jdbcType instanceof ArrayJdbcType ) {
dependencies.add( Identifier.toIdentifier( ( (SqlTypedJdbcType) elementJdbcType ).getSqlTypeName() ) ); final JdbcType elementJdbcType = ( (ArrayJdbcType) jdbcType ).getElementJdbcType();
if ( elementJdbcType instanceof SqlTypedJdbcType ) {
dependencies.add( Identifier.toIdentifier( ( (SqlTypedJdbcType) elementJdbcType ).getSqlTypeName() ) );
}
} }
} }
} }

View File

@ -24,13 +24,11 @@ import java.util.ArrayList;
import java.util.TimeZone; import java.util.TimeZone;
import org.hibernate.internal.util.CharSequenceHelper; import org.hibernate.internal.util.CharSequenceHelper;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.spi.StringBuilderSqlAppender; import org.hibernate.sql.ast.spi.StringBuilderSqlAppender;
import org.hibernate.type.BasicPluralType; import org.hibernate.type.BasicPluralType;
@ -1008,7 +1006,7 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
final int size = numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 ); final int size = numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
int count = 0; int count = 0;
for ( int i = 0; i < size; i++ ) { for ( int i = 0; i < size; i++ ) {
final ValuedModelPart modelPart = getEmbeddedPart( embeddableMappingType, numberOfAttributeMappings, orderMapping[i] ); final ValuedModelPart modelPart = getEmbeddedPart( embeddableMappingType, orderMapping[i] );
final MappingType mappedType = modelPart.getMappedType(); final MappingType mappedType = modelPart.getMappedType();
if ( mappedType instanceof EmbeddableMappingType ) { if ( mappedType instanceof EmbeddableMappingType ) {
final EmbeddableMappingType embeddableMappingType = (EmbeddableMappingType) mappedType; final EmbeddableMappingType embeddableMappingType = (EmbeddableMappingType) mappedType;
@ -1178,7 +1176,7 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
return rawJdbcValue.toString(); return rawJdbcValue.toString();
} }
protected <X> String toString(X value, JavaType<X> javaType, WrapperOptions options) { protected <X> String toString(X value, JavaType<X> javaType, WrapperOptions options) throws SQLException {
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
@ -1187,86 +1185,62 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
return sb.toString(); return sb.toString();
} }
private void serializeStructTo(PostgreSQLAppender appender, Object value, WrapperOptions options) { private void serializeStructTo(PostgreSQLAppender appender, Object value, WrapperOptions options) throws SQLException {
serializeValuesTo( appender, options, embeddableMappingType, value, '(' ); serializeDomainValueTo( appender, options, value, '(' );
appender.append( ')' ); appender.append( ')' );
} }
private void serializeValuesTo( private void serializeDomainValueTo(
PostgreSQLAppender appender, PostgreSQLAppender appender,
WrapperOptions options, WrapperOptions options,
EmbeddableMappingType embeddableMappingType,
Object domainValue, Object domainValue,
char separator) { char separator) throws SQLException {
final Object[] array = embeddableMappingType.getValues( domainValue ); serializeJdbcValuesTo(
final int numberOfAttributes = embeddableMappingType.getNumberOfAttributeMappings(); appender,
for ( int i = 0; i < array.length; i++ ) { options,
final ValuedModelPart attributeMapping; StructHelper.getJdbcValues( embeddableMappingType, orderMapping, domainValue, options ),
final Object attributeValue; separator
if ( orderMapping == null ) { );
attributeMapping = getEmbeddedPart( embeddableMappingType, numberOfAttributes, i );
attributeValue = array[i];
}
else {
attributeMapping = getEmbeddedPart( embeddableMappingType, numberOfAttributes, orderMapping[i] );
attributeValue = array[orderMapping[i]];
}
if ( attributeMapping instanceof BasicValuedMapping ) {
appender.append( separator );
separator = ',';
if ( attributeValue == null ) {
continue;
}
final JdbcMapping jdbcMapping = ( (BasicValuedMapping) attributeMapping ).getJdbcMapping();
serializeBasicTo( appender, options, jdbcMapping, attributeValue );
}
else if ( attributeMapping instanceof EmbeddedAttributeMapping ) {
final EmbeddableMappingType mappingType = (EmbeddableMappingType) attributeMapping.getMappedType();
final SelectableMapping aggregateMapping = mappingType.getAggregateMapping();
if ( aggregateMapping == null ) {
serializeValuesTo(
appender,
options,
mappingType,
attributeValue,
separator
);
separator = ',';
}
else {
appender.append( separator );
separator = ',';
if ( attributeValue == null ) {
continue;
}
appender.quoteStart();
( (AbstractPostgreSQLStructJdbcType) aggregateMapping.getJdbcMapping().getJdbcType() ).serializeStructTo(
appender,
attributeValue,
options
);
appender.quoteEnd();
}
}
else {
throw new UnsupportedOperationException( "Unsupported attribute mapping: " + attributeMapping );
}
}
} }
private void serializeBasicTo( private void serializeJdbcValuesTo(
PostgreSQLAppender appender, PostgreSQLAppender appender,
WrapperOptions options, WrapperOptions options,
JdbcMapping jdbcMapping, Object[] jdbcValues,
Object value) { char separator) throws SQLException {
serializeConvertedBasicTo( appender, options, jdbcMapping, jdbcMapping.convertToRelationalValue( value ) ); for ( int i = 0; i < jdbcValues.length; i++ ) {
appender.append( separator );
separator = ',';
final Object jdbcValue = jdbcValues[i];
if ( jdbcValue == null ) {
continue;
}
final SelectableMapping selectableMapping = orderMapping == null ?
embeddableMappingType.getJdbcValueSelectable( i ) :
embeddableMappingType.getJdbcValueSelectable( orderMapping[i] );
final JdbcMapping jdbcMapping = selectableMapping.getJdbcMapping();
if ( jdbcMapping.getJdbcType() instanceof AbstractPostgreSQLStructJdbcType ) {
appender.quoteStart();
( (AbstractPostgreSQLStructJdbcType) jdbcMapping.getJdbcType() ).serializeJdbcValuesTo(
appender,
options,
(Object[]) jdbcValue,
'('
);
appender.append( ')' );
appender.quoteEnd();
}
else {
serializeConvertedBasicTo( appender, options, jdbcMapping, jdbcValue );
}
}
} }
private void serializeConvertedBasicTo( private void serializeConvertedBasicTo(
PostgreSQLAppender appender, PostgreSQLAppender appender,
WrapperOptions options, WrapperOptions options,
JdbcMapping jdbcMapping, JdbcMapping jdbcMapping,
Object subValue) { Object subValue) throws SQLException {
//noinspection unchecked //noinspection unchecked
final JavaType<Object> jdbcJavaType = (JavaType<Object>) jdbcMapping.getJdbcJavaType(); final JavaType<Object> jdbcJavaType = (JavaType<Object>) jdbcMapping.getJdbcJavaType();
switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) { switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) {
@ -1291,14 +1265,7 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
case SqlTypes.DECIMAL: case SqlTypes.DECIMAL:
case SqlTypes.NUMERIC: case SqlTypes.NUMERIC:
case SqlTypes.DURATION: case SqlTypes.DURATION:
jdbcJavaType.appendEncodedString( appender.append( subValue.toString() );
appender,
jdbcJavaType.unwrap(
subValue,
jdbcJavaType.getJavaTypeClass(),
options
)
);
break; break;
case SqlTypes.CHAR: case SqlTypes.CHAR:
case SqlTypes.NCHAR: case SqlTypes.NCHAR:
@ -1316,14 +1283,7 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
case SqlTypes.ENUM: case SqlTypes.ENUM:
case SqlTypes.NAMED_ENUM: case SqlTypes.NAMED_ENUM:
appender.quoteStart(); appender.quoteStart();
jdbcJavaType.appendEncodedString( appender.append( (String) subValue );
appender,
jdbcJavaType.unwrap(
subValue,
jdbcJavaType.getJavaTypeClass(),
options
)
);
appender.quoteEnd(); appender.quoteEnd();
break; break;
case SqlTypes.DATE: case SqlTypes.DATE:
@ -1393,9 +1353,8 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
case SqlTypes.STRUCT: case SqlTypes.STRUCT:
if ( subValue != null ) { if ( subValue != null ) {
final AbstractPostgreSQLStructJdbcType structJdbcType = (AbstractPostgreSQLStructJdbcType) jdbcMapping.getJdbcType(); final AbstractPostgreSQLStructJdbcType structJdbcType = (AbstractPostgreSQLStructJdbcType) jdbcMapping.getJdbcType();
final EmbeddableMappingType subEmbeddableMappingType = structJdbcType.getEmbeddableMappingType();
appender.quoteStart(); appender.quoteStart();
structJdbcType.serializeValuesTo( appender, options, subEmbeddableMappingType, subValue, '(' ); structJdbcType.serializeJdbcValuesTo( appender, options, (Object[]) subValue, '(' );
appender.append( ')' ); appender.append( ')' );
appender.quoteEnd(); appender.quoteEnd();
} }
@ -1427,9 +1386,8 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
else { else {
attributeIndex = orderMapping[i]; attributeIndex = orderMapping[i];
} }
final ValuedModelPart modelPart = getEmbeddedPart( embeddableMappingType, numberOfAttributeMappings, attributeIndex );
jdbcIndex += injectAttributeValue( jdbcIndex += injectAttributeValue(
modelPart, getEmbeddedPart( embeddableMappingType, attributeIndex ),
attributeValues, attributeValues,
attributeIndex, attributeIndex,
rawJdbcValues, rawJdbcValues,
@ -1567,6 +1525,10 @@ public abstract class AbstractPostgreSQLStructJdbcType implements StructJdbcType
: options.getJdbcTimeZone(); : options.getJdbcTimeZone();
} }
protected <X> Object getBindValue(X value, WrapperOptions options) throws SQLException {
return StructHelper.getJdbcValues( embeddableMappingType, orderMapping, value, options );
}
private static class PostgreSQLAppender extends StringBuilderSqlAppender { private static class PostgreSQLAppender extends StringBuilderSqlAppender {
private int quote = 1; private int quote = 1;

View File

@ -410,6 +410,8 @@ public class CockroachDialect extends Dialect {
.getDescriptor( Object.class ) .getDescriptor( Object.class )
) )
); );
jdbcTypeRegistry.addTypeConstructor( PostgreSQLArrayJdbcTypeConstructor.INSTANCE );
} }
@Override @Override

View File

@ -73,7 +73,7 @@ public class JsonHelper {
final Object[] values = embeddableMappingType.getValues( domainValue ); final Object[] values = embeddableMappingType.getValues( domainValue );
final int numberOfAttributes = embeddableMappingType.getNumberOfAttributeMappings(); final int numberOfAttributes = embeddableMappingType.getNumberOfAttributeMappings();
for ( int i = 0; i < values.length; i++ ) { for ( int i = 0; i < values.length; i++ ) {
final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, numberOfAttributes, i ); final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, i );
if ( attributeMapping instanceof SelectableMapping ) { if ( attributeMapping instanceof SelectableMapping ) {
final String name = ( (SelectableMapping) attributeMapping ).getSelectableName(); final String name = ( (SelectableMapping) attributeMapping ).getSelectableName();
appender.append( separator ); appender.append( separator );

View File

@ -69,6 +69,8 @@ public class OracleArrayJdbcType extends ArrayJdbcType implements SqlTypedJdbcTy
@Override @Override
public <X> ValueBinder<X> getBinder(final JavaType<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaType<X> javaTypeDescriptor) {
//noinspection unchecked
final ValueBinder<Object> elementBinder = getElementJdbcType().getBinder( ( (BasicPluralJavaType<Object>) javaTypeDescriptor ).getElementJavaType() );
return new BasicBinder<>( javaTypeDescriptor, this ) { return new BasicBinder<>( javaTypeDescriptor, this ) {
private String typeName(WrapperOptions options) { private String typeName(WrapperOptions options) {
return ( upperTypeName == null return ( upperTypeName == null
@ -105,7 +107,7 @@ public class OracleArrayJdbcType extends ArrayJdbcType implements SqlTypedJdbcTy
@Override @Override
public java.sql.Array getBindValue(X value, WrapperOptions options) throws SQLException { public java.sql.Array getBindValue(X value, WrapperOptions options) throws SQLException {
final Object[] objects = OracleArrayJdbcType.this.getArray( this, value, options ); final Object[] objects = ( (OracleArrayJdbcType) getJdbcType() ).getArray( this, elementBinder, value, options );
final String arrayTypeName = typeName( options ); final String arrayTypeName = typeName( options );
final OracleConnection oracleConnection = options.getSession() final OracleConnection oracleConnection = options.getSession()

View File

@ -6,6 +6,9 @@
*/ */
package org.hibernate.dialect; package org.hibernate.dialect;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Locale; import java.util.Locale;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
@ -17,9 +20,12 @@ import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType; import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
import org.hibernate.type.descriptor.jdbc.BasicBinder;
import oracle.sql.TIMESTAMPTZ; import oracle.sql.TIMESTAMPTZ;
@ -41,6 +47,28 @@ public class OracleBaseStructJdbcType extends StructJdbcType {
); );
} }
@Override
public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
return new BasicBinder<>( javaType, this ) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException {
st.setObject( index, createJdbcValue( value, options ) );
}
@Override
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
throws SQLException {
st.setObject( name, createJdbcValue( value, options ) );
}
@Override
public Object getBindValue(X value, WrapperOptions options) throws SQLException {
return createJdbcValue( value, options );
}
};
}
@Override @Override
public String getExtraCreateTableInfo( public String getExtraCreateTableInfo(
JavaType<?> javaType, JavaType<?> javaType,
@ -51,21 +79,24 @@ public class OracleBaseStructJdbcType extends StructJdbcType {
.locateUserDefinedType( Identifier.toIdentifier( getSqlTypeName() ) ); .locateUserDefinedType( Identifier.toIdentifier( getSqlTypeName() ) );
StringBuilder sb = null; StringBuilder sb = null;
for ( Column column : udt.getColumns() ) { for ( Column column : udt.getColumns() ) {
final JdbcMapping jdbcMapping = (JdbcMapping) column.getValue().getType(); final Type columnType = column.getValue().getType();
final String extraCreateTableInfo = jdbcMapping.getJdbcType().getExtraCreateTableInfo( if ( columnType instanceof JdbcMapping ) {
jdbcMapping.getJavaTypeDescriptor(), final JdbcMapping jdbcMapping = (JdbcMapping) columnType;
columnName + "." + column.getName(), final String extraCreateTableInfo = jdbcMapping.getJdbcType().getExtraCreateTableInfo(
tableName, jdbcMapping.getJavaTypeDescriptor(),
database columnName + "." + column.getName(),
); tableName,
if ( !extraCreateTableInfo.isEmpty() ) { database
if ( sb == null ) { );
sb = new StringBuilder(); if ( !extraCreateTableInfo.isEmpty() ) {
if ( sb == null ) {
sb = new StringBuilder();
}
else {
sb.append( ',' );
}
sb.append( extraCreateTableInfo );
} }
else {
sb.append( ',' );
}
sb.append( extraCreateTableInfo );
} }
} }
return sb != null ? sb.toString() : ""; return sb != null ? sb.toString() : "";

View File

@ -0,0 +1,97 @@
/*
* 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.dialect;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.BasicPluralJavaType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.jdbc.AggregateJdbcType;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcType;
import org.hibernate.type.descriptor.jdbc.BasicBinder;
import org.hibernate.type.descriptor.jdbc.JdbcType;
/**
* Descriptor for {@link Types#ARRAY ARRAY} handling.
*/
public class PostgreSQLArrayJdbcType extends ArrayJdbcType {
public PostgreSQLArrayJdbcType(JdbcType elementJdbcType) {
super( elementJdbcType );
}
@Override
public <X> ValueBinder<X> getBinder(final JavaType<X> javaTypeDescriptor) {
//noinspection unchecked
final ValueBinder<Object> elementBinder = getElementJdbcType().getBinder( ( (BasicPluralJavaType<Object>) javaTypeDescriptor ).getElementJavaType() );
return new BasicBinder<>( javaTypeDescriptor, this ) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException {
st.setArray( index, getArray( value, options ) );
}
@Override
protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
throws SQLException {
final java.sql.Array arr = getArray( value, options );
try {
st.setObject( name, arr, java.sql.Types.ARRAY );
}
catch (SQLException ex) {
throw new HibernateException( "JDBC driver does not support named parameters for setArray. Use positional.", ex );
}
}
@Override
public Object getBindValue(X value, WrapperOptions options) throws SQLException {
return ( (PostgreSQLArrayJdbcType) getJdbcType() ).getArray( this, elementBinder, value, options );
}
private java.sql.Array getArray(X value, WrapperOptions options) throws SQLException {
final PostgreSQLArrayJdbcType arrayJdbcType = (PostgreSQLArrayJdbcType) getJdbcType();
final Object[] objects;
final JdbcType elementJdbcType = arrayJdbcType.getElementJdbcType();
if ( elementJdbcType instanceof AggregateJdbcType ) {
// The PostgreSQL JDBC driver does not support arrays of structs, which contain byte[]
final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) elementJdbcType;
final Object[] domainObjects = getJavaType().unwrap(
value,
Object[].class,
options
);
objects = new Object[domainObjects.length];
for ( int i = 0; i < domainObjects.length; i++ ) {
objects[i] = aggregateJdbcType.createJdbcValue( domainObjects[i], options );
}
}
else {
objects = arrayJdbcType.getArray( this, elementBinder, value, options );
}
final SharedSessionContractImplementor session = options.getSession();
final String typeName = arrayJdbcType.getElementTypeName( getJavaType(), session );
return session.getJdbcCoordinator().getLogicalConnection().getPhysicalConnection()
.createArrayOf( typeName, objects );
}
};
}
@Override
public String toString() {
return "PostgreSQLArrayTypeDescriptor(" + getElementJdbcType().toString() + ")";
}
}

View File

@ -0,0 +1,46 @@
/*
* 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.dialect;
import java.sql.Types;
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Factory for {@link PostgreSQLArrayJdbcType}.
*/
public class PostgreSQLArrayJdbcTypeConstructor implements JdbcTypeConstructor {
public static final PostgreSQLArrayJdbcTypeConstructor INSTANCE = new PostgreSQLArrayJdbcTypeConstructor();
@Override
public JdbcType resolveType(
TypeConfiguration typeConfiguration,
Dialect dialect,
BasicType<?> elementType,
ColumnTypeInformation columnTypeInformation) {
return resolveType( typeConfiguration, dialect, elementType.getJdbcType(), columnTypeInformation );
}
@Override
public JdbcType resolveType(
TypeConfiguration typeConfiguration,
Dialect dialect,
JdbcType elementType,
ColumnTypeInformation columnTypeInformation) {
return new PostgreSQLArrayJdbcType( elementType );
}
@Override
public int getDefaultSqlTypeCode() {
return Types.ARRAY;
}
}

View File

@ -1474,6 +1474,8 @@ public class PostgreSQLDialect extends Dialect {
jdbcTypeRegistry.addDescriptor( PostgreSQLEnumJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptor( PostgreSQLEnumJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( PostgreSQLOrdinalEnumJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptor( PostgreSQLOrdinalEnumJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( PostgreSQLUUIDJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptor( PostgreSQLUUIDJdbcType.INSTANCE );
jdbcTypeRegistry.addTypeConstructor( PostgreSQLArrayJdbcTypeConstructor.INSTANCE );
} }
@Override @Override

View File

@ -89,6 +89,11 @@ public class PostgreSQLStructCastingJdbcType extends AbstractPostgreSQLStructJdb
); );
st.setString( name, stringValue ); st.setString( name, stringValue );
} }
@Override
public Object getBindValue(X value, WrapperOptions options) throws SQLException {
return ( (PostgreSQLStructCastingJdbcType) getJdbcType() ).getBindValue( value, options );
}
}; };
} }
} }

View File

@ -97,6 +97,11 @@ public class PostgreSQLStructPGObjectJdbcType extends AbstractPostgreSQLStructJd
holder.setValue( stringValue ); holder.setValue( stringValue );
st.setObject( name, holder ); st.setObject( name, holder );
} }
@Override
public Object getBindValue(X value, WrapperOptions options) throws SQLException {
return ( (PostgreSQLStructPGObjectJdbcType) getJdbcType() ).getBindValue( value, options );
}
}; };
} }

View File

@ -14,12 +14,18 @@ import java.sql.SQLException;
import org.hibernate.Internal; import org.hibernate.Internal;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping;
import org.hibernate.metamodel.spi.EmbeddableInstantiator; import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy; import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.type.SqlTypes; import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.WrapperOptions; import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
@ -39,12 +45,14 @@ public class StructHelper {
final StructAttributeValues attributeValues = new StructAttributeValues( numberOfAttributeMappings, rawJdbcValues ); final StructAttributeValues attributeValues = new StructAttributeValues( numberOfAttributeMappings, rawJdbcValues );
int jdbcIndex = 0; int jdbcIndex = 0;
for ( int i = 0; i < size; i++ ) { for ( int i = 0; i < size; i++ ) {
final ValuedModelPart valuedModelPart = getEmbeddedPart( jdbcIndex += injectAttributeValue(
embeddableMappingType, getEmbeddedPart( embeddableMappingType, i ),
numberOfAttributeMappings, attributeValues,
i i,
rawJdbcValues,
jdbcIndex,
options
); );
jdbcIndex += injectAttributeValue( valuedModelPart, attributeValues, i, rawJdbcValues, jdbcIndex, options );
} }
return attributeValues; return attributeValues;
} }
@ -104,26 +112,58 @@ public class StructHelper {
else { else {
jdbcValues = values; jdbcValues = values;
} }
int jdbcIndex = 0; injectJdbcValues(
embeddableMappingType,
values,
jdbcValues,
0,
options
);
if ( orderMapping != null ) {
final Object[] originalJdbcValues = jdbcValues.clone();
for ( int i = 0; i < orderMapping.length; i++ ) {
jdbcValues[i] = originalJdbcValues[orderMapping[i]];
}
}
return jdbcValues;
}
private static int injectJdbcValues(
EmbeddableMappingType embeddableMappingType,
Object domainValue,
Object[] jdbcValues,
int jdbcIndex,
WrapperOptions options) throws SQLException {
return injectJdbcValues(
embeddableMappingType,
embeddableMappingType.getValues( domainValue ),
jdbcValues,
jdbcIndex,
options
);
}
private static int injectJdbcValues(
EmbeddableMappingType embeddableMappingType,
Object[] values,
Object[] jdbcValues,
int jdbcIndex,
WrapperOptions options) throws SQLException {
final int jdbcValueCount = embeddableMappingType.getJdbcValueCount();
final int valueCount = jdbcValueCount + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
int offset = 0;
for ( int i = 0; i < values.length; i++ ) { for ( int i = 0; i < values.length; i++ ) {
final int attributeIndex; offset += injectJdbcValue(
if ( orderMapping == null ) { getEmbeddedPart( embeddableMappingType, i ),
attributeIndex = i;
}
else {
attributeIndex = orderMapping[i];
}
jdbcIndex += injectJdbcValue(
getEmbeddedPart( embeddableMappingType, jdbcValueCount, attributeIndex ),
values, values,
attributeIndex, i,
jdbcValues, jdbcValues,
jdbcIndex, jdbcIndex + offset,
options options
); );
} }
assert jdbcIndex == valueCount; assert offset == valueCount;
return jdbcValues; return offset;
} }
public static Object instantiate( public static Object instantiate(
@ -142,13 +182,10 @@ public class StructHelper {
return instantiator.instantiate( attributeValues, sessionFactory ); return instantiator.instantiate( attributeValues, sessionFactory );
} }
public static ValuedModelPart getEmbeddedPart( public static ValuedModelPart getEmbeddedPart(EmbeddableMappingType embeddableMappingType, int position) {
EmbeddableMappingType embeddableMappingType, return position == embeddableMappingType.getNumberOfAttributeMappings()
int numberOfAttributes, ? embeddableMappingType.getDiscriminatorMapping()
int position) { : embeddableMappingType.getAttributeMapping( position );
return position == numberOfAttributes ?
embeddableMappingType.getDiscriminatorMapping() :
embeddableMappingType.getAttributeMapping( position );
} }
private static int injectJdbcValue( private static int injectJdbcValue(
@ -158,20 +195,65 @@ public class StructHelper {
Object[] jdbcValues, Object[] jdbcValues,
int jdbcIndex, int jdbcIndex,
WrapperOptions options) throws SQLException { WrapperOptions options) throws SQLException {
final MappingType mappedType = attributeMapping.getMappedType();
final int jdbcValueCount; final int jdbcValueCount;
if ( mappedType instanceof EmbeddableMappingType ) { if ( attributeMapping instanceof ToOneAttributeMapping ) {
final EmbeddableMappingType embeddableMappingType = (EmbeddableMappingType) mappedType; final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping;
if ( embeddableMappingType.getAggregateMapping() != null ) { if ( toOneAttributeMapping.getSideNature() == ForeignKeyDescriptor.Nature.TARGET ) {
final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) embeddableMappingType.getAggregateMapping() return 0;
.getJdbcMapping() }
.getJdbcType(); final ForeignKeyDescriptor foreignKeyDescriptor = toOneAttributeMapping.getForeignKeyDescriptor();
final ValuedModelPart keyPart = foreignKeyDescriptor.getKeyPart();
final Object foreignKeyValue = foreignKeyDescriptor.getAssociationKeyFromSide(
attributeValues[attributeIndex],
ForeignKeyDescriptor.Nature.TARGET,
options.getSession()
);
if ( keyPart instanceof BasicValuedMapping ) {
jdbcValueCount = 1; jdbcValueCount = 1;
jdbcValues[jdbcIndex] = aggregateJdbcType.createJdbcValue( jdbcValues[jdbcIndex] = foreignKeyValue;
attributeValues[attributeIndex], }
else if ( keyPart instanceof EmbeddableValuedModelPart ) {
final EmbeddableMappingType mappingType = ( (EmbeddableValuedModelPart) keyPart ).getEmbeddableTypeDescriptor();
jdbcValueCount = injectJdbcValues(
mappingType,
foreignKeyValue,
jdbcValues,
jdbcIndex,
options options
); );
} }
else {
throw new UnsupportedOperationException( "Unsupported foreign key part: " + keyPart );
}
}
else if ( attributeMapping instanceof PluralAttributeMapping ) {
return 0;
}
else if ( attributeMapping instanceof DiscriminatedAssociationAttributeMapping ) {
jdbcValueCount = attributeMapping.decompose(
attributeValues[attributeIndex],
jdbcIndex,
jdbcValues,
options,
(valueIndex, objects, wrapperOptions, value, jdbcValueMapping) -> {
objects[valueIndex] = value;
},
options.getSession()
);
}
else if ( attributeMapping instanceof EmbeddableValuedModelPart ) {
final EmbeddableValuedModelPart embeddableValuedModelPart = (EmbeddableValuedModelPart) attributeMapping;
final EmbeddableMappingType embeddableMappingType = embeddableValuedModelPart.getMappedType();
if ( embeddableMappingType.getAggregateMapping() != null ) {
jdbcValueCount = 1;
jdbcValues[jdbcIndex] = embeddableMappingType.getAggregateMapping()
.getJdbcMapping()
.getJdbcValueBinder()
.getBindValue(
attributeValues[attributeIndex],
options
);
}
else { else {
jdbcValueCount = embeddableMappingType.getJdbcValueCount() + ( embeddableMappingType.isPolymorphic() ? 1 : 0 ); jdbcValueCount = embeddableMappingType.getJdbcValueCount() + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings(); final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings();
@ -180,7 +262,7 @@ public class StructHelper {
int offset = 0; int offset = 0;
for ( int i = 0; i < numberOfValues; i++ ) { for ( int i = 0; i < numberOfValues; i++ ) {
offset += injectJdbcValue( offset += injectJdbcValue(
getEmbeddedPart( embeddableMappingType, numberOfAttributeMappings, i ), getEmbeddedPart( embeddableMappingType, i ),
subValues, subValues,
i, i,
jdbcValues, jdbcValues,
@ -251,23 +333,27 @@ public class StructHelper {
int[] inverseMapping, int[] inverseMapping,
Object[] sourceJdbcValues, Object[] sourceJdbcValues,
Object[] targetJdbcValues) { Object[] targetJdbcValues) {
final int numberOfAttributes = embeddableMappingType.getNumberOfAttributeMappings(); for ( int i = 0; i < inverseMapping.length; i++ ) {
int targetJdbcOffset = 0; targetJdbcValues[i] = sourceJdbcValues[inverseMapping[i]];
for ( int i = 0; i < numberOfAttributes + ( embeddableMappingType.isPolymorphic() ? 1 : 0 ); i++ ) {
final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, numberOfAttributes, 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];
}
} }
// final int numberOfAttributes = embeddableMappingType.getNumberOfAttributeMappings();
// int targetJdbcOffset = 0;
// for ( int i = 0; i < numberOfAttributes + ( embeddableMappingType.isPolymorphic() ? 1 : 0 ); i++ ) {
// final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, 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) { public static int getJdbcValueCount(MappingType mappedType) {

View File

@ -13,10 +13,16 @@ import java.sql.SQLException;
import java.sql.Struct; import java.sql.Struct;
import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.type.BasicPluralType; import org.hibernate.type.BasicPluralType;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
@ -144,9 +150,12 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
@Override @Override
public Object[] extractJdbcValues(Object rawJdbcValue, WrapperOptions options) throws SQLException { public Object[] extractJdbcValues(Object rawJdbcValue, WrapperOptions options) throws SQLException {
final Object[] attributes = ( (Struct) rawJdbcValue ).getAttributes(); final Object[] jdbcValues = ( (Struct) rawJdbcValue ).getAttributes();
wrapRawJdbcValues( embeddableMappingType, orderMapping, inverseOrderMapping, attributes, 0, options ); if ( orderMapping != null ) {
return attributes; StructHelper.orderJdbcValues( embeddableMappingType, inverseOrderMapping, jdbcValues.clone(), jdbcValues );
}
wrapRawJdbcValues( embeddableMappingType, jdbcValues, 0, options );
return jdbcValues;
} }
@Override @Override
@ -198,18 +207,21 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
return null; return null;
} }
final Struct struct = (Struct) object; final Struct struct = (Struct) object;
final Object[] values = struct.getAttributes(); final Object[] jdbcValues = struct.getAttributes();
final boolean jdbcRepresentation = getJavaType().getJavaTypeClass() == Object[].class; final boolean jdbcRepresentation = getJavaType().getJavaTypeClass() == Object[].class;
if ( jdbcRepresentation ) { if ( jdbcRepresentation ) {
wrapRawJdbcValues( embeddableMappingType, orderMapping, inverseOrderMapping, values, 0, options ); if ( orderMapping != null ) {
StructHelper.orderJdbcValues( embeddableMappingType, inverseOrderMapping, jdbcValues.clone(), jdbcValues );
}
wrapRawJdbcValues( embeddableMappingType, jdbcValues, 0, options );
//noinspection unchecked //noinspection unchecked
return (X) values; return (X) jdbcValues;
} }
assert embeddableMappingType != null && embeddableMappingType.getJavaType() == getJavaType(); assert embeddableMappingType != null && embeddableMappingType.getJavaType() == getJavaType();
final StructAttributeValues attributeValues = getAttributeValues( final StructAttributeValues attributeValues = getAttributeValues(
embeddableMappingType, embeddableMappingType,
orderMapping, orderMapping,
values, jdbcValues,
options options
); );
//noinspection unchecked //noinspection unchecked
@ -240,9 +252,8 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
else { else {
attributeIndex = orderMapping[i]; attributeIndex = orderMapping[i];
} }
final ValuedModelPart modelPart = getEmbeddedPart( embeddableMappingType, numberOfAttributeMappings, attributeIndex );
jdbcIndex += injectAttributeValue( jdbcIndex += injectAttributeValue(
modelPart, getEmbeddedPart( embeddableMappingType, attributeIndex ),
attributeValues, attributeValues,
attributeIndex, attributeIndex,
rawJdbcValues, rawJdbcValues,
@ -381,117 +392,150 @@ public class StructJdbcType implements org.hibernate.type.descriptor.jdbc.Struct
private int wrapRawJdbcValues( private int wrapRawJdbcValues(
EmbeddableMappingType embeddableMappingType, EmbeddableMappingType embeddableMappingType,
int[] orderMapping,
int[] inverseOrderMapping,
Object[] jdbcValues, Object[] jdbcValues,
int jdbcIndex, int jdbcIndex,
WrapperOptions options) throws SQLException { WrapperOptions options) throws SQLException {
final Object[] targetJdbcValues;
if ( orderMapping == null ) {
targetJdbcValues = jdbcValues;
}
else {
targetJdbcValues = jdbcValues.clone();
}
final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings(); final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings();
for ( int i = 0; i < numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 ); i++ ) { for ( int i = 0; i < numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 ); i++ ) {
final ValuedModelPart attributeMapping; final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, i );
if ( orderMapping == null ) { if ( attributeMapping instanceof ToOneAttributeMapping ) {
attributeMapping = getEmbeddedPart( embeddableMappingType, numberOfAttributeMappings, i ); final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping;
if ( toOneAttributeMapping.getSideNature() == ForeignKeyDescriptor.Nature.TARGET ) {
continue;
}
final ForeignKeyDescriptor foreignKeyDescriptor = toOneAttributeMapping.getForeignKeyDescriptor();
final ValuedModelPart keyPart = foreignKeyDescriptor.getKeyPart();
if ( keyPart instanceof BasicValuedMapping ) {
wrapRawJdbcValue( keyPart.getSingleJdbcMapping(), jdbcValues, jdbcIndex, options );
jdbcIndex++;
}
else if ( keyPart instanceof EmbeddableValuedModelPart ) {
final EmbeddableMappingType mappingType = ( (EmbeddableValuedModelPart) keyPart ).getEmbeddableTypeDescriptor();
jdbcIndex = wrapRawJdbcValues(
mappingType,
jdbcValues,
jdbcIndex,
options
);
}
else {
throw new UnsupportedOperationException( "Unsupported foreign key part: " + keyPart );
}
} }
else { else if ( attributeMapping instanceof PluralAttributeMapping ) {
attributeMapping = getEmbeddedPart( embeddableMappingType, numberOfAttributeMappings, orderMapping[i] ); continue;
} }
final MappingType mappedType = attributeMapping.getMappedType(); else if ( attributeMapping instanceof DiscriminatedAssociationAttributeMapping ) {
final DiscriminatedAssociationAttributeMapping discriminatedAssociationAttributeMapping = (DiscriminatedAssociationAttributeMapping) attributeMapping;
if ( mappedType instanceof EmbeddableMappingType ) { wrapRawJdbcValue(
final EmbeddableMappingType embeddableType = (EmbeddableMappingType) mappedType; discriminatedAssociationAttributeMapping.getDiscriminatorPart().getSingleJdbcMapping(),
jdbcValues,
jdbcIndex,
options
);
jdbcIndex++;
wrapRawJdbcValue(
discriminatedAssociationAttributeMapping.getKeyPart().getSingleJdbcMapping(),
jdbcValues,
jdbcIndex,
options
);
jdbcIndex++;
}
else if ( attributeMapping instanceof EmbeddableValuedModelPart ) {
final EmbeddableValuedModelPart embeddableValuedModelPart = (EmbeddableValuedModelPart) attributeMapping;
final EmbeddableMappingType embeddableType = embeddableValuedModelPart.getMappedType();
if ( embeddableType.getAggregateMapping() != null ) { if ( embeddableType.getAggregateMapping() != null ) {
final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) embeddableType.getAggregateMapping() final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) embeddableType.getAggregateMapping()
.getJdbcMapping() .getJdbcMapping()
.getJdbcType(); .getJdbcType();
final Object rawJdbcValue = targetJdbcValues[jdbcIndex]; final Object rawJdbcValue = jdbcValues[jdbcIndex];
targetJdbcValues[jdbcIndex] = aggregateJdbcType.extractJdbcValues( rawJdbcValue, options ); jdbcValues[jdbcIndex] = aggregateJdbcType.extractJdbcValues( rawJdbcValue, options );
jdbcIndex++; jdbcIndex++;
} }
else { else {
jdbcIndex = wrapRawJdbcValues( embeddableType, null, null, targetJdbcValues, jdbcIndex, options ); jdbcIndex = wrapRawJdbcValues( embeddableType, jdbcValues, jdbcIndex, options );
} }
} }
else { else {
assert attributeMapping.getJdbcTypeCount() == 1; assert attributeMapping.getJdbcTypeCount() == 1;
final Object rawJdbcValue = targetJdbcValues[jdbcIndex]; wrapRawJdbcValue( attributeMapping.getSingleJdbcMapping(), jdbcValues, jdbcIndex, options );
if ( rawJdbcValue != null ) {
final JdbcMapping jdbcMapping = attributeMapping.getSingleJdbcMapping();
switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) {
case SqlTypes.TIME_WITH_TIMEZONE:
case SqlTypes.TIME_UTC:
case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
case SqlTypes.TIMESTAMP_UTC:
// Only transform the raw jdbc value if it could be a TIMESTAMPTZ
targetJdbcValues[jdbcIndex] = jdbcMapping.getJdbcJavaType()
.wrap( transformRawJdbcValue( rawJdbcValue, options ), options );
break;
case SqlTypes.ARRAY:
final BasicType<?> elementType = ( (BasicPluralType<?, ?>) jdbcMapping ).getElementType();
final JdbcType elementJdbcType = elementType.getJdbcType();
final Object[] array;
final Object[] newArray;
switch ( elementJdbcType.getDefaultSqlTypeCode() ) {
case SqlTypes.TIME_WITH_TIMEZONE:
case SqlTypes.TIME_UTC:
case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
case SqlTypes.TIMESTAMP_UTC:
// Only transform the raw jdbc value if it could be a TIMESTAMPTZ
array = (Object[]) ((java.sql.Array) rawJdbcValue).getArray();
newArray = new Object[array.length];
for ( int j = 0; j < array.length; j++ ) {
newArray[j] = elementType.getJdbcJavaType().wrap(
transformRawJdbcValue( array[j], options ),
options
);
}
targetJdbcValues[jdbcIndex] = jdbcMapping.getJdbcJavaType().wrap( newArray, options );
break;
case SqlTypes.STRUCT:
case SqlTypes.JSON:
case SqlTypes.SQLXML:
array = (Object[]) ( (java.sql.Array) rawJdbcValue ).getArray();
newArray = new Object[array.length];
final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) elementJdbcType;
final EmbeddableMappingType subEmbeddableMappingType = aggregateJdbcType.getEmbeddableMappingType();
for ( int j = 0; j < array.length; j++ ) {
final StructAttributeValues subValues = StructHelper.getAttributeValues(
subEmbeddableMappingType,
aggregateJdbcType.extractJdbcValues(
array[j],
options
),
options
);
newArray[j] = instantiate( subEmbeddableMappingType, subValues, options.getSessionFactory() );
}
targetJdbcValues[jdbcIndex] = jdbcMapping.getJdbcJavaType().wrap( newArray, options );
break;
default:
targetJdbcValues[jdbcIndex] = jdbcMapping.getJdbcJavaType().wrap( rawJdbcValue, options );
break;
}
break;
default:
targetJdbcValues[jdbcIndex] = jdbcMapping.getJdbcJavaType().wrap( rawJdbcValue, options );
break;
}
}
jdbcIndex++; jdbcIndex++;
} }
} }
if ( orderMapping != null ) {
StructHelper.orderJdbcValues( embeddableMappingType, inverseOrderMapping, targetJdbcValues, jdbcValues );
}
return jdbcIndex; return jdbcIndex;
} }
private void wrapRawJdbcValue(
JdbcMapping jdbcMapping,
Object[] jdbcValues,
int jdbcIndex,
WrapperOptions options) throws SQLException {
final Object rawJdbcValue = jdbcValues[jdbcIndex];
if ( rawJdbcValue == null ) {
return;
}
switch ( jdbcMapping.getJdbcType().getDefaultSqlTypeCode() ) {
case SqlTypes.TIME_WITH_TIMEZONE:
case SqlTypes.TIME_UTC:
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()
.wrap( transformRawJdbcValue( rawJdbcValue, options ), options );
break;
case SqlTypes.ARRAY:
final BasicType<?> elementType = ( (BasicPluralType<?, ?>) jdbcMapping ).getElementType();
final JdbcType elementJdbcType = elementType.getJdbcType();
final Object[] array;
final Object[] newArray;
switch ( elementJdbcType.getDefaultSqlTypeCode() ) {
case SqlTypes.TIME_WITH_TIMEZONE:
case SqlTypes.TIME_UTC:
case SqlTypes.TIMESTAMP_WITH_TIMEZONE:
case SqlTypes.TIMESTAMP_UTC:
// Only transform the raw jdbc value if it could be a TIMESTAMPTZ
array = (Object[]) ((java.sql.Array) rawJdbcValue ).getArray();
newArray = new Object[array.length];
for ( int j = 0; j < array.length; j++ ) {
newArray[j] = elementType.getJdbcJavaType().wrap(
transformRawJdbcValue( array[j], options ),
options
);
}
jdbcValues[jdbcIndex] = jdbcMapping.getJdbcJavaType().wrap( newArray, options );
break;
case SqlTypes.STRUCT:
case SqlTypes.JSON:
case SqlTypes.SQLXML:
array = (Object[]) ( (java.sql.Array) rawJdbcValue ).getArray();
newArray = new Object[array.length];
final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) elementJdbcType;
final EmbeddableMappingType subEmbeddableMappingType = aggregateJdbcType.getEmbeddableMappingType();
for ( int j = 0; j < array.length; j++ ) {
final StructAttributeValues subValues = StructHelper.getAttributeValues(
subEmbeddableMappingType,
aggregateJdbcType.extractJdbcValues(
array[j],
options
),
options
);
newArray[j] = instantiate( subEmbeddableMappingType, subValues, options.getSessionFactory() );
}
jdbcValues[jdbcIndex] = jdbcMapping.getJdbcJavaType().wrap( newArray, options );
break;
default:
jdbcValues[jdbcIndex] = jdbcMapping.getJdbcJavaType().wrap( rawJdbcValue, options );
break;
}
break;
default:
jdbcValues[jdbcIndex] = jdbcMapping.getJdbcJavaType().wrap( rawJdbcValue, options );
break;
}
}
protected Object transformRawJdbcValue(Object rawJdbcValue, WrapperOptions options) { protected Object transformRawJdbcValue(Object rawJdbcValue, WrapperOptions options) {
return rawJdbcValue; return rawJdbcValue;
} }

View File

@ -493,7 +493,7 @@ public class XmlHelper {
if ( array[i] == null ) { if ( array[i] == null ) {
continue; continue;
} }
final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, numberOfAttributes, i ); final ValuedModelPart attributeMapping = getEmbeddedPart( embeddableMappingType, i );
if ( attributeMapping instanceof SelectableMapping ) { if ( attributeMapping instanceof SelectableMapping ) {
final SelectableMapping selectable = (SelectableMapping) attributeMapping; final SelectableMapping selectable = (SelectableMapping) attributeMapping;
final String tagName = selectable.getSelectableName(); final String tagName = selectable.getSelectableName();

View File

@ -94,7 +94,10 @@ public class GeneratedValueBasicResultBuilder implements ResultBuilder {
return new BasicResult<>( return new BasicResult<>(
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
null, null,
modelPart.getJdbcMapping() modelPart.getJdbcMapping(),
navigablePath,
false,
false
); );
} }

View File

@ -13,6 +13,7 @@ import org.hibernate.sql.Template;
import static org.hibernate.type.SqlTypes.JSON_ARRAY; import static org.hibernate.type.SqlTypes.JSON_ARRAY;
import static org.hibernate.type.SqlTypes.STRUCT_ARRAY; import static org.hibernate.type.SqlTypes.STRUCT_ARRAY;
import static org.hibernate.type.SqlTypes.STRUCT_TABLE; import static org.hibernate.type.SqlTypes.STRUCT_TABLE;
import static org.hibernate.type.SqlTypes.XML_ARRAY;
/** /**
* An aggregate column is a column of type {@link org.hibernate.type.SqlTypes#STRUCT}, * An aggregate column is a column of type {@link org.hibernate.type.SqlTypes#STRUCT},
@ -79,7 +80,9 @@ public class AggregateColumn extends Column {
final AggregateColumn parentAggregateColumn = component.getParentAggregateColumn(); final AggregateColumn parentAggregateColumn = component.getParentAggregateColumn();
final String simpleAggregateName = aggregateColumn.getQuotedName( dialect ); final String simpleAggregateName = aggregateColumn.getQuotedName( dialect );
final String aggregateSelectableExpression; final String aggregateSelectableExpression;
if ( parentAggregateColumn == null ) { // If the aggregate column is an array, drop the parent read expression, because this is a NestedColumnReference
// and will require special rendering
if ( parentAggregateColumn == null || isArray( aggregateColumn ) ) {
aggregateSelectableExpression = getRootAggregateSelectableExpression( aggregateColumn, simpleAggregateName ); aggregateSelectableExpression = getRootAggregateSelectableExpression( aggregateColumn, simpleAggregateName );
} }
else { else {
@ -99,13 +102,23 @@ public class AggregateColumn extends Column {
} }
private static String getRootAggregateSelectableExpression(AggregateColumn aggregateColumn, String simpleAggregateName) { private static String getRootAggregateSelectableExpression(AggregateColumn aggregateColumn, String simpleAggregateName) {
if ( isArray( aggregateColumn ) ) {
return Template.TEMPLATE;
}
else {
return Template.TEMPLATE + "." + simpleAggregateName;
}
}
private static boolean isArray(AggregateColumn aggregateColumn) {
switch ( aggregateColumn.getTypeCode() ) { switch ( aggregateColumn.getTypeCode() ) {
case JSON_ARRAY: case JSON_ARRAY:
case XML_ARRAY:
case STRUCT_ARRAY: case STRUCT_ARRAY:
case STRUCT_TABLE: case STRUCT_TABLE:
return Template.TEMPLATE; return true;
default: default:
return Template.TEMPLATE + "." + simpleAggregateName; return false;
} }
} }

View File

@ -967,7 +967,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
return aggregateColumn == null return aggregateColumn == null
? jdbcTypeCode ? jdbcTypeCode
: getDialect().getAggregateSupport() : getDialect().getAggregateSupport()
.aggregateComponentSqlTypeCode( aggregateColumn.getSqlTypeCode(), jdbcTypeCode ); .aggregateComponentSqlTypeCode( aggregateColumn.getSqlTypeCode( getMetadata() ), jdbcTypeCode );
} }
@Override @Override

View File

@ -24,6 +24,7 @@ import org.hibernate.Remove;
import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.model.relational.Database; import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.relational.ExportableProducer; import org.hibernate.boot.model.relational.ExportableProducer;
import org.hibernate.boot.model.relational.QualifiedName;
import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.boot.model.source.internal.hbm.MappingDocument; import org.hibernate.boot.model.source.internal.hbm.MappingDocument;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
@ -93,7 +94,7 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
private AggregateColumn aggregateColumn; private AggregateColumn aggregateColumn;
private AggregateColumn parentAggregateColumn; private AggregateColumn parentAggregateColumn;
private String structName; private QualifiedName structName;
private String[] structColumnNames; private String[] structColumnNames;
private transient Class<?> componentClass; private transient Class<?> componentClass;
// lazily computed based on 'properties' field: invalidate by setting to null when properties are modified // lazily computed based on 'properties' field: invalidate by setting to null when properties are modified
@ -307,11 +308,11 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
this.parentAggregateColumn = parentAggregateColumn; this.parentAggregateColumn = parentAggregateColumn;
} }
public String getStructName() { public QualifiedName getStructName() {
return structName; return structName;
} }
public void setStructName(String structName) { public void setStructName(QualifiedName structName) {
this.structName = structName; this.structName = structName;
} }

View File

@ -87,6 +87,8 @@ public interface CollectionPart extends ValuedModelPart, Fetchable, JavaTypedExp
Nature getNature(); Nature getNature();
PluralAttributeMapping getCollectionAttribute();
@Override @Override
default String getPartName() { default String getPartName() {
return getNature().getName(); return getNature().getName();

View File

@ -12,9 +12,11 @@ import java.util.function.BiConsumer;
import org.hibernate.internal.util.IndexedConsumer; import org.hibernate.internal.util.IndexedConsumer;
import org.hibernate.internal.util.MutableInteger; import org.hibernate.internal.util.MutableInteger;
import org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.spi.EmbeddableInstantiator; import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy; import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.property.access.spi.Getter; import org.hibernate.property.access.spi.Getter;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
@ -156,9 +158,43 @@ public interface EmbeddableMappingType extends ManagedMappingType, SelectableMap
int count = 0; int count = 0;
for ( int i = 0; i < numberOfAttributeMappings; i++ ) { for ( int i = 0; i < numberOfAttributeMappings; i++ ) {
final AttributeMapping attributeMapping = getAttributeMapping( i ); final AttributeMapping attributeMapping = getAttributeMapping( i );
final MappingType mappedType = attributeMapping.getMappedType(); if ( attributeMapping instanceof DiscriminatedAssociationAttributeMapping ) {
if ( mappedType instanceof EmbeddableMappingType ) { final DiscriminatedAssociationAttributeMapping discriminatedAssociationAttributeMapping = (DiscriminatedAssociationAttributeMapping) attributeMapping;
final EmbeddableMappingType embeddableMappingType = (EmbeddableMappingType) mappedType; if ( count == columnIndex ) {
return discriminatedAssociationAttributeMapping.getDiscriminatorMapping();
}
count++;
if ( count == columnIndex ) {
return discriminatedAssociationAttributeMapping.getKeyPart();
}
count++;
}
else if ( attributeMapping instanceof ToOneAttributeMapping ) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping;
if ( toOneAttributeMapping.getSideNature() == ForeignKeyDescriptor.Nature.KEY ) {
final ValuedModelPart keyPart = toOneAttributeMapping.getForeignKeyDescriptor().getKeyPart();
if ( keyPart instanceof BasicValuedMapping ) {
if ( count == columnIndex ) {
return (SelectableMapping) keyPart;
}
count++;
}
else if ( keyPart instanceof EmbeddableValuedModelPart ) {
final EmbeddableMappingType mappingType = ( (EmbeddableValuedModelPart) keyPart ).getEmbeddableTypeDescriptor();
final SelectableMapping selectable = mappingType.getJdbcValueSelectable( columnIndex - count );
if ( selectable != null ) {
return selectable;
}
count += mappingType.getJdbcValueCount();
}
else {
throw new UnsupportedOperationException( "Unsupported foreign key part: " + keyPart );
}
}
}
else if ( attributeMapping instanceof EmbeddableValuedModelPart ) {
final EmbeddableValuedModelPart embeddableValuedModelPart = (EmbeddableValuedModelPart) attributeMapping;
final EmbeddableMappingType embeddableMappingType = embeddableValuedModelPart.getMappedType();
final SelectableMapping aggregateMapping = embeddableMappingType.getAggregateMapping(); final SelectableMapping aggregateMapping = embeddableMappingType.getAggregateMapping();
if ( aggregateMapping == null ) { if ( aggregateMapping == null ) {
final SelectableMapping subSelectable = embeddableMappingType.getJdbcValueSelectable( columnIndex - count ); final SelectableMapping subSelectable = embeddableMappingType.getJdbcValueSelectable( columnIndex - count );
@ -176,7 +212,10 @@ public interface EmbeddableMappingType extends ManagedMappingType, SelectableMap
} }
else { else {
if ( count == columnIndex ) { if ( count == columnIndex ) {
return (SelectableMapping) attributeMapping; if ( attributeMapping instanceof SelectableMapping ) {
return (SelectableMapping) attributeMapping;
}
assert attributeMapping.getJdbcTypeCount() == 0;
} }
count += attributeMapping.getJdbcTypeCount(); count += attributeMapping.getJdbcTypeCount();
} }

View File

@ -138,7 +138,7 @@ public interface PluralAttributeMapping
TableGroup parentTableGroup, TableGroup parentTableGroup,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
return new BasicResult( 0, null, getJavaType() ); return new BasicResult( 0, null, getJavaType(), null, null, false, false );
} }
String getSeparateCollectionTable(); String getSeparateCollectionTable();

View File

@ -13,7 +13,6 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.IndexedConsumer; import org.hibernate.internal.util.IndexedConsumer;
import org.hibernate.metamodel.mapping.DiscriminatorConverter; import org.hibernate.metamodel.mapping.DiscriminatorConverter;
import org.hibernate.metamodel.mapping.DiscriminatorType; import org.hibernate.metamodel.mapping.DiscriminatorType;
import org.hibernate.metamodel.mapping.DiscriminatorValueDetails;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
@ -124,7 +123,9 @@ public abstract class AbstractDiscriminatorMapping implements EntityDiscriminato
resultVariable, resultVariable,
discriminatorType.getJavaTypeDescriptor(), discriminatorType.getJavaTypeDescriptor(),
discriminatorType.getValueConverter(), discriminatorType.getValueConverter(),
navigablePath navigablePath,
false,
!sqlSelection.isVirtual()
); );
} }
@ -175,7 +176,10 @@ public abstract class AbstractDiscriminatorMapping implements EntityDiscriminato
this, this,
discriminatorType.getValueConverter(), discriminatorType.getValueConverter(),
fetchTiming, fetchTiming,
creationState true,
creationState,
false,
!sqlSelection.isVirtual()
); );
} }

View File

@ -120,6 +120,11 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa
return nature; return nature;
} }
@Override
public PluralAttributeMapping getCollectionAttribute() {
return collectionDescriptor.getAttributeMapping();
}
@Override @Override
public String getFetchableName() { public String getFetchableName() {
return nature.getName(); return nature.getName();

View File

@ -37,7 +37,6 @@ import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch;
import org.hibernate.sql.results.graph.FetchOptions; import org.hibernate.sql.results.graph.FetchOptions;
import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.basic.BasicFetch; import org.hibernate.sql.results.graph.basic.BasicFetch;
@ -59,6 +58,8 @@ public class AnyDiscriminatorPart implements DiscriminatorMapping, FetchOptions
private final String table; private final String table;
private final String column; private final String column;
private final String customReadExpression;
private final String customWriteExpression;
private final String columnDefinition; private final String columnDefinition;
private final Long length; private final Long length;
private final Integer precision; private final Integer precision;
@ -75,7 +76,7 @@ public class AnyDiscriminatorPart implements DiscriminatorMapping, FetchOptions
NavigableRole partRole, NavigableRole partRole,
DiscriminatedAssociationModelPart declaringType, DiscriminatedAssociationModelPart declaringType,
String table, String table,
String column, String column, String customReadExpression, String customWriteExpression,
String columnDefinition, String columnDefinition,
Long length, Long length,
Integer precision, Integer precision,
@ -90,6 +91,8 @@ public class AnyDiscriminatorPart implements DiscriminatorMapping, FetchOptions
this.declaringType = declaringType; this.declaringType = declaringType;
this.table = table; this.table = table;
this.column = column; this.column = column;
this.customReadExpression = customReadExpression;
this.customWriteExpression = customWriteExpression;
this.columnDefinition = columnDefinition; this.columnDefinition = columnDefinition;
this.length = length; this.length = length;
this.precision = precision; this.precision = precision;
@ -160,12 +163,12 @@ public class AnyDiscriminatorPart implements DiscriminatorMapping, FetchOptions
@Override @Override
public String getCustomReadExpression() { public String getCustomReadExpression() {
return null; return customReadExpression;
} }
@Override @Override
public String getCustomWriteExpression() { public String getCustomWriteExpression() {
return null; return customWriteExpression;
} }
@Override @Override
@ -326,7 +329,8 @@ public class AnyDiscriminatorPart implements DiscriminatorMapping, FetchOptions
fetchablePath, fetchablePath,
this, this,
fetchTiming, fetchTiming,
creationState creationState,
!sqlSelection.isVirtual()
); );
} }
@ -351,7 +355,9 @@ public class AnyDiscriminatorPart implements DiscriminatorMapping, FetchOptions
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
resultVariable, resultVariable,
jdbcMapping(), jdbcMapping(),
navigablePath navigablePath,
false,
!sqlSelection.isVirtual()
); );
} }

View File

@ -48,6 +48,8 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
private final String table; private final String table;
private final String column; private final String column;
private final DiscriminatedAssociationModelPart anyPart; private final DiscriminatedAssociationModelPart anyPart;
private final String customReadExpression;
private final String customWriteExpression;
private final String columnDefinition; private final String columnDefinition;
private final Long length; private final Long length;
private final Integer precision; private final Integer precision;
@ -63,6 +65,8 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
DiscriminatedAssociationModelPart anyPart, DiscriminatedAssociationModelPart anyPart,
String table, String table,
String column, String column,
String customReadExpression,
String customWriteExpression,
String columnDefinition, String columnDefinition,
Long length, Long length,
Integer precision, Integer precision,
@ -76,6 +80,8 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
this.table = table; this.table = table;
this.column = column; this.column = column;
this.anyPart = anyPart; this.anyPart = anyPart;
this.customReadExpression = customReadExpression;
this.customWriteExpression = customWriteExpression;
this.columnDefinition = columnDefinition; this.columnDefinition = columnDefinition;
this.length = length; this.length = length;
this.precision = precision; this.precision = precision;
@ -124,12 +130,12 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
@Override @Override
public String getCustomReadExpression() { public String getCustomReadExpression() {
return null; return customReadExpression;
} }
@Override @Override
public String getCustomWriteExpression() { public String getCustomWriteExpression() {
return null; return customWriteExpression;
} }
@Override @Override
@ -242,7 +248,8 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
fetchablePath, fetchablePath,
this, this,
fetchTiming, fetchTiming,
creationState creationState,
!sqlSelection.isVirtual()
); );
} }
@ -333,7 +340,9 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
resultVariable, resultVariable,
jdbcMapping, jdbcMapping,
navigablePath navigablePath,
false,
!sqlSelection.isVirtual()
); );
} }

View File

@ -337,7 +337,9 @@ public class BasicAttributeMapping
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
resultVariable, resultVariable,
jdbcMapping, jdbcMapping,
navigablePath navigablePath,
false,
!sqlSelection.isVirtual()
); );
} }
@ -391,10 +393,12 @@ public class BasicAttributeMapping
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final int valuesArrayPosition; final int valuesArrayPosition;
boolean coerceResultType = false; boolean coerceResultType = false;
final SqlSelection sqlSelection;
if ( fetchTiming == FetchTiming.DELAYED && isLazy ) { if ( fetchTiming == FetchTiming.DELAYED && isLazy ) {
// Lazy property. A valuesArrayPosition of -1 will lead to // Lazy property. A valuesArrayPosition of -1 will lead to
// returning a domain result assembler that returns LazyPropertyInitializer.UNFETCHED_PROPERTY // returning a domain result assembler that returns LazyPropertyInitializer.UNFETCHED_PROPERTY
valuesArrayPosition = -1; valuesArrayPosition = -1;
sqlSelection = null;
} }
else { else {
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
@ -404,7 +408,7 @@ public class BasicAttributeMapping
assert tableGroup != null; assert tableGroup != null;
final SqlSelection sqlSelection = resolveSqlSelection( sqlSelection = resolveSqlSelection(
fetchablePath, fetchablePath,
tableGroup, tableGroup,
fetchParent, fetchParent,
@ -422,9 +426,12 @@ public class BasicAttributeMapping
fetchParent, fetchParent,
fetchablePath, fetchablePath,
this, this,
getJdbcMapping().getValueConverter(),
fetchTiming, fetchTiming,
true,
creationState, creationState,
coerceResultType coerceResultType,
sqlSelection != null && !sqlSelection.isVirtual()
); );
} }

View File

@ -231,7 +231,9 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
resultVariable, resultVariable,
entityPersister.getIdentifierMapping().getSingleJdbcMapping(), entityPersister.getIdentifierMapping().getSingleJdbcMapping(),
navigablePath navigablePath,
false,
!sqlSelection.isVirtual()
); );
} }
@ -425,10 +427,13 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
fetchParent, fetchParent,
fetchablePath, fetchablePath,
this, this,
getJdbcMapping().getValueConverter(),
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
true,
creationState, creationState,
// if the expression type is different that the expected type coerce the value // if the expression type is different that the expected type coerce the value
selectionType != null && selectionType.getSingleJdbcMapping().getJdbcJavaType() != getJdbcMapping().getJdbcJavaType() selectionType != null && selectionType.getSingleJdbcMapping().getJdbcJavaType() != getJdbcMapping().getJdbcJavaType(),
!sqlSelection.isVirtual()
); );
} }

View File

@ -17,6 +17,7 @@ import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
@ -66,6 +67,11 @@ public class BasicValuedCollectionPart
return nature; return nature;
} }
@Override
public PluralAttributeMapping getCollectionAttribute() {
return collectionDescriptor.getAttributeMapping();
}
@Override @Override
public MappingType getPartMappingType() { public MappingType getPartMappingType() {
return selectableMapping.getJdbcMapping()::getJavaTypeDescriptor; return selectableMapping.getJdbcMapping()::getJavaTypeDescriptor;
@ -168,7 +174,9 @@ public class BasicValuedCollectionPart
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
resultVariable, resultVariable,
selectableMapping.getJdbcMapping(), selectableMapping.getJdbcMapping(),
navigablePath navigablePath,
false,
!sqlSelection.isVirtual()
); );
} }
@ -280,7 +288,8 @@ public class BasicValuedCollectionPart
fetchablePath, fetchablePath,
this, this,
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
creationState creationState,
!sqlSelection.isVirtual()
); );
} }

View File

@ -18,6 +18,7 @@ import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
@ -65,6 +66,11 @@ public class CollectionIdentifierDescriptorImpl implements CollectionIdentifierD
return Nature.ID; return Nature.ID;
} }
@Override
public PluralAttributeMapping getCollectionAttribute() {
return collectionDescriptor.getAttributeMapping();
}
@Override @Override
public String getContainingTableExpression() { public String getContainingTableExpression() {
return containingTableName; return containingTableName;
@ -278,7 +284,8 @@ public class CollectionIdentifierDescriptorImpl implements CollectionIdentifierD
fetchablePath, fetchablePath,
this, this,
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
creationState creationState,
!sqlSelection.isVirtual()
); );
} }
@ -305,7 +312,9 @@ public class CollectionIdentifierDescriptorImpl implements CollectionIdentifierD
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
null, null,
type, type,
collectionPath collectionPath,
false,
!sqlSelection.isVirtual()
); );
} }

View File

@ -80,6 +80,8 @@ public class DiscriminatedAssociationMapping implements MappingType, FetchOption
declaringModelPart, declaringModelPart,
tableName, tableName,
metaColumn.getText( dialect ), metaColumn.getText( dialect ),
metaColumn.getCustomReadExpression(),
metaColumn.getCustomWriteExpression(),
metaColumn.getSqlType(), metaColumn.getSqlType(),
metaColumn.getLength(), metaColumn.getLength(),
metaColumn.getPrecision(), metaColumn.getPrecision(),
@ -99,6 +101,8 @@ public class DiscriminatedAssociationMapping implements MappingType, FetchOption
declaringModelPart, declaringModelPart,
tableName, tableName,
keyColumn.getText( dialect ), keyColumn.getText( dialect ),
keyColumn.getCustomReadExpression(),
keyColumn.getCustomWriteExpression(),
keyColumn.getSqlType(), keyColumn.getSqlType(),
keyColumn.getLength(), keyColumn.getLength(),
keyColumn.getPrecision(), keyColumn.getPrecision(),

View File

@ -22,6 +22,7 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
@ -80,6 +81,11 @@ public class DiscriminatedCollectionPart implements DiscriminatedAssociationMode
return nature; return nature;
} }
@Override
public PluralAttributeMapping getCollectionAttribute() {
return collectionDescriptor.getAttributeMapping();
}
@Override @Override
public DiscriminatorMapping getDiscriminatorMapping() { public DiscriminatorMapping getDiscriminatorMapping() {
return associationMapping.getDiscriminatorPart(); return associationMapping.getDiscriminatorPart();

View File

@ -292,7 +292,7 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
case STRUCT_TABLE: case STRUCT_TABLE:
isArray = true; isArray = true;
aggregateSqlTypeCode = STRUCT; aggregateSqlTypeCode = STRUCT;
structTypeName = bootDescriptor.getStructName(); structTypeName = bootDescriptor.getStructName().render();
if ( structTypeName == null ) { if ( structTypeName == null ) {
final String arrayTypeName = aggregateColumn.getSqlType( creationContext.getMetadata() ); final String arrayTypeName = aggregateColumn.getSqlType( creationContext.getMetadata() );
if ( arrayTypeName.endsWith( " array" ) ) { if ( arrayTypeName.endsWith( " array" ) ) {
@ -322,7 +322,7 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
); );
// Register the resolved type under its struct name and java class name // Register the resolved type under its struct name and java class name
if ( bootDescriptor.getStructName() != null ) { if ( bootDescriptor.getStructName() != null ) {
basicTypeRegistry.register( basicType, bootDescriptor.getStructName() ); basicTypeRegistry.register( basicType, bootDescriptor.getStructName().render() );
basicTypeRegistry.register( basicType, getMappedJavaType().getJavaTypeClass().getName() ); basicTypeRegistry.register( basicType, getMappedJavaType().getJavaTypeClass().getName() );
} }
final BasicValue basicValue = (BasicValue) aggregateColumn.getValue(); final BasicValue basicValue = (BasicValue) aggregateColumn.getValue();

View File

@ -94,6 +94,11 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
this.sqlAliasStem = sqlAliasStem; this.sqlAliasStem = sqlAliasStem;
} }
@Override
public PluralAttributeMapping getCollectionAttribute() {
return collectionDescriptor.getAttributeMapping();
}
@Override @Override
public <T> DomainResult<T> createDomainResult( public <T> DomainResult<T> createDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,

View File

@ -108,7 +108,9 @@ public class EntityRowIdMappingImpl implements EntityRowIdMapping {
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
resultVariable, resultVariable,
rowIdType, rowIdType,
navigablePath navigablePath,
false,
!sqlSelection.isVirtual()
); );
} }

View File

@ -263,7 +263,8 @@ public class EntityVersionMappingImpl implements EntityVersionMapping, FetchOpti
fetchablePath, fetchablePath,
this, this,
fetchTiming, fetchTiming,
creationState creationState,
!sqlSelection.isVirtual()
); );
} }
@ -279,7 +280,9 @@ public class EntityVersionMappingImpl implements EntityVersionMapping, FetchOpti
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
resultVariable, resultVariable,
versionBasicType, versionBasicType,
navigablePath navigablePath,
false,
!sqlSelection.isVirtual()
); );
} }

View File

@ -384,8 +384,10 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
null, null,
selectableMapping.getJdbcMapping(), selectableMapping.getJdbcMapping(),
navigablePath,
// if the expression type is different that the expected type coerce the value // if the expression type is different that the expected type coerce the value
selectionType != null && selectionType.getSingleJdbcMapping().getJdbcJavaType() != javaType selectionType != null && selectionType.getSingleJdbcMapping().getJdbcJavaType() != javaType,
!sqlSelection.isVirtual()
); );
} }

View File

@ -163,7 +163,14 @@ public class SoftDeleteMappingImpl implements SoftDeleteMapping {
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, creationState ); final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, creationState );
return new BasicResult<>( sqlSelection.getValuesArrayPosition(), resultVariable, getJdbcMapping() ); return new BasicResult<>(
sqlSelection.getValuesArrayPosition(),
resultVariable,
getJdbcMapping(),
navigablePath,
false,
!sqlSelection.isVirtual()
);
} }
private SqlSelection resolveSqlSelection( private SqlSelection resolveSqlSelection(

View File

@ -18,6 +18,7 @@ import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.basic.BasicResult; import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.graph.basic.BasicResultAssembler;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
@ -69,7 +70,10 @@ public class ScalarDomainResultBuilder<T> implements ResultBuilder {
return new BasicResult<>( return new BasicResult<>(
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
null, null,
( (BasicType<?>) sqlSelection.getExpressionType() ) (BasicType<?>) sqlSelection.getExpressionType(),
null,
false,
false
); );
} }

View File

@ -218,7 +218,9 @@ public class AnonymousTupleBasicValuedModelPart implements OwnedValuedModelPart,
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
resultVariable, resultVariable,
jdbcMapping, jdbcMapping,
navigablePath navigablePath,
false,
!sqlSelection.isVirtual()
); );
} }
@ -270,7 +272,8 @@ public class AnonymousTupleBasicValuedModelPart implements OwnedValuedModelPart,
fetchablePath, fetchablePath,
this, this,
fetchTiming, fetchTiming,
creationState creationState,
!sqlSelection.isVirtual()
); );
} }

View File

@ -31,6 +31,7 @@ import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.basic.BasicResult; import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.graph.basic.BasicResultAssembler;
import org.hibernate.sql.results.graph.entity.EntityResult; import org.hibernate.sql.results.graph.entity.EntityResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
@ -328,7 +329,10 @@ public class ResultSetMappingImpl implements ResultSetMapping {
return new BasicResult( return new BasicResult(
valuesArrayPosition, valuesArrayPosition,
name, name,
jdbcMapping jdbcMapping,
null,
false,
false
); );
} }

View File

@ -13,9 +13,7 @@ import org.hibernate.query.results.ResultsHelper;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.query.results.DomainResultCreationStateImpl; import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.ResultBuilder; import org.hibernate.query.results.ResultBuilder;
import org.hibernate.query.results.ResultSetMappingSqlSelection;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy; import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
@ -24,7 +22,6 @@ import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import static org.hibernate.query.results.ResultsHelper.impl; import static org.hibernate.query.results.ResultsHelper.impl;
import static org.hibernate.query.results.ResultsHelper.jdbcPositionToValuesArrayPosition;
/** /**
* CompleteResultBuilder for basic-valued ModelParts * CompleteResultBuilder for basic-valued ModelParts
@ -93,7 +90,10 @@ public class CompleteResultBuilderBasicModelPart
return new BasicResult<>( return new BasicResult<>(
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
columnAlias, columnAlias,
modelPart.getJdbcMapping() modelPart.getJdbcMapping(),
navigablePath,
false,
!sqlSelection.isVirtual()
); );
} }

View File

@ -111,7 +111,10 @@ public class CompleteResultBuilderBasicValuedConverted<O,R> implements CompleteR
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
columnName, columnName,
valueConverter.getDomainJavaType(), valueConverter.getDomainJavaType(),
valueConverter valueConverter,
null,
false,
false
); );
} }

View File

@ -112,7 +112,10 @@ public class CompleteResultBuilderBasicValuedStandard implements CompleteResultB
return new BasicResult<>( return new BasicResult<>(
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
columnName, columnName,
jdbcMapping jdbcMapping,
null,
false,
false
); );
} }

View File

@ -19,6 +19,7 @@ import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.basic.BasicFetch; import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.basic.BasicResultAssembler;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
/** /**
@ -69,7 +70,9 @@ public class DelayedFetchBuilderBasicPart
null, null,
FetchTiming.DELAYED, FetchTiming.DELAYED,
isEnhancedForLazyLoading, isEnhancedForLazyLoading,
domainResultCreationState domainResultCreationState,
false,
false
); );
} }

View File

@ -14,7 +14,6 @@ import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
import org.hibernate.query.NativeQuery; import org.hibernate.query.NativeQuery;
import org.hibernate.query.results.DomainResultCreationStateImpl; import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.ResultSetMappingSqlSelection; import org.hibernate.query.results.ResultSetMappingSqlSelection;
import org.hibernate.query.results.ResultsHelper;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
@ -103,7 +102,10 @@ public class DynamicResultBuilderAttribute implements DynamicResultBuilder, Nati
return new BasicResult<>( return new BasicResult<>(
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
columnAlias, columnAlias,
attributeMapping.getJdbcMapping() attributeMapping.getJdbcMapping(),
null,
false,
!sqlSelection.isVirtual()
); );
} }

View File

@ -141,7 +141,10 @@ public class DynamicResultBuilderBasicConverted<O,R> implements DynamicResultBui
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
columnAlias, columnAlias,
basicValueConverter.getDomainJavaType(), basicValueConverter.getDomainJavaType(),
basicValueConverter basicValueConverter,
null,
false,
false
); );
} }

View File

@ -180,7 +180,15 @@ public class DynamicResultBuilderBasicStandard implements DynamicResultBuilderBa
// StandardRowReader expects there to be a JavaType as part of the ResultAssembler. // StandardRowReader expects there to be a JavaType as part of the ResultAssembler.
assert javaType != null; assert javaType != null;
return new BasicResult( sqlSelection.getValuesArrayPosition(), resultAlias, javaType, converter ); return new BasicResult(
sqlSelection.getValuesArrayPosition(),
resultAlias,
javaType,
converter,
null,
false,
false
);
} }
@Override @Override

View File

@ -17,7 +17,6 @@ import org.hibernate.query.results.BasicValuedFetchBuilder;
import org.hibernate.query.results.DomainResultCreationStateImpl; import org.hibernate.query.results.DomainResultCreationStateImpl;
import org.hibernate.query.results.FetchBuilder; import org.hibernate.query.results.FetchBuilder;
import org.hibernate.query.results.ResultsHelper; import org.hibernate.query.results.ResultsHelper;
import org.hibernate.query.results.ResultSetMappingSqlSelection;
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy; import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
@ -28,7 +27,6 @@ import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
import static org.hibernate.query.results.ResultsHelper.impl; import static org.hibernate.query.results.ResultsHelper.impl;
import static org.hibernate.query.results.ResultsHelper.jdbcPositionToValuesArrayPosition;
import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey; import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey;
/** /**
@ -118,7 +116,8 @@ public class ImplicitFetchBuilderBasic implements ImplicitFetchBuilder, BasicVal
fetchPath, fetchPath,
fetchable, fetchable,
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
domainResultCreationState domainResultCreationState,
!sqlSelection.isVirtual()
); );
} }

View File

@ -174,7 +174,10 @@ public class SelfRenderingFunctionSqlAstExpression
.getValuesArrayPosition(), .getValuesArrayPosition(),
resultVariable, resultVariable,
type == null ? null : type.getExpressibleJavaType(), type == null ? null : type.getExpressibleJavaType(),
converter converter,
null,
false,
false
); );
} }

View File

@ -15,6 +15,7 @@ import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.basic.BasicResult; import org.hibernate.sql.results.graph.basic.BasicResult;
import org.hibernate.sql.results.graph.basic.BasicResultAssembler;
/** /**
* A wrapper around a basic {@link Expression} that produces a {@link BasicResult} as domain result. * A wrapper around a basic {@link Expression} that produces a {@link BasicResult} as domain result.
@ -33,7 +34,9 @@ public class ExpressionDomainResultProducer implements DomainResultProducer<Obje
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
resultVariable, resultVariable,
expression.getExpressionType().getSingleJdbcMapping(), expression.getExpressionType().getSingleJdbcMapping(),
null null,
false,
false
); );
} }

View File

@ -78,6 +78,7 @@ import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation;
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect; import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.results.graph.basic.BasicFetch; import org.hibernate.sql.results.graph.basic.BasicFetch;
import org.hibernate.sql.results.graph.basic.BasicResultAssembler;
import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.sql.results.internal.SqlSelectionImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer; import org.hibernate.sql.results.spi.ListResultsConsumer;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
@ -402,7 +403,8 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
null, null,
identifierMapping, identifierMapping,
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
null null,
false
) )
) )
); );

View File

@ -105,13 +105,16 @@ public class SqlAstQueryPartProcessingStateImpl
else { else {
throw new IllegalArgumentException( "Illegal expression passed for nested fetching: " + expression ); throw new IllegalArgumentException( "Illegal expression passed for nested fetching: " + expression );
} }
return expression.createSqlSelection( final int selectableIndex = nestingFetchParent.getReferencedMappingType().getSelectableIndex( selectableName );
-1, if ( selectableIndex != -1 ) {
nestingFetchParent.getReferencedMappingType().getSelectableIndex( selectableName ), return expression.createSqlSelection(
javaType, -1,
true, selectableIndex,
typeConfiguration javaType,
); true,
typeConfiguration
);
}
} }
final Map<Expression, Object> selectionMap; final Map<Expression, Object> selectionMap;
if ( deduplicateSelectionItems ) { if ( deduplicateSelectionItems ) {

View File

@ -108,7 +108,10 @@ public class SqmParameterInterpretation implements Expression, DomainResultProdu
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
resultVariable, resultVariable,
jdbcMapping.getMappedJavaType(), jdbcMapping.getMappedJavaType(),
converter converter,
null,
false,
false
); );
} }

View File

@ -38,26 +38,6 @@ public class BasicFetch<T> implements Fetch, BasicResultGraphNode<T> {
private final FetchTiming fetchTiming; private final FetchTiming fetchTiming;
public BasicFetch(
int valuesArrayPosition,
FetchParent fetchParent,
NavigablePath fetchablePath,
BasicValuedModelPart valuedMapping,
FetchTiming fetchTiming,
DomainResultCreationState creationState) {
//noinspection unchecked
this(
valuesArrayPosition,
fetchParent,
fetchablePath,
valuedMapping,
(BasicValueConverter<T, ?>) valuedMapping.getJdbcMapping().getValueConverter(),
fetchTiming,
true,
creationState
);
}
public BasicFetch( public BasicFetch(
int valuesArrayPosition, int valuesArrayPosition,
FetchParent fetchParent, FetchParent fetchParent,
@ -65,7 +45,7 @@ public class BasicFetch<T> implements Fetch, BasicResultGraphNode<T> {
BasicValuedModelPart valuedMapping, BasicValuedModelPart valuedMapping,
FetchTiming fetchTiming, FetchTiming fetchTiming,
DomainResultCreationState creationState, DomainResultCreationState creationState,
boolean coerceResultType) { boolean unwrapRowProcessingState) {
//noinspection unchecked //noinspection unchecked
this( this(
valuesArrayPosition, valuesArrayPosition,
@ -76,49 +56,8 @@ public class BasicFetch<T> implements Fetch, BasicResultGraphNode<T> {
fetchTiming, fetchTiming,
true, true,
creationState, creationState,
coerceResultType false,
); unwrapRowProcessingState
}
public BasicFetch(
int valuesArrayPosition,
FetchParent fetchParent,
NavigablePath fetchablePath,
BasicValuedModelPart valuedMapping,
BasicValueConverter<T, ?> valueConverter,
FetchTiming fetchTiming,
DomainResultCreationState creationState) {
this(
valuesArrayPosition,
fetchParent,
fetchablePath,
valuedMapping,
valueConverter,
fetchTiming,
true,
creationState
);
}
public BasicFetch(
int valuesArrayPosition,
FetchParent fetchParent,
NavigablePath fetchablePath,
BasicValuedModelPart valuedMapping,
BasicValueConverter<T, ?> valueConverter,
FetchTiming fetchTiming,
boolean canBasicPartFetchBeDelayed,
DomainResultCreationState creationState) {
this(
valuesArrayPosition,
fetchParent,
fetchablePath,
valuedMapping,
valueConverter,
fetchTiming,
canBasicPartFetchBeDelayed,
creationState,
false
); );
} }
@ -131,7 +70,8 @@ public class BasicFetch<T> implements Fetch, BasicResultGraphNode<T> {
FetchTiming fetchTiming, FetchTiming fetchTiming,
boolean canBasicPartFetchBeDelayed, boolean canBasicPartFetchBeDelayed,
DomainResultCreationState creationState, DomainResultCreationState creationState,
boolean coerceResultType) { boolean coerceResultType,
boolean unwrapRowProcessingState) {
this.navigablePath = fetchablePath; this.navigablePath = fetchablePath;
this.fetchParent = fetchParent; this.fetchParent = fetchParent;
@ -149,10 +89,10 @@ public class BasicFetch<T> implements Fetch, BasicResultGraphNode<T> {
} }
else { else {
if (coerceResultType) { if (coerceResultType) {
this.assembler = new CoercingResultAssembler<>( valuesArrayPosition, javaType, valueConverter ); this.assembler = new CoercingResultAssembler<>( valuesArrayPosition, javaType, valueConverter, unwrapRowProcessingState );
} }
else { else {
this.assembler = new BasicResultAssembler<>( valuesArrayPosition, javaType, valueConverter ); this.assembler = new BasicResultAssembler<>( valuesArrayPosition, javaType, valueConverter, unwrapRowProcessingState );
} }
} }
} }

View File

@ -40,7 +40,9 @@ public class BasicResult<T> implements DomainResult<T>, BasicResultGraphNode<T>
jdbcValuesArrayPosition, jdbcValuesArrayPosition,
resultVariable, resultVariable,
jdbcMapping, jdbcMapping,
null null,
false,
false
); );
} }
@ -48,81 +50,38 @@ public class BasicResult<T> implements DomainResult<T>, BasicResultGraphNode<T>
int jdbcValuesArrayPosition, int jdbcValuesArrayPosition,
String resultVariable, String resultVariable,
JdbcMapping jdbcMapping, JdbcMapping jdbcMapping,
boolean coerceResultType) { NavigablePath navigablePath,
boolean coerceResultType,
boolean unwrapRowProcessingState) {
//noinspection unchecked //noinspection unchecked
this( this(
jdbcValuesArrayPosition, jdbcValuesArrayPosition,
resultVariable, resultVariable,
jdbcMapping.getJavaTypeDescriptor(), jdbcMapping.getJavaTypeDescriptor(),
jdbcMapping.getValueConverter(), jdbcMapping.getValueConverter(),
null, navigablePath,
coerceResultType coerceResultType,
unwrapRowProcessingState
); );
} }
public BasicResult(
int jdbcValuesArrayPosition,
String resultVariable,
JdbcMapping jdbcMapping,
NavigablePath navigablePath) {
//noinspection unchecked
this(
jdbcValuesArrayPosition,
resultVariable,
(JavaType<T>) jdbcMapping.getJavaTypeDescriptor(),
(BasicValueConverter<T, ?>) jdbcMapping.getValueConverter(),
navigablePath
);
}
public BasicResult(
int jdbcValuesArrayPosition,
String resultVariable,
JavaType<T> javaType) {
this( jdbcValuesArrayPosition, resultVariable, javaType, null, null );
}
public BasicResult(
int jdbcValuesArrayPosition,
String resultVariable,
JavaType<T> javaType,
NavigablePath navigablePath) {
this( jdbcValuesArrayPosition, resultVariable, javaType, null, navigablePath );
}
public BasicResult(
int valuesArrayPosition,
String resultVariable,
JavaType<T> javaType,
BasicValueConverter<T,?> valueConverter) {
this( valuesArrayPosition, resultVariable, javaType, valueConverter, null );
}
public BasicResult(
int valuesArrayPosition,
String resultVariable,
JavaType<T> javaType,
BasicValueConverter<T,?> valueConverter,
NavigablePath navigablePath) {
this( valuesArrayPosition, resultVariable, javaType, valueConverter, navigablePath, false );
}
public BasicResult( public BasicResult(
int valuesArrayPosition, int valuesArrayPosition,
String resultVariable, String resultVariable,
JavaType<T> javaType, JavaType<T> javaType,
BasicValueConverter<T,?> valueConverter, BasicValueConverter<T,?> valueConverter,
NavigablePath navigablePath, NavigablePath navigablePath,
boolean coerceResultType) { boolean coerceResultType,
boolean unwrapRowProcessingState) {
this.resultVariable = resultVariable; this.resultVariable = resultVariable;
this.javaType = javaType; this.javaType = javaType;
this.navigablePath = navigablePath; this.navigablePath = navigablePath;
if ( coerceResultType ) { if ( coerceResultType ) {
this.assembler = new CoercingResultAssembler<>( valuesArrayPosition, javaType, valueConverter ); this.assembler = new CoercingResultAssembler<>( valuesArrayPosition, javaType, valueConverter, unwrapRowProcessingState );
} }
else { else {
this.assembler = new BasicResultAssembler<>( valuesArrayPosition, javaType, valueConverter ); this.assembler = new BasicResultAssembler<>( valuesArrayPosition, javaType, valueConverter, unwrapRowProcessingState );
} }
} }

View File

@ -29,26 +29,30 @@ public class BasicResultAssembler<J> implements DomainResultAssembler<J> {
protected final int valuesArrayPosition; protected final int valuesArrayPosition;
protected final JavaType<J> assembledJavaType; protected final JavaType<J> assembledJavaType;
private final BasicValueConverter<J,?> valueConverter; private final BasicValueConverter<J,?> valueConverter;
private final boolean unwrapRowProcessingState;
public BasicResultAssembler( public BasicResultAssembler(int valuesArrayPosition, JavaType<J> assembledJavaType) {
int valuesArrayPosition, this( valuesArrayPosition, assembledJavaType, null, false );
JavaType<J> assembledJavaType) {
this( valuesArrayPosition, assembledJavaType, null );
} }
public BasicResultAssembler( public BasicResultAssembler(
int valuesArrayPosition, int valuesArrayPosition,
JavaType<J> assembledJavaType, JavaType<J> assembledJavaType,
BasicValueConverter<J, ?> valueConverter) { BasicValueConverter<J, ?> valueConverter,
boolean unwrapRowProcessingState) {
this.valuesArrayPosition = valuesArrayPosition; this.valuesArrayPosition = valuesArrayPosition;
this.assembledJavaType = assembledJavaType; this.assembledJavaType = assembledJavaType;
this.valueConverter = valueConverter; this.valueConverter = valueConverter;
this.unwrapRowProcessingState = unwrapRowProcessingState;
} }
/** /**
* Access to the raw value (unconverted, if a converter applied) * Access to the raw value (unconverted, if a converter applied)
*/ */
public Object extractRawValue(RowProcessingState rowProcessingState) { public Object extractRawValue(RowProcessingState rowProcessingState) {
if ( unwrapRowProcessingState ) {
rowProcessingState = rowProcessingState.unwrap();
}
return rowProcessingState.getJdbcValue( valuesArrayPosition ); return rowProcessingState.getJdbcValue( valuesArrayPosition );
} }

View File

@ -21,8 +21,9 @@ public class CoercingResultAssembler<J> extends BasicResultAssembler<J> {
public CoercingResultAssembler( public CoercingResultAssembler(
int valuesArrayPosition, int valuesArrayPosition,
JavaType<J> assembledJavaType, JavaType<J> assembledJavaType,
BasicValueConverter<J, ?> valueConverter) { BasicValueConverter<J, ?> valueConverter,
super( valuesArrayPosition, assembledJavaType, valueConverter ); boolean nestedInAggregateComponent) {
super( valuesArrayPosition, assembledJavaType, valueConverter, nestedInAggregateComponent );
} }
/** /**
@ -31,7 +32,7 @@ public class CoercingResultAssembler<J> extends BasicResultAssembler<J> {
@Override @Override
public Object extractRawValue(RowProcessingState rowProcessingState) { public Object extractRawValue(RowProcessingState rowProcessingState) {
return assembledJavaType.coerce( return assembledJavaType.coerce(
rowProcessingState.getJdbcValue( valuesArrayPosition ), super.extractRawValue( rowProcessingState ),
rowProcessingState.getSession() rowProcessingState.getSession()
); );
} }

View File

@ -74,7 +74,8 @@ public class EmbeddableExpressionResultImpl<T> extends AbstractFetchParent imple
resolveNavigablePath( attribute ), resolveNavigablePath( attribute ),
attribute, attribute,
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
creationState creationState,
!sqlSelection.isVirtual()
) )
); );
} }

View File

@ -49,6 +49,11 @@ public class NestedRowProcessingState extends BaseExecutionContext implements Ro
return jdbcValue == null ? null : jdbcValue[position]; return jdbcValue == null ? null : jdbcValue[position];
} }
@Override
public RowProcessingState unwrap() {
return processingState;
}
// -- delegate the rest // -- delegate the rest
@Override @Override

View File

@ -68,4 +68,12 @@ public interface RowProcessingState extends ExecutionContext {
finishRowProcessing(); finishRowProcessing();
} }
/**
* If this is a row processing state for aggregate components,
* this will return the underlying row processing state.
*/
default RowProcessingState unwrap() {
return this;
}
} }

View File

@ -76,7 +76,7 @@ public class BooleanJavaType extends AbstractClassJavaType<Boolean> implements
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
if ( Boolean.class.isAssignableFrom( type ) ) { if ( Boolean.class.isAssignableFrom( type ) || type == Object.class ) {
return (X) value; return (X) value;
} }
if ( Byte.class.isAssignableFrom( type ) ) { if ( Byte.class.isAssignableFrom( type ) ) {

View File

@ -47,7 +47,7 @@ public class ByteJavaType extends AbstractClassJavaType<Byte>
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
if ( Byte.class.isAssignableFrom( type ) ) { if ( Byte.class.isAssignableFrom( type ) || type == Object.class ) {
return (X) value; return (X) value;
} }
if ( Short.class.isAssignableFrom( type ) ) { if ( Short.class.isAssignableFrom( type ) ) {

View File

@ -44,7 +44,7 @@ public class CharacterJavaType extends AbstractClassJavaType<Character> implemen
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
if ( Character.class.isAssignableFrom( type ) ) { if ( Character.class.isAssignableFrom( type ) || type == Object.class ) {
return (X) value; return (X) value;
} }
if ( String.class.isAssignableFrom( type ) ) { if ( String.class.isAssignableFrom( type ) ) {

View File

@ -51,7 +51,7 @@ public class DoubleJavaType extends AbstractClassJavaType<Double> implements
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
if ( Double.class.isAssignableFrom( type ) ) { if ( Double.class.isAssignableFrom( type ) || type == Object.class ) {
return (X) value; return (X) value;
} }
if ( Float.class.isAssignableFrom( type ) ) { if ( Float.class.isAssignableFrom( type ) ) {

View File

@ -50,7 +50,7 @@ public class FloatJavaType extends AbstractClassJavaType<Float> implements Primi
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
if ( Float.class.isAssignableFrom( type ) ) { if ( Float.class.isAssignableFrom( type ) || type == Object.class ) {
return (X) value; return (X) value;
} }
if ( Double.class.isAssignableFrom( type ) ) { if ( Double.class.isAssignableFrom( type ) ) {

View File

@ -47,7 +47,7 @@ public class IntegerJavaType extends AbstractClassJavaType<Integer>
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
if ( Integer.class.isAssignableFrom( type ) ) { if ( Integer.class.isAssignableFrom( type ) || type == Object.class ) {
return (X) value; return (X) value;
} }
if ( Byte.class.isAssignableFrom( type ) ) { if ( Byte.class.isAssignableFrom( type ) ) {

View File

@ -47,7 +47,7 @@ public class LongJavaType extends AbstractClassJavaType<Long>
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
if ( Long.class.isAssignableFrom( type ) ) { if ( Long.class.isAssignableFrom( type ) || type == Object.class ) {
return (X) value; return (X) value;
} }
if ( Byte.class.isAssignableFrom( type ) ) { if ( Byte.class.isAssignableFrom( type ) ) {

View File

@ -57,7 +57,7 @@ public class ShortJavaType extends AbstractClassJavaType<Short>
if ( value == null ) { if ( value == null ) {
return null; return null;
} }
if ( Short.class.isAssignableFrom( type ) ) { if ( Short.class.isAssignableFrom( type ) || type == Object.class ) {
return (X) value; return (X) value;
} }
if ( Byte.class.isAssignableFrom( type ) ) { if ( Byte.class.isAssignableFrom( type ) ) {

View File

@ -20,7 +20,6 @@ import org.hibernate.dialect.StructHelper;
import org.hibernate.engine.jdbc.Size; import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.type.BasicPluralType; import org.hibernate.type.BasicPluralType;
import org.hibernate.type.descriptor.ValueBinder; import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.ValueExtractor;
@ -107,23 +106,58 @@ public class ArrayJdbcType implements JdbcType {
@Override @Override
public Class<?> getPreferredJavaTypeClass(WrapperOptions options) { public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
return java.sql.Array.class; return Object[].class;
} }
protected Object[] getArray(BasicBinder<?> binder, Object value, WrapperOptions options) throws SQLException { protected String getElementTypeName(JavaType<?> javaType, SharedSessionContractImplementor session) {
// TODO: ideally, we would have the actual size or the actual type/column accessible
// this is something that we would need for supporting composite types anyway
if ( elementJdbcType instanceof StructJdbcType ) {
return ( (StructJdbcType) elementJdbcType ).getStructTypeName();
}
final JavaType<?> elementJavaType;
if ( javaType instanceof ByteArrayJavaType ) {
// Special handling needed for Byte[], because that would conflict with the VARBINARY mapping
elementJavaType = ByteJavaType.INSTANCE;
}
else {
elementJavaType = ( (BasicPluralJavaType<?>) javaType ).getElementJavaType();
}
final Size size = session.getJdbcServices()
.getDialect()
.getSizeStrategy()
.resolveSize( elementJdbcType, elementJavaType, null, null, null );
final DdlTypeRegistry ddlTypeRegistry = session.getTypeConfiguration().getDdlTypeRegistry();
final String typeName = ddlTypeRegistry.getDescriptor( elementJdbcType.getDdlTypeCode() )
.getTypeName( size, new BasicTypeImpl<>( elementJavaType, elementJdbcType), ddlTypeRegistry );
int cutIndex = typeName.indexOf( '(' );
if ( cutIndex > 0 ) {
// getTypeName for this case required length, etc, parameters.
// Cut them out and use database defaults.
return typeName.substring( 0, cutIndex );
}
else {
return typeName;
}
}
protected Object[] getArray(
BasicBinder<?> binder,
ValueBinder<Object> elementBinder,
Object value,
WrapperOptions options) throws SQLException {
final JdbcType elementJdbcType = ( (ArrayJdbcType) binder.getJdbcType() ).getElementJdbcType(); final JdbcType elementJdbcType = ( (ArrayJdbcType) binder.getJdbcType() ).getElementJdbcType();
//noinspection unchecked //noinspection unchecked
final JavaType<Object> javaType = (JavaType<Object>) binder.getJavaType(); final JavaType<Object> javaType = (JavaType<Object>) binder.getJavaType();
if ( elementJdbcType instanceof AggregateJdbcType ) { if ( elementJdbcType instanceof AggregateJdbcType ) {
final AggregateJdbcType aggregateJdbcType = (AggregateJdbcType) elementJdbcType; final Object[] domainObjects = javaType.unwrap(
final Object[] domainObjects = ( javaType ).unwrap(
value, value,
Object[].class, Object[].class,
options options
); );
final Object[] objects = new Object[domainObjects.length]; final Object[] objects = new Object[domainObjects.length];
for ( int i = 0; i < domainObjects.length; i++ ) { for ( int i = 0; i < domainObjects.length; i++ ) {
objects[i] = aggregateJdbcType.createJdbcValue( domainObjects[i], options ); objects[i] = elementBinder.getBindValue( domainObjects[i], options );
} }
return objects; return objects;
} }
@ -174,6 +208,8 @@ public class ArrayJdbcType implements JdbcType {
@Override @Override
public <X> ValueBinder<X> getBinder(final JavaType<X> javaTypeDescriptor) { public <X> ValueBinder<X> getBinder(final JavaType<X> javaTypeDescriptor) {
//noinspection unchecked
final ValueBinder<Object> elementBinder = elementJdbcType.getBinder( ( (BasicPluralJavaType<Object>) javaTypeDescriptor ).getElementJavaType() );
return new BasicBinder<>( javaTypeDescriptor, this ) { return new BasicBinder<>( javaTypeDescriptor, this ) {
@Override @Override
@ -193,48 +229,19 @@ public class ArrayJdbcType implements JdbcType {
} }
} }
private java.sql.Array getArray(X value, WrapperOptions options) throws SQLException { @Override
final JdbcType elementJdbcType = ( (ArrayJdbcType) getJdbcType() ).getElementJdbcType(); public Object getBindValue(X value, WrapperOptions options) throws SQLException {
final Object[] objects = ArrayJdbcType.this.getArray( this, value, options ); return ( (ArrayJdbcType) getJdbcType() ).getArray( this, elementBinder, value, options );
final SharedSessionContractImplementor session = options.getSession();
final String typeName = getElementTypeName( elementJdbcType, session );
return session.getJdbcCoordinator().getLogicalConnection().getPhysicalConnection()
.createArrayOf( typeName, objects );
} }
private String getElementTypeName(JdbcType elementJdbcType, SharedSessionContractImplementor session) { private java.sql.Array getArray(X value, WrapperOptions options) throws SQLException {
// TODO: ideally, we would have the actual size or the actual type/column accessible final ArrayJdbcType arrayJdbcType = (ArrayJdbcType) getJdbcType();
// this is something that we would need for supporting composite types anyway final Object[] objects = arrayJdbcType.getArray( this, elementBinder, value, options );
if ( elementJdbcType instanceof StructJdbcType ) {
return ( (StructJdbcType) elementJdbcType ).getStructTypeName(); final SharedSessionContractImplementor session = options.getSession();
} final String typeName = arrayJdbcType.getElementTypeName( getJavaType(), session );
final JavaType<X> elementJavaType; return session.getJdbcCoordinator().getLogicalConnection().getPhysicalConnection()
if ( getJavaType() instanceof ByteArrayJavaType ) { .createArrayOf( typeName, objects );
// Special handling needed for Byte[], because that would conflict with the VARBINARY mapping
//noinspection unchecked
elementJavaType = (JavaType<X>) ByteJavaType.INSTANCE;
}
else {
//noinspection unchecked
elementJavaType = ( (BasicPluralJavaType<X>) getJavaType() ).getElementJavaType();
}
final Size size = session.getJdbcServices()
.getDialect()
.getSizeStrategy()
.resolveSize( elementJdbcType, elementJavaType, null, null, null );
final DdlTypeRegistry ddlTypeRegistry = session.getTypeConfiguration().getDdlTypeRegistry();
final String typeName = ddlTypeRegistry.getDescriptor( elementJdbcType.getDdlTypeCode() )
.getTypeName( size, new BasicTypeImpl<>( elementJavaType, elementJdbcType), ddlTypeRegistry );
int cutIndex = typeName.indexOf( '(' );
if ( cutIndex > 0 ) {
// getTypeName for this case required length, etc, parameters.
// Cut them out and use database defaults.
return typeName.substring( 0, cutIndex );
}
else {
return typeName;
}
} }
}; };
} }
@ -266,7 +273,7 @@ public class ArrayJdbcType implements JdbcType {
@Override @Override
public String toString() { public String toString() {
return "ArrayTypeDescriptor"; return "ArrayTypeDescriptor(" + getElementJdbcType().toString() + ")";
} }
/** /**

View File

@ -20,18 +20,21 @@ import jakarta.persistence.Entity;
import jakarta.persistence.FetchType; import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue; import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id; import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne; import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany; import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
@JiraKey( "HHH-15831" )
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsStructAggregate.class) @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsStructAggregate.class)
public class StructComponentCollectionErrorTest { @RequiresDialectFeature(feature = DialectFeatureChecks.SupportsStructuralArrays.class)
public class StructComponentAssociationErrorTest {
@Test @Test
@JiraKey( "HHH-15831" ) public void testOneToOneMappedBy() {
public void testError1() {
final StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistry(); final StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistry();
try { try {
new MetadataSources( ssr ) new MetadataSources( ssr )
@ -41,7 +44,7 @@ public class StructComponentCollectionErrorTest {
Assertions.fail( "Expected a failure" ); Assertions.fail( "Expected a failure" );
} }
catch (MappingException ex) { catch (MappingException ex) {
assertThat( ex.getMessage(), containsString( "author.favoriteBook" ) ); assertThat( ex.getMessage(), containsString( "authors.favoriteBook" ) );
} }
finally { finally {
StandardServiceRegistryBuilder.destroy( ssr ); StandardServiceRegistryBuilder.destroy( ssr );
@ -56,19 +59,21 @@ public class StructComponentCollectionErrorTest {
@GeneratedValue @GeneratedValue
private Long id; private Long id;
private String title; private String title;
private Person1 author; private Person1[] authors;
@OneToOne(fetch = FetchType.LAZY)
private Book1 favoredBook;
} }
@Embeddable @Embeddable
@Struct(name = "person_type") @Struct(name = "person_type")
public static class Person1 { public static class Person1 {
private String name; private String name;
@ManyToOne(fetch = FetchType.LAZY) @OneToOne(mappedBy = "favoredBook", fetch = FetchType.LAZY)
private Book1 favoriteBook; private Book1 favoriteBook;
} }
@Test @Test
@JiraKey( "HHH-15831" ) public void testOneToManyMappedBy() {
public void testError2() {
final StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistry(); final StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistry();
try { try {
new MetadataSources( ssr ) new MetadataSources( ssr )
@ -78,7 +83,7 @@ public class StructComponentCollectionErrorTest {
Assertions.fail( "Expected a failure" ); Assertions.fail( "Expected a failure" );
} }
catch (MappingException ex) { catch (MappingException ex) {
assertThat( ex.getMessage(), containsString( "author.bookCollection" ) ); assertThat( ex.getMessage(), containsString( "authors.bookCollection" ) );
} }
finally { finally {
StandardServiceRegistryBuilder.destroy( ssr ); StandardServiceRegistryBuilder.destroy( ssr );
@ -93,15 +98,54 @@ public class StructComponentCollectionErrorTest {
@GeneratedValue @GeneratedValue
private Long id; private Long id;
private String title; private String title;
private Person2 author; private Person2[] authors;
@ManyToOne(fetch = FetchType.LAZY)
private Book2 mainBook;
} }
@Embeddable @Embeddable
@Struct(name = "person_type") @Struct(name = "person_type")
public static class Person2 { public static class Person2 {
private String name; private String name;
@OneToMany @OneToMany(mappedBy = "mainBook")
private List<Book2> bookCollection; private List<Book2> bookCollection;
} }
@Test
public void testOneToMany() {
final StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistry();
try {
new MetadataSources( ssr )
.addAnnotatedClass( Book3.class )
.getMetadataBuilder()
.build();
Assertions.fail( "Expected a failure" );
}
catch (MappingException ex) {
assertThat( ex.getMessage(), containsString( "authors.bookCollection" ) );
}
finally {
StandardServiceRegistryBuilder.destroy( ssr );
}
}
@Entity(name = "Book")
public static class Book3 {
@Id
@GeneratedValue
private Long id;
private String title;
private Person3[] authors;
}
@Embeddable
@Struct(name = "person_type")
public static class Person3 {
private String name;
@OneToMany
private List<Book3> bookCollection;
}
} }

View File

@ -0,0 +1,153 @@
package org.hibernate.orm.test.component;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyDiscriminatorValue;
import org.hibernate.annotations.AnyKeyJavaClass;
import org.hibernate.annotations.Struct;
import org.hibernate.testing.jdbc.SharedDriverManagerTypeCacheClearingIntegrator;
import org.hibernate.testing.orm.junit.BootstrapServiceRegistry;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@BootstrapServiceRegistry(
// Clear the type cache, otherwise we might run into ORA-21700: object does not exist or is marked for delete
integrators = SharedDriverManagerTypeCacheClearingIntegrator.class
)
@DomainModel(
annotatedClasses = {
StructComponentManyToAnyTest.Book.class
}
)
@SessionFactory
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsStructAggregate.class)
public class StructComponentManyToAnyTest {
@BeforeEach
public void setUp(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book1 = new Book();
book1.id = 1L;
book1.title = "Hibernate 3";
book1.author = new Author( "Gavin", null );
session.save( book1 );
Book book2 = new Book();
book2.id = 2L;
book2.title = "Hibernate 6";
book2.author = new Author( "Steve", book1 );
session.save( book2 );
}
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope){
scope.inTransaction(
session ->
session.createQuery( "delete from Book" ).executeUpdate()
);
}
@Test
public void testGet(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery( "from Book b where b.id = 2", Book.class ).getSingleResult();
assertFalse( Hibernate.isInitialized( book.author.getFavoriteBook() ) );
assertEquals( "Gavin", book.author.getFavoriteBook().getAuthor().getName() );
}
);
}
@Entity(name = "Book")
public static class Book {
@Id
private Long id;
private String title;
private Author author;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}
@Embeddable
@Struct( name = "author_type")
public static class Author {
private String name;
@Any(fetch = FetchType.LAZY)
@AnyKeyJavaClass(Long.class)
@AnyDiscriminatorValue(entity = Book.class, discriminator = "B")
@JoinColumn(name = "favorite_book_id")
@Column(name = "favorite_book_type")
private Object favoriteBook;
public Author() {
}
public Author(String name, Object favoriteBook) {
this.name = name;
this.favoriteBook = favoriteBook;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Book getFavoriteBook() {
return (Book) favoriteBook;
}
public void setFavoriteBook(Book favoriteBook) {
this.favoriteBook = favoriteBook;
}
}
}

View File

@ -0,0 +1,173 @@
package org.hibernate.orm.test.component;
import java.util.Set;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Struct;
import org.hibernate.testing.jdbc.SharedDriverManagerTypeCacheClearingIntegrator;
import org.hibernate.testing.orm.junit.BootstrapServiceRegistry;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@BootstrapServiceRegistry(
// Clear the type cache, otherwise we might run into ORA-21700: object does not exist or is marked for delete
integrators = SharedDriverManagerTypeCacheClearingIntegrator.class
)
@DomainModel(
annotatedClasses = {
StructComponentManyToManyMappedByTest.Book.class
}
)
@SessionFactory
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsStructAggregate.class)
public class StructComponentManyToManyMappedByTest {
@BeforeEach
public void setUp(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book1 = new Book();
book1.id = 1L;
book1.title = "Main book";
book1.author = new Author( "Abc" );
session.save( book1 );
Book book2 = new Book();
book2.id = 2L;
book2.title = "Second book";
book2.booksInSeries = Set.of( book1 );
book2.author = new Author( "Abc" );
session.save( book2 );
}
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope){
scope.inTransaction(
session ->
session.createQuery( "delete from Book" ).executeUpdate()
);
}
@Test
public void testGet(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery( "from Book b where b.id = 1", Book.class ).getSingleResult();
assertFalse( Hibernate.isInitialized( book.author.getBooks() ) );
assertEquals( "Second book", book.author.getBooks().iterator().next().getTitle() );
}
);
}
@Test
public void testJoin(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery(
"from Book b join fetch b.author.books where b.id = 1",
Book.class
).getSingleResult();
assertTrue( Hibernate.isInitialized( book.author.getBooks() ) );
assertEquals( "Second book", book.author.getBooks().iterator().next().getTitle() );
}
);
}
@Entity(name = "Book")
public static class Book {
@Id
private Long id;
private String title;
private Author author;
@ManyToMany
private Set<Book> booksInSeries;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
public Set<Book> getBooksInSeries() {
return booksInSeries;
}
public void setBooksInSeries(Set<Book> booksInSeries) {
this.booksInSeries = booksInSeries;
}
}
@Embeddable
@Struct( name = "author_type")
public static class Author {
private String name;
@ManyToMany(mappedBy = "booksInSeries")
private Set<Book> books;
public Author() {
}
public Author(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Book> getBooks() {
return books;
}
public void setBooks(Set<Book> books) {
this.books = books;
}
}
}

View File

@ -0,0 +1,161 @@
package org.hibernate.orm.test.component;
import java.util.Set;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Struct;
import org.hibernate.testing.jdbc.SharedDriverManagerTypeCacheClearingIntegrator;
import org.hibernate.testing.orm.junit.BootstrapServiceRegistry;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@BootstrapServiceRegistry(
// Clear the type cache, otherwise we might run into ORA-21700: object does not exist or is marked for delete
integrators = SharedDriverManagerTypeCacheClearingIntegrator.class
)
@DomainModel(
annotatedClasses = {
StructComponentManyToManyTest.Book.class
}
)
@SessionFactory
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsStructAggregate.class)
public class StructComponentManyToManyTest {
@BeforeEach
public void setUp(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book1 = new Book();
book1.id = 1L;
book1.title = "Main book";
book1.author = new Author( "Abc", null );
session.save( book1 );
Book book2 = new Book();
book2.id = 2L;
book2.title = "Second book";
book2.author = new Author( "Abc", book1 );
session.save( book2 );
}
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope){
scope.inTransaction(
session ->
session.createQuery( "delete from Book" ).executeUpdate()
);
}
@Test
public void testGet(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery( "from Book b where b.id = 2", Book.class ).getSingleResult();
assertFalse( Hibernate.isInitialized( book.author.getBooks() ) );
assertEquals( "Main book", book.author.getBooks().iterator().next().getTitle() );
}
);
}
@Test
public void testJoin(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery(
"from Book b join fetch b.author.books where b.id = 2",
Book.class
).getSingleResult();
assertTrue( Hibernate.isInitialized( book.author.getBooks() ) );
assertEquals( "Main book", book.author.getBooks().iterator().next().getTitle() );
}
);
}
@Entity(name = "Book")
public static class Book {
@Id
private Long id;
private String title;
private Author author;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}
@Embeddable
@Struct( name = "author_type")
public static class Author {
private String name;
@ManyToMany
private Set<Book> books;
public Author() {
}
public Author(String name, Book book) {
this.name = name;
this.books = book == null ? null : Set.of( book );
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Book> getBooks() {
return books;
}
public void setBooks(Set<Book> books) {
this.books = books;
}
}
}

View File

@ -0,0 +1,159 @@
package org.hibernate.orm.test.component;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Struct;
import org.hibernate.testing.jdbc.SharedDriverManagerTypeCacheClearingIntegrator;
import org.hibernate.testing.orm.junit.BootstrapServiceRegistry;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@BootstrapServiceRegistry(
// Clear the type cache, otherwise we might run into ORA-21700: object does not exist or is marked for delete
integrators = SharedDriverManagerTypeCacheClearingIntegrator.class
)
@DomainModel(
annotatedClasses = {
StructComponentManyToOneTest.Book.class
}
)
@SessionFactory
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsStructAggregate.class)
public class StructComponentManyToOneTest {
@BeforeEach
public void setUp(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book1 = new Book();
book1.id = 1L;
book1.title = "Hibernate 3";
book1.author = new Author( "Gavin", null );
session.save( book1 );
Book book2 = new Book();
book2.id = 2L;
book2.title = "Hibernate 6";
book2.author = new Author( "Steve", book1 );
session.save( book2 );
}
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope){
scope.inTransaction(
session ->
session.createQuery( "delete from Book" ).executeUpdate()
);
}
@Test
public void testGet(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery( "from Book b where b.id = 2", Book.class ).getSingleResult();
assertFalse( Hibernate.isInitialized( book.author.getFavoriteBook() ) );
assertEquals( "Gavin", book.author.getFavoriteBook().getAuthor().getName() );
}
);
}
@Test
public void testJoin(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery(
"from Book b join fetch b.author.favoriteBook where b.id = 2",
Book.class
).getSingleResult();
assertTrue( Hibernate.isInitialized( book.author.getFavoriteBook() ) );
assertEquals( "Gavin", book.author.getFavoriteBook().getAuthor().getName() );
}
);
}
@Entity(name = "Book")
public static class Book {
@Id
private Long id;
private String title;
private Author author;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}
@Embeddable
@Struct( name = "author_type")
public static class Author {
private String name;
@ManyToOne(fetch = FetchType.LAZY)
private Book favoriteBook;
public Author() {
}
public Author(String name, Book favoriteBook) {
this.name = name;
this.favoriteBook = favoriteBook;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Book getFavoriteBook() {
return favoriteBook;
}
public void setFavoriteBook(Book favoriteBook) {
this.favoriteBook = favoriteBook;
}
}
}

View File

@ -0,0 +1,173 @@
package org.hibernate.orm.test.component;
import java.util.Set;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Struct;
import org.hibernate.testing.jdbc.SharedDriverManagerTypeCacheClearingIntegrator;
import org.hibernate.testing.orm.junit.BootstrapServiceRegistry;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@BootstrapServiceRegistry(
// Clear the type cache, otherwise we might run into ORA-21700: object does not exist or is marked for delete
integrators = SharedDriverManagerTypeCacheClearingIntegrator.class
)
@DomainModel(
annotatedClasses = {
StructComponentOneToManyMappedByTest.Book.class
}
)
@SessionFactory
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsStructAggregate.class)
public class StructComponentOneToManyMappedByTest {
@BeforeEach
public void setUp(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book1 = new Book();
book1.id = 1L;
book1.title = "Main book";
book1.author = new Author( "Abc" );
session.save( book1 );
Book book2 = new Book();
book2.id = 2L;
book2.title = "Second book";
book2.mainBook = book1;
book2.author = new Author( "Abc" );
session.save( book2 );
}
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope){
scope.inTransaction(
session ->
session.createQuery( "delete from Book" ).executeUpdate()
);
}
@Test
public void testGet(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery( "from Book b where b.id = 1", Book.class ).getSingleResult();
assertFalse( Hibernate.isInitialized( book.author.getBooks() ) );
assertEquals( "Second book", book.author.getBooks().iterator().next().getTitle() );
}
);
}
@Test
public void testJoin(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery(
"from Book b join fetch b.author.books where b.id = 1",
Book.class
).getSingleResult();
assertTrue( Hibernate.isInitialized( book.author.getBooks() ) );
assertEquals( "Second book", book.author.getBooks().iterator().next().getTitle() );
}
);
}
@Entity(name = "Book")
public static class Book {
@Id
private Long id;
private String title;
private Author author;
@ManyToOne(fetch = FetchType.LAZY)
private Book mainBook;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
public Book getMainBook() {
return mainBook;
}
public void setMainBook(Book mainBook) {
this.mainBook = mainBook;
}
}
@Embeddable
@Struct( name = "author_type")
public static class Author {
private String name;
@OneToMany(mappedBy = "mainBook")
private Set<Book> books;
public Author() {
}
public Author(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Book> getBooks() {
return books;
}
public void setBooks(Set<Book> books) {
this.books = books;
}
}
}

View File

@ -0,0 +1,161 @@
package org.hibernate.orm.test.component;
import java.util.Set;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Struct;
import org.hibernate.testing.jdbc.SharedDriverManagerTypeCacheClearingIntegrator;
import org.hibernate.testing.orm.junit.BootstrapServiceRegistry;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToMany;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@BootstrapServiceRegistry(
// Clear the type cache, otherwise we might run into ORA-21700: object does not exist or is marked for delete
integrators = SharedDriverManagerTypeCacheClearingIntegrator.class
)
@DomainModel(
annotatedClasses = {
StructComponentOneToManyTest.Book.class
}
)
@SessionFactory
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsStructAggregate.class)
public class StructComponentOneToManyTest {
@BeforeEach
public void setUp(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book1 = new Book();
book1.id = 1L;
book1.title = "Hibernate 3";
book1.author = new Author( "Gavin", null );
session.save( book1 );
Book book2 = new Book();
book2.id = 2L;
book2.title = "Hibernate 6";
book2.author = new Author( "Steve", book1 );
session.save( book2 );
}
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope){
scope.inTransaction(
session ->
session.createQuery( "delete from Book" ).executeUpdate()
);
}
@Test
public void testGet(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery( "from Book b where b.id = 2", Book.class ).getSingleResult();
assertFalse( Hibernate.isInitialized( book.author.getBooks() ) );
assertEquals( "Gavin", book.author.getBooks().iterator().next().getAuthor().getName() );
}
);
}
@Test
public void testJoin(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery(
"from Book b join fetch b.author.books where b.id = 2",
Book.class
).getSingleResult();
assertTrue( Hibernate.isInitialized( book.author.getBooks() ) );
assertEquals( "Gavin", book.author.getBooks().iterator().next().getAuthor().getName() );
}
);
}
@Entity(name = "Book")
public static class Book {
@Id
private Long id;
private String title;
private Author author;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}
@Embeddable
@Struct( name = "author_type")
public static class Author {
private String name;
@OneToMany
private Set<Book> books;
public Author() {
}
public Author(String name, Book book) {
this.name = name;
this.books = book == null ? null : Set.of( book );
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Book> getBooks() {
return books;
}
public void setBooks(Set<Book> books) {
this.books = books;
}
}
}

View File

@ -0,0 +1,190 @@
package org.hibernate.orm.test.component;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Struct;
import org.hibernate.testing.jdbc.SharedDriverManagerTypeCacheClearingIntegrator;
import org.hibernate.testing.orm.junit.BootstrapServiceRegistry;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@BootstrapServiceRegistry(
// Clear the type cache, otherwise we might run into ORA-21700: object does not exist or is marked for delete
integrators = SharedDriverManagerTypeCacheClearingIntegrator.class
)
@DomainModel(
annotatedClasses = {
StructComponentOneToOneMappedByTest.Book.class,
StructComponentOneToOneMappedByTest.BookDetails.class
}
)
@SessionFactory
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsStructAggregate.class)
public class StructComponentOneToOneMappedByTest {
@BeforeEach
public void setUp(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = new Book();
BookDetails bookDetails = new BookDetails(book, "A nice book");
book.id = 1L;
book.title = "Hibernate 6";
book.author = new Author( "Steve", bookDetails );
session.save( book );
session.save( bookDetails );
}
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope){
scope.inTransaction(
session -> {
session.createQuery( "delete from BookDetails" ).executeUpdate();
session.createQuery( "delete from Book" ).executeUpdate();
}
);
}
@Test
public void testGet(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery( "from Book b", Book.class ).getSingleResult();
// One-to-one mappedBy is eager by default
assertTrue( Hibernate.isInitialized( book.author.getDetails() ) );
assertEquals( "A nice book", book.author.getDetails().getSummary() );
}
);
}
@Test
public void testJoin(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery(
"from Book b join fetch b.author.details",
Book.class
).getSingleResult();
assertTrue( Hibernate.isInitialized( book.author.getDetails() ) );
assertEquals( "A nice book", book.author.getDetails().getSummary() );
}
);
}
@Entity(name = "Book")
public static class Book {
@Id
private Long id;
private String title;
private Author author;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}
@Embeddable
@Struct( name = "author_type")
public static class Author {
private String name;
@OneToOne(mappedBy = "book", fetch = FetchType.LAZY)
private BookDetails details;
public Author() {
}
public Author(String name, BookDetails details) {
this.name = name;
this.details = details;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BookDetails getDetails() {
return details;
}
public void setDetails(BookDetails details) {
this.details = details;
}
}
@Entity(name = "BookDetails")
public static class BookDetails {
@Id
@OneToOne
private Book book;
private String summary;
public BookDetails() {
}
public BookDetails(Book book, String summary) {
this.book = book;
this.summary = summary;
}
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
}
}

View File

@ -0,0 +1,161 @@
package org.hibernate.orm.test.component;
import java.util.List;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Struct;
import org.hibernate.testing.jdbc.SharedDriverManagerTypeCacheClearingIntegrator;
import org.hibernate.testing.orm.junit.BootstrapServiceRegistry;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@BootstrapServiceRegistry(
// Clear the type cache, otherwise we might run into ORA-21700: object does not exist or is marked for delete
integrators = SharedDriverManagerTypeCacheClearingIntegrator.class
)
@DomainModel(
annotatedClasses = {
StructComponentOneToOneTest.Book.class
}
)
@SessionFactory
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsStructAggregate.class)
public class StructComponentOneToOneTest {
@BeforeEach
public void setUp(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book1 = new Book();
book1.id = 1L;
book1.title = "Hibernate 3";
book1.author = new Author( "Gavin", null );
session.save( book1 );
Book book2 = new Book();
book2.id = 2L;
book2.title = "Hibernate 6";
book2.author = new Author( "Steve", book1 );
session.save( book2 );
}
);
}
@AfterEach
public void tearDown(SessionFactoryScope scope){
scope.inTransaction(
session ->
session.createQuery( "delete from Book" ).executeUpdate()
);
}
@Test
public void testGet(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery( "from Book b where b.id = 2", Book.class ).getSingleResult();
assertFalse( Hibernate.isInitialized( book.author.getFavoriteBook() ) );
assertEquals( "Gavin", book.author.getFavoriteBook().getAuthor().getName() );
}
);
}
@Test
public void testJoin(SessionFactoryScope scope){
scope.inTransaction(
session -> {
Book book = session.createQuery(
"from Book b join fetch b.author.favoriteBook where b.id = 2",
Book.class
).getSingleResult();
assertTrue( Hibernate.isInitialized( book.author.getFavoriteBook() ) );
assertEquals( "Gavin", book.author.getFavoriteBook().getAuthor().getName() );
}
);
}
@Entity(name = "Book")
public static class Book {
@Id
private Long id;
private String title;
private Author author;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}
@Embeddable
@Struct( name = "author_type")
public static class Author {
private String name;
@OneToOne(fetch = FetchType.LAZY)
private Book favoriteBook;
public Author() {
}
public Author(String name, Book favoriteBook) {
this.name = name;
this.favoriteBook = favoriteBook;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Book getFavoriteBook() {
return favoriteBook;
}
public void setFavoriteBook(Book favoriteBook) {
this.favoriteBook = favoriteBook;
}
}
}

View File

@ -0,0 +1,169 @@
package org.hibernate.orm.test.component;
import java.util.List;
import org.hibernate.MappingException;
import org.hibernate.annotations.Struct;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.util.ServiceRegistryUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
@JiraKey( "HHH-15831" )
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsStructAggregate.class)
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsStructuralArrays.class)
public class StructNestedComponentAssociationErrorTest {
@Test
public void testOneToOneMappedByNested() {
final StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistry();
try {
new MetadataSources( ssr )
.addAnnotatedClass( Book1.class )
.getMetadataBuilder()
.build();
Assertions.fail( "Expected a failure" );
}
catch (MappingException ex) {
assertThat( ex.getMessage(), containsString( "authorInfos.person.favoriteBook" ) );
}
finally {
StandardServiceRegistryBuilder.destroy( ssr );
}
}
@Entity(name = "Book")
public static class Book1 {
@Id
@GeneratedValue
private Long id;
private String title;
private AuthorInfo[] authorInfos;
@OneToOne(fetch = FetchType.LAZY)
private Book1 favoredBook;
}
@Embeddable
@Struct(name = "author_info_type")
public static class AuthorInfo {
Person1 person;
}
@Embeddable
@Struct(name = "person_type")
public static class Person1 {
private String name;
@OneToOne(mappedBy = "favoredBook", fetch = FetchType.LAZY)
private Book1 favoriteBook;
}
@Test
public void testOneToManyMappedByNested() {
final StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistry();
try {
new MetadataSources( ssr )
.addAnnotatedClass( Book2.class )
.getMetadataBuilder()
.build();
Assertions.fail( "Expected a failure" );
}
catch (MappingException ex) {
assertThat( ex.getMessage(), containsString( "authorInfos.person.bookCollection" ) );
}
finally {
StandardServiceRegistryBuilder.destroy( ssr );
}
}
@Entity(name = "Book")
public static class Book2 {
@Id
@GeneratedValue
private Long id;
private String title;
private AuthorInfo2[] authorInfos;
@ManyToOne(fetch = FetchType.LAZY)
private Book2 mainBook;
}
@Embeddable
@Struct(name = "author_info_type")
public static class AuthorInfo2 {
Person2 person;
}
@Embeddable
@Struct(name = "person_type")
public static class Person2 {
private String name;
@OneToMany(mappedBy = "mainBook")
private List<Book2> bookCollection;
}
@Test
public void testOneToMany() {
final StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistry();
try {
new MetadataSources( ssr )
.addAnnotatedClass( Book3.class )
.getMetadataBuilder()
.build();
Assertions.fail( "Expected a failure" );
}
catch (MappingException ex) {
assertThat( ex.getMessage(), containsString( "authorInfos.person.bookCollection" ) );
}
finally {
StandardServiceRegistryBuilder.destroy( ssr );
}
}
@Entity(name = "Book")
public static class Book3 {
@Id
@GeneratedValue
private Long id;
private String title;
private AuthorInfo3[] authorInfos;
}
@Embeddable
@Struct(name = "author_info_type")
public static class AuthorInfo3 {
Person3 person;
}
@Embeddable
@Struct(name = "person_type")
public static class Person3 {
private String name;
@OneToMany
private List<Book3> bookCollection;
}
}