Skip to content

Commit 6172c4e

Browse files
committed
HHH-19575 Ensure null struct is fetched as null embeddable
1 parent f3c6c19 commit 6172c4e

File tree

13 files changed

+210
-112
lines changed

13 files changed

+210
-112
lines changed

hibernate-core/src/main/java/org/hibernate/boot/model/internal/AggregateComponentSecondPass.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.hibernate.AnnotationException;
1313
import org.hibernate.MappingException;
1414
import org.hibernate.annotations.Comment;
15+
import org.hibernate.boot.model.naming.Identifier;
1516
import org.hibernate.boot.model.relational.Database;
1617
import org.hibernate.boot.model.relational.Namespace;
1718
import org.hibernate.boot.model.relational.QualifiedName;
@@ -111,7 +112,8 @@ public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws
111112
orderColumns( registeredUdt, originalOrder );
112113
}
113114
else {
114-
addAuxiliaryObjects = false;
115+
addAuxiliaryObjects =
116+
isAggregateArray() && namespace.locateUserDefinedArrayType( Identifier.toIdentifier( aggregateColumn.getSqlType() ) ) == null;
115117
validateEqual( registeredUdt, udt );
116118
}
117119
}

hibernate-core/src/main/java/org/hibernate/dialect/aggregate/DB2AggregateSupport.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,9 +581,9 @@ public List<AuxiliaryDatabaseObject> aggregateAuxiliaryDatabaseObjects(
581581
var serializerSb = new StringBuilder();
582582
var deserializerSb = new StringBuilder();
583583
serializerSb.append( "create function " ).append( columnType ).append( "_serializer(v " ).append( columnType ).append( ") returns xml language sql " )
584-
.append( "return xmlelement(name \"").append( XmlHelper.ROOT_TAG ).append( "\"" );
584+
.append( "return case when v is null then null else xmlelement(name \"").append( XmlHelper.ROOT_TAG ).append( "\"" );
585585
appendSerializer( aggregatedColumns, serializerSb, "v..", legacyXmlFormatEnabled );
586-
serializerSb.append( ')' );
586+
serializerSb.append( ") end" );
587587

588588
deserializerSb.append( "create function " ).append( columnType ).append( "_deserializer(v xml) returns " ).append( columnType ).append( " language sql " )
589589
.append( "return select " ).append( columnType ).append( "()" );
@@ -633,6 +633,10 @@ private static void appendSerializer(List<Column> aggregatedColumns, StringBuild
633633
}
634634
for ( Column udtColumn : aggregatedColumns ) {
635635
serializerSb.append( sep );
636+
if ( udtColumn.getSqlTypeCode() == STRUCT ) {
637+
serializerSb.append( "case when ").append( prefix ).append( udtColumn.getName() )
638+
.append( " is null then null else " );
639+
}
636640
serializerSb.append( "xmlelement(name \"" ).append( udtColumn.getName() ).append( "\"" );
637641
if ( udtColumn.getSqlTypeCode() == STRUCT ) {
638642
final AggregateColumn aggregateColumn = (AggregateColumn) udtColumn;
@@ -664,6 +668,9 @@ else if ( needsVarcharForBitDataCast( udtColumn.getSqlType() ) ) {
664668
serializerSb.append( ',' ).append( prefix ).append( udtColumn.getName() );
665669
}
666670
serializerSb.append( ')' );
671+
if ( udtColumn.getSqlTypeCode() == STRUCT ) {
672+
serializerSb.append( " end" );
673+
}
667674
sep = ',';
668675
}
669676
if ( aggregatedColumns.size() > 1 ) {

hibernate-core/src/main/java/org/hibernate/dialect/type/AbstractPostgreSQLStructJdbcType.java

Lines changed: 10 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,6 @@ else if ( string.charAt( i + 1 ) == '{' ) {
510510
quotes + 1,
511511
arrayList,
512512
(BasicType<Object>) pluralType.getElementType(),
513-
returnEmbeddable,
514513
options
515514
);
516515
assert string.charAt( subEnd - 1 ) == '}';
@@ -620,7 +619,6 @@ else if ( jdbcMapping.getJavaTypeDescriptor().getJavaTypeClass().isEnum()
620619
quotes + 1,
621620
arrayList,
622621
(BasicType<Object>) pluralType.getElementType(),
623-
returnEmbeddable,
624622
options
625623
);
626624
assert string.charAt( i - 1 ) == '}';
@@ -663,7 +661,6 @@ private int deserializeArray(
663661
int quotes,
664662
ArrayList<Object> values,
665663
BasicType<Object> elementType,
666-
boolean returnEmbeddable,
667664
WrapperOptions options) throws SQLException {
668665
boolean inQuote = false;
669666
StringBuilder escapingSb = null;
@@ -854,29 +851,16 @@ private int deserializeArray(
854851
i + 1,
855852
quotes + 1,
856853
subValues,
857-
returnEmbeddable,
854+
true,
858855
options
859856
);
860-
if ( returnEmbeddable ) {
861-
final StructAttributeValues attributeValues = structJdbcType.getAttributeValues(
862-
structJdbcType.embeddableMappingType,
863-
structJdbcType.orderMapping,
864-
subValues,
865-
options
866-
);
867-
values.add( instantiate( structJdbcType.embeddableMappingType, attributeValues ) );
868-
}
869-
else {
870-
if ( structJdbcType.inverseOrderMapping != null ) {
871-
StructHelper.orderJdbcValues(
872-
structJdbcType.embeddableMappingType,
873-
structJdbcType.inverseOrderMapping,
874-
subValues.clone(),
875-
subValues
876-
);
877-
}
878-
values.add( subValues );
879-
}
857+
final StructAttributeValues attributeValues = structJdbcType.getAttributeValues(
858+
structJdbcType.embeddableMappingType,
859+
structJdbcType.orderMapping,
860+
subValues,
861+
options
862+
);
863+
values.add( instantiate( structJdbcType.embeddableMappingType, attributeValues ) );
880864
// The subEnd points to the first character after the '}',
881865
// so move forward the index to point to the next char after quotes
882866
assert isDoubleQuote( string, subEnd, expectedQuotes );
@@ -994,38 +978,8 @@ else if ( elementType.getJavaTypeDescriptor().getJavaTypeClass().isEnum()
994978
}
995979

996980
private SelectableMapping getJdbcValueSelectable(int jdbcValueSelectableIndex) {
997-
if ( orderMapping != null ) {
998-
final int numberOfAttributeMappings = embeddableMappingType.getNumberOfAttributeMappings();
999-
final int size = numberOfAttributeMappings + ( embeddableMappingType.isPolymorphic() ? 1 : 0 );
1000-
int count = 0;
1001-
for ( int i = 0; i < size; i++ ) {
1002-
final ValuedModelPart modelPart = getSubPart( embeddableMappingType, orderMapping[i] );
1003-
if ( modelPart.getMappedType() instanceof EmbeddableMappingType embeddableMappingType ) {
1004-
final SelectableMapping aggregateMapping = embeddableMappingType.getAggregateMapping();
1005-
if ( aggregateMapping == null ) {
1006-
final SelectableMapping subSelectable = embeddableMappingType.getJdbcValueSelectable( jdbcValueSelectableIndex - count );
1007-
if ( subSelectable != null ) {
1008-
return subSelectable;
1009-
}
1010-
count += embeddableMappingType.getJdbcValueCount();
1011-
}
1012-
else {
1013-
if ( count == jdbcValueSelectableIndex ) {
1014-
return aggregateMapping;
1015-
}
1016-
count++;
1017-
}
1018-
}
1019-
else {
1020-
if ( count == jdbcValueSelectableIndex ) {
1021-
return (SelectableMapping) modelPart;
1022-
}
1023-
count += modelPart.getJdbcTypeCount();
1024-
}
1025-
}
1026-
return null;
1027-
}
1028-
return embeddableMappingType.getJdbcValueSelectable( jdbcValueSelectableIndex );
981+
return embeddableMappingType.getJdbcValueSelectable(
982+
orderMapping != null ? orderMapping[jdbcValueSelectableIndex] : jdbcValueSelectableIndex );
1029983
}
1030984

1031985
private static boolean repeatsChar(String string, int start, int times, char expectedChar) {

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyDiscriminatorPart.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.hibernate.metamodel.mapping.JdbcMapping;
1818
import org.hibernate.metamodel.mapping.MappingType;
1919
import org.hibernate.metamodel.mapping.SelectableConsumer;
20+
import org.hibernate.metamodel.mapping.SelectablePath;
2021
import org.hibernate.metamodel.model.domain.NavigableRole;
2122
import org.hibernate.metamodel.spi.ImplicitDiscriminatorStrategy;
2223
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
@@ -54,6 +55,7 @@ public class AnyDiscriminatorPart implements DiscriminatorMapping, FetchOptions
5455

5556
private final String table;
5657
private final String column;
58+
private final SelectablePath selectablePath;
5759
private final String customReadExpression;
5860
private final String customWriteExpression;
5961
private final String columnDefinition;
@@ -72,7 +74,10 @@ public AnyDiscriminatorPart(
7274
NavigableRole partRole,
7375
DiscriminatedAssociationModelPart declaringType,
7476
String table,
75-
String column, String customReadExpression, String customWriteExpression,
77+
String column,
78+
SelectablePath selectablePath,
79+
String customReadExpression,
80+
String customWriteExpression,
7681
String columnDefinition,
7782
Long length,
7883
Integer precision,
@@ -88,6 +93,7 @@ public AnyDiscriminatorPart(
8893
this.declaringType = declaringType;
8994
this.table = table;
9095
this.column = column;
96+
this.selectablePath = selectablePath;
9197
this.customReadExpression = customReadExpression;
9298
this.customWriteExpression = customWriteExpression;
9399
this.columnDefinition = columnDefinition;
@@ -142,6 +148,16 @@ public String getSelectionExpression() {
142148
return column;
143149
}
144150

151+
@Override
152+
public String getSelectableName() {
153+
return selectablePath.getSelectableName();
154+
}
155+
156+
@Override
157+
public SelectablePath getSelectablePath() {
158+
return selectablePath;
159+
}
160+
145161
@Override
146162
public boolean isFormula() {
147163
return false;

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyKeyPart.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.hibernate.metamodel.mapping.EntityMappingType;
1616
import org.hibernate.metamodel.mapping.JdbcMapping;
1717
import org.hibernate.metamodel.mapping.MappingType;
18+
import org.hibernate.metamodel.mapping.SelectablePath;
1819
import org.hibernate.metamodel.model.domain.NavigableRole;
1920
import org.hibernate.spi.NavigablePath;
2021
import org.hibernate.sql.ast.spi.FromClauseAccess;
@@ -43,6 +44,7 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
4344
private final NavigableRole navigableRole;
4445
private final String table;
4546
private final String column;
47+
private final SelectablePath selectablePath;
4648
private final DiscriminatedAssociationModelPart anyPart;
4749
private final String customReadExpression;
4850
private final String customWriteExpression;
@@ -61,6 +63,7 @@ public AnyKeyPart(
6163
DiscriminatedAssociationModelPart anyPart,
6264
String table,
6365
String column,
66+
SelectablePath selectablePath,
6467
String customReadExpression,
6568
String customWriteExpression,
6669
String columnDefinition,
@@ -75,6 +78,7 @@ public AnyKeyPart(
7578
this.navigableRole = navigableRole;
7679
this.table = table;
7780
this.column = column;
81+
this.selectablePath = selectablePath;
7882
this.anyPart = anyPart;
7983
this.customReadExpression = customReadExpression;
8084
this.customWriteExpression = customWriteExpression;
@@ -99,6 +103,16 @@ public String getSelectionExpression() {
99103
return column;
100104
}
101105

106+
@Override
107+
public String getSelectableName() {
108+
return selectablePath.getSelectableName();
109+
}
110+
111+
@Override
112+
public SelectablePath getSelectablePath() {
113+
return selectablePath;
114+
}
115+
102116
@Override
103117
public boolean isFormula() {
104118
return false;

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/DiscriminatedAssociationMapping.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.hibernate.metamodel.mapping.EntityMappingType;
2424
import org.hibernate.metamodel.mapping.MappingType;
2525
import org.hibernate.metamodel.mapping.ModelPart;
26+
import org.hibernate.metamodel.mapping.SelectablePath;
2627
import org.hibernate.metamodel.model.domain.NavigableRole;
2728
import org.hibernate.spi.NavigablePath;
2829
import org.hibernate.sql.ast.tree.from.TableGroup;
@@ -71,13 +72,18 @@ public static DiscriminatedAssociationMapping from(
7172
assert !keySelectable.isFormula();
7273
final Column metaColumn = (Column) metaSelectable;
7374
final Column keyColumn = (Column) keySelectable;
75+
final SelectablePath parentSelectablePath = declaringModelPart.asAttributeMapping() != null
76+
? MappingModelCreationHelper.getSelectablePath( declaringModelPart.asAttributeMapping().getDeclaringType() )
77+
: null;
7478

7579
final MetaType metaType = (MetaType) anyType.getDiscriminatorType();
7680
final AnyDiscriminatorPart discriminatorPart = new AnyDiscriminatorPart(
77-
containerRole.append( AnyDiscriminatorPart.ROLE_NAME),
81+
containerRole.append( AnyDiscriminatorPart.ROLE_NAME ),
7882
declaringModelPart,
7983
tableName,
8084
metaColumn.getText( dialect ),
85+
parentSelectablePath != null ? parentSelectablePath.append( metaColumn.getQuotedName( dialect ) )
86+
: new SelectablePath( metaColumn.getQuotedName( dialect ) ),
8187
metaColumn.getCustomReadExpression(),
8288
metaColumn.getCustomWriteExpression(),
8389
metaColumn.getSqlType(),
@@ -100,6 +106,8 @@ public static DiscriminatedAssociationMapping from(
100106
declaringModelPart,
101107
tableName,
102108
keyColumn.getText( dialect ),
109+
parentSelectablePath != null ? parentSelectablePath.append( keyColumn.getQuotedName( dialect ) )
110+
: new SelectablePath( keyColumn.getQuotedName( dialect ) ),
103111
keyColumn.getCustomReadExpression(),
104112
keyColumn.getCustomWriteExpression(),
105113
keyColumn.getSqlType(),

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,13 +950,15 @@ else if ( modelPart instanceof EmbeddableValuedModelPart embeddableValuedModelPa
950950
( (PropertyBasedMapping) simpleFkTarget ).getPropertyAccess()
951951
);
952952
}
953+
final SelectablePath parentSelectablePath = getSelectablePath( attributeMapping.getDeclaringType() );
953954
final SelectableMapping keySelectableMapping;
954955
int i = 0;
955956
final Value value = bootProperty.getValue();
956957
if ( columnIterator.hasNext() ) {
957958
keySelectableMapping = SelectableMappingImpl.from(
958959
tableExpression,
959960
columnIterator.next(),
961+
parentSelectablePath,
960962
simpleFkTarget.getJdbcMapping(),
961963
creationProcess.getCreationContext().getTypeConfiguration(),
962964
value.isColumnInsertable( i ),
@@ -973,6 +975,7 @@ else if ( modelPart instanceof EmbeddableValuedModelPart embeddableValuedModelPa
973975
keySelectableMapping = SelectableMappingImpl.from(
974976
tableExpression,
975977
table.getPrimaryKey().getColumn( 0 ),
978+
parentSelectablePath,
976979
simpleFkTarget.getJdbcMapping(),
977980
creationProcess.getCreationContext().getTypeConfiguration(),
978981
value.isColumnInsertable( 0 ),
@@ -1104,6 +1107,7 @@ private static EmbeddedForeignKeyDescriptor buildEmbeddableForeignKeyDescriptor(
11041107
boolean[] updateable,
11051108
Dialect dialect,
11061109
MappingModelCreationProcess creationProcess) {
1110+
final SelectablePath parentSelectablePath = getSelectablePath( keyDeclaringType );
11071111
final boolean hasConstraint;
11081112
final SelectableMappings keySelectableMappings;
11091113
if ( bootValueMapping instanceof Collection collectionBootValueMapping ) {
@@ -1115,6 +1119,7 @@ private static EmbeddedForeignKeyDescriptor buildEmbeddableForeignKeyDescriptor(
11151119
keyTableExpression,
11161120
collectionBootValueMapping.getKey(),
11171121
getPropertyOrder( bootValueMapping, creationProcess ),
1122+
parentSelectablePath,
11181123
creationProcess.getCreationContext().getMetadata(),
11191124
creationProcess.getCreationContext().getTypeConfiguration(),
11201125
insertable,
@@ -1139,6 +1144,7 @@ private static EmbeddedForeignKeyDescriptor buildEmbeddableForeignKeyDescriptor(
11391144
keyTableExpression,
11401145
bootValueMapping,
11411146
getPropertyOrder( bootValueMapping, creationProcess ),
1147+
parentSelectablePath,
11421148
creationProcess.getCreationContext().getMetadata(),
11431149
creationProcess.getCreationContext().getTypeConfiguration(),
11441150
insertable,
@@ -1186,6 +1192,11 @@ private static EmbeddedForeignKeyDescriptor buildEmbeddableForeignKeyDescriptor(
11861192
}
11871193
}
11881194

1195+
public static @Nullable SelectablePath getSelectablePath(ManagedMappingType type) {
1196+
return type instanceof EmbeddableMappingType embeddableType && embeddableType.getAggregateMapping() != null
1197+
? embeddableType.getAggregateMapping().getSelectablePath() : null;
1198+
}
1199+
11891200
public static int[] getPropertyOrder(Value bootValueMapping, MappingModelCreationProcess creationProcess) {
11901201
final RuntimeModelCreationContext creationContext = creationProcess.getCreationContext();
11911202
final ComponentType componentType;

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SelectableMappingImpl.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import java.util.Locale;
88

9+
import org.checkerframework.checker.nullness.qual.Nullable;
910
import org.hibernate.dialect.Dialect;
1011
import org.hibernate.mapping.Column;
1112
import org.hibernate.mapping.Selectable;
@@ -124,7 +125,7 @@ public static SelectableMapping from(
124125
public static SelectableMapping from(
125126
final String containingTableExpression,
126127
final Selectable selectable,
127-
final SelectablePath parentPath,
128+
@Nullable final SelectablePath parentPath,
128129
final JdbcMapping jdbcMapping,
129130
final TypeConfiguration typeConfiguration,
130131
boolean insertable,
@@ -152,7 +153,7 @@ public static SelectableMapping from(
152153
public static SelectableMapping from(
153154
final String containingTableExpression,
154155
final Selectable selectable,
155-
final SelectablePath parentPath,
156+
@Nullable final SelectablePath parentPath,
156157
final JdbcMapping jdbcMapping,
157158
final TypeConfiguration typeConfiguration,
158159
boolean insertable,

0 commit comments

Comments
 (0)