From 2cc1362df7cd167ec12b53e60676c3f116316700 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 31 May 2023 10:49:11 +0200 Subject: [PATCH] HHH-15929 Handle the possibility of different JdbcMappings for the same column --- .../dialect/OracleLegacySqlAstTranslator.java | 2 +- .../dialect/OracleSqlAstTranslator.java | 2 +- ...regateWindowEmulationQueryTransformer.java | 5 - .../ast/internal/LoaderSelectBuilder.java | 1 - .../metamodel/mapping/SelectableConsumer.java | 6 +- .../mapping/internal/AbstractDomainPath.java | 13 +- ...CaseStatementDiscriminatorMappingImpl.java | 6 +- .../mapping/ordering/ast/ColumnReference.java | 10 +- .../AbstractCollectionPersister.java | 4 - .../entity/AbstractEntityPersister.java | 22 ++- .../entity/JoinedSubclassEntityPersister.java | 6 +- .../entity/SingleTableEntityPersister.java | 3 +- .../entity/UnionSubclassEntityPersister.java | 6 +- .../AnonymousTupleEntityValuedModelPart.java | 6 +- .../results/ResultSetMappingSqlSelection.java | 10 +- ...SelfRenderingFunctionSqlAstExpression.java | 17 +- .../sqm/internal/SimpleDeleteQueryPlan.java | 2 +- .../internal/MatchingIdSelectionHelper.java | 1 - .../cte/AbstractCteMutationHandler.java | 4 +- .../internal/cte/CteInsertHandler.java | 14 +- .../internal/cte/CteUpdateHandler.java | 4 - .../internal/inline/InlineUpdateHandler.java | 6 +- .../ExecuteWithTemporaryTableHelper.java | 8 +- .../ExecuteWithoutIdTableHelper.java | 1 - .../temptable/InsertExecutionDelegate.java | 6 +- .../temptable/TableBasedInsertHandler.java | 6 +- .../temptable/UpdateExecutionDelegate.java | 4 +- .../sqm/sql/BaseSqmToSqlAstConverter.java | 17 +- .../SqlAstQueryPartProcessingStateImpl.java | 81 ++++++--- .../sql/ast/spi/AbstractSqlAstTranslator.java | 12 +- .../sql/ast/spi/SqlExpressionResolver.java | 40 +++-- .../hibernate/sql/ast/spi/SqlSelection.java | 6 + .../sql/ast/spi/SqlSelectionProducer.java | 18 ++ .../sql/ast/tree/expression/Expression.java | 33 +++- .../ast/tree/from/DelegatingTableGroup.java | 16 ++ .../exec/internal/AbstractJdbcParameter.java | 3 +- .../internal/ResolvedSqlSelection.java | 15 +- .../RowProcessingStateStandardImpl.java | 3 +- .../results/internal/SqlSelectionImpl.java | 31 +++- .../jdbc/internal/AbstractJdbcValues.java | 24 +-- .../jdbc/internal/JdbcValuesCacheHit.java | 22 ++- .../internal/JdbcValuesResultSetImpl.java | 87 ++++++++- .../sql/results/jdbc/spi/JdbcValues.java | 17 +- .../mapping/basic/PolymorphicJsonTests.java | 170 ++++++++++++++++++ 44 files changed, 580 insertions(+), 190 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/PolymorphicJsonTests.java diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacySqlAstTranslator.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacySqlAstTranslator.java index f210f72f78..84a8067129 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacySqlAstTranslator.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacySqlAstTranslator.java @@ -360,7 +360,7 @@ public class OracleLegacySqlAstTranslator extends Abstr } final QuerySpec subquery = new QuerySpec( false, 1 ); for ( ColumnReference idColumnReference : idColumnReferences ) { - subquery.getSelectClause().addSqlSelection( new SqlSelectionImpl( 0, -1, idColumnReference ) ); + subquery.getSelectClause().addSqlSelection( new SqlSelectionImpl( idColumnReference ) ); } subquery.getFromClause().addRoot( rootTableGroup ); subquery.applyPredicate( querySpec.getWhereClauseRestrictions() ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java index a7061b6a07..598dc9a1da 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleSqlAstTranslator.java @@ -305,7 +305,7 @@ public class OracleSqlAstTranslator extends SqlAstTrans } final QuerySpec subquery = new QuerySpec( false, 1 ); for ( ColumnReference idColumnReference : idColumnReferences ) { - subquery.getSelectClause().addSqlSelection( new SqlSelectionImpl( 0, -1, idColumnReference ) ); + subquery.getSelectClause().addSqlSelection( new SqlSelectionImpl( idColumnReference ) ); } subquery.getFromClause().addRoot( rootTableGroup ); subquery.applyPredicate( querySpec.getWhereClauseRestrictions() ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/AggregateWindowEmulationQueryTransformer.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/AggregateWindowEmulationQueryTransformer.java index f0d6c7c76f..6e81043466 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/AggregateWindowEmulationQueryTransformer.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/AggregateWindowEmulationQueryTransformer.java @@ -147,7 +147,6 @@ public class AggregateWindowEmulationQueryTransformer implements QueryTransforme columnNames.add( columnName ); selectClause.addSqlSelection( new ResolvedSqlSelection( - i + 1, i, finalExpression, (BasicType) mapping.getJdbcMapping() @@ -193,7 +192,6 @@ public class AggregateWindowEmulationQueryTransformer implements QueryTransforme ); final int subValuesPosition = subSelectClause.getSqlSelections().size(); final SqlSelection subSelection = new ResolvedSqlSelection( - subValuesPosition + 1, subValuesPosition, realExpression, (BasicType) jdbcMapping @@ -254,7 +252,6 @@ public class AggregateWindowEmulationQueryTransformer implements QueryTransforme ); final int subValuesPosition = subSelectClause.getSqlSelections().size(); final SqlSelection subSelection = new ResolvedSqlSelection( - subValuesPosition + 1, subValuesPosition, realExpression, (BasicType) jdbcMapping @@ -312,7 +309,6 @@ public class AggregateWindowEmulationQueryTransformer implements QueryTransforme ); final int subValuesPosition = subSelectClause.getSqlSelections().size(); final SqlSelection subSelection = new ResolvedSqlSelection( - subValuesPosition + 1, subValuesPosition, realExpression, (BasicType) jdbcMapping @@ -352,7 +348,6 @@ public class AggregateWindowEmulationQueryTransformer implements QueryTransforme columnNames.add( columnName ); subSelectClause.addSqlSelection( new ResolvedSqlSelection( - oldValueIndex + 1, oldValueIndex, sortExpression, (BasicType) mapping.getJdbcMapping() diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java index 72c9f325b1..d14a21e691 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java @@ -1179,7 +1179,6 @@ public class LoaderSelectBuilder { ); subQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( - valuesPosition + 1, valuesPosition, expression ) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/SelectableConsumer.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/SelectableConsumer.java index 27deb7b21a..3d3594a655 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/SelectableConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/SelectableConsumer.java @@ -6,7 +6,9 @@ */ package org.hibernate.metamodel.mapping; +import java.util.List; import java.util.function.BiConsumer; +import java.util.function.IntFunction; /** * Consumer used to visit selectable (column/formula) mappings @@ -157,7 +159,7 @@ public interface SelectableConsumer { * Very limited functionality in terms of the visited SelectableMappings * will not have any defined JdbcMapping, etc */ - default void accept(String tableName, String[] columnNames) { + default void accept(String tableName, String[] columnNames, IntFunction jdbcMappings) { class SelectableMappingIterator implements SelectableMapping { private int index; @@ -229,7 +231,7 @@ public interface SelectableConsumer { @Override public JdbcMapping getJdbcMapping() { - return null; + return jdbcMappings.apply( index ); } } for ( diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java index a8f849be24..b1494632f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java @@ -69,7 +69,11 @@ public abstract class AbstractDomainPath implements DomainPath { selection.getContainingTableExpression() ); return creationState.getSqlExpressionResolver().resolveSqlExpression( - createColumnReferenceKey( tableReference, selection.getSelectionExpression() ), + createColumnReferenceKey( + tableReference, + selection.getSelectionExpression(), + selection.getJdbcMapping() + ), processingState -> new ColumnReference( tableReference, selection @@ -250,7 +254,11 @@ public abstract class AbstractDomainPath implements DomainPath { SqlAstCreationState creationState) { final TableReference tableReference = tableGroup.resolveTableReference( getNavigablePath(), selection.getContainingTableExpression() ); final Expression expression = creationState.getSqlExpressionResolver().resolveSqlExpression( - createColumnReferenceKey( tableReference, selection.getSelectionExpression() ), + createColumnReferenceKey( + tableReference, + selection.getSelectionExpression(), + selection.getJdbcMapping() + ), processingState -> new ColumnReference( tableReference, selection @@ -271,7 +279,6 @@ public abstract class AbstractDomainPath implements DomainPath { if ( selectClause.isDistinct() && selectClauseDoesNotContainOrderExpression( expression, selectClause ) ) { final int valuesArrayPosition = selectClause.getSqlSelections().size(); SqlSelection sqlSelection = new SqlSelectionImpl( - valuesArrayPosition + 1, valuesArrayPosition, expression ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CaseStatementDiscriminatorMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CaseStatementDiscriminatorMappingImpl.java index f9ac2ea1d0..b89c3a0441 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CaseStatementDiscriminatorMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CaseStatementDiscriminatorMappingImpl.java @@ -108,7 +108,11 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator SqlAstCreationState creationState) { final SqlExpressionResolver expressionResolver = creationState.getSqlExpressionResolver(); return expressionResolver.resolveSqlExpression( - createColumnReferenceKey( tableGroup.getPrimaryTableReference(), getSelectionExpression() ), + createColumnReferenceKey( + tableGroup.getPrimaryTableReference(), + getSelectionExpression(), + jdbcMappingToUse + ), sqlAstProcessingState -> createCaseSearchedExpression( tableGroup ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ordering/ast/ColumnReference.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ordering/ast/ColumnReference.java index 72fd8b65c9..3e4bf1b737 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ordering/ast/ColumnReference.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ordering/ast/ColumnReference.java @@ -21,6 +21,9 @@ import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.SortSpecification; +import org.hibernate.type.NullType; + +import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey; /** * Represents a column-reference used in an order-by fragment @@ -53,13 +56,10 @@ public class ColumnReference implements OrderingExpression, SequencePart { TableGroup tableGroup, String modelPartName, SqlAstCreationState creationState) { - TableReference tableReference; - - tableReference = getTableReference( tableGroup ); - + final TableReference tableReference = getTableReference( tableGroup ); final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlExpressionResolver(); return sqlExpressionResolver.resolveSqlExpression( - SqlExpressionResolver.createColumnReferenceKey( tableReference, columnExpression ), + createColumnReferenceKey( tableReference, columnExpression, NullType.INSTANCE ), sqlAstProcessingState -> new org.hibernate.sql.ast.tree.expression.ColumnReference( tableReference, columnExpression, diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index 99d06a9497..c47d1a3f3c 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -918,7 +918,6 @@ public abstract class AbstractCollectionPersister i, new SqlSelectionImpl( i, - i + 1, new AliasedExpression( sqlSelections.get( i ).getExpression(), keyAlias + columnSuffix ) ) ); @@ -931,7 +930,6 @@ public abstract class AbstractCollectionPersister i, new SqlSelectionImpl( i, - i + 1, new AliasedExpression( sqlSelections.get( i ).getExpression(), indexAlias + columnSuffix ) ) ); @@ -943,7 +941,6 @@ public abstract class AbstractCollectionPersister i, new SqlSelectionImpl( i, - i + 1, new AliasedExpression( sqlSelections.get( i ).getExpression(), identifierColumnAlias + columnSuffix ) ) ); @@ -956,7 +953,6 @@ public abstract class AbstractCollectionPersister i, new SqlSelectionImpl( sqlSelection.getValuesArrayPosition(), - sqlSelection.getJdbcResultSetIndex(), new AliasedExpression( sqlSelection.getExpression(), elementColumnAliases[columnIndex] + columnSuffix ) ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index afef358286..216c487013 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -1353,7 +1353,11 @@ public abstract class AbstractEntityPersister (columnIndex, selection) -> { final String rootPkColumnName = pkColumnNames[ columnIndex ]; final Expression pkColumnExpression = creationState.getSqlExpressionResolver().resolveSqlExpression( - createColumnReferenceKey( rootTableReference, rootPkColumnName ), + createColumnReferenceKey( + rootTableReference, + rootPkColumnName, + selection.getJdbcMapping() + ), sqlAstProcessingState -> new ColumnReference( rootTableReference.getIdentificationVariable(), rootPkColumnName, @@ -1365,7 +1369,11 @@ public abstract class AbstractEntityPersister final String fkColumnName = fkColumnNames[ columnIndex ]; final Expression fkColumnExpression = creationState.getSqlExpressionResolver().resolveSqlExpression( - createColumnReferenceKey( joinedTableReference, fkColumnName ), + createColumnReferenceKey( + joinedTableReference, + fkColumnName, + selection.getJdbcMapping() + ), sqlAstProcessingState -> new ColumnReference( joinedTableReference.getIdentificationVariable(), fkColumnName, @@ -1743,7 +1751,6 @@ public abstract class AbstractEntityPersister i, new SqlSelectionImpl( i, - i + 1, new AliasedExpression( sqlSelections.get( i ).getExpression(), identifierAlias + suffix ) ) ); @@ -1755,7 +1762,6 @@ public abstract class AbstractEntityPersister i, new SqlSelectionImpl( i, - i + 1, new AliasedExpression( sqlSelections.get( i ).getExpression(), getDiscriminatorAlias() + suffix ) ) ); @@ -1767,7 +1773,6 @@ public abstract class AbstractEntityPersister i, new SqlSelectionImpl( i, - i + 1, new AliasedExpression( sqlSelections.get( i ).getExpression(), ROWID_ALIAS + suffix ) ) ); @@ -1788,7 +1793,6 @@ public abstract class AbstractEntityPersister i, new SqlSelectionImpl( sqlSelection.getValuesArrayPosition(), - sqlSelection.getJdbcResultSetIndex(), new AliasedExpression( sqlSelection.getExpression(), selectAlias ) ) ); @@ -2995,14 +2999,16 @@ public abstract class AbstractEntityPersister discriminatorExpression = getDiscriminatorFormulaTemplate(); columnReferenceKey = createColumnReferenceKey( tableGroup.getPrimaryTableReference(), - getDiscriminatorFormulaTemplate() + getDiscriminatorFormulaTemplate(), + getDiscriminatorType() ); } else { discriminatorExpression = getDiscriminatorColumnName(); columnReferenceKey = createColumnReferenceKey( tableGroup.getPrimaryTableReference(), - getDiscriminatorColumnName() + getDiscriminatorColumnName(), + getDiscriminatorType() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java index 0ef2ee9873..dba24f4be5 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java @@ -1417,7 +1417,11 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { final int tablePosition = i; consumer.consume( tableName, - () -> columnConsumer -> columnConsumer.accept( tableName, constraintOrderedKeyColumnNames[tablePosition] ) + () -> columnConsumer -> columnConsumer.accept( + tableName, + constraintOrderedKeyColumnNames[tablePosition], + getIdentifierMapping()::getJdbcMapping + ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java index 3cbd17b1c2..3f4057ea6b 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java @@ -759,7 +759,8 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { tableName, () -> columnConsumer -> columnConsumer.accept( tableName, - constraintOrderedKeyColumnNames[tablePosition] + constraintOrderedKeyColumnNames[tablePosition], + getIdentifierMapping()::getJdbcMapping ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java index 4374e1d61f..6661f7e295 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java @@ -436,7 +436,11 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { final int tablePosition = i; consumer.consume( tableName, - () -> columnConsumer -> columnConsumer.accept( tableName, constraintOrderedKeyColumnNames[tablePosition] ) + () -> columnConsumer -> columnConsumer.accept( + tableName, + constraintOrderedKeyColumnNames[tablePosition], + getIdentifierMapping()::getJdbcMapping + ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEntityValuedModelPart.java b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEntityValuedModelPart.java index c17a736c43..8b8dbc0b9d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEntityValuedModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEntityValuedModelPart.java @@ -351,7 +351,8 @@ public class AnonymousTupleEntityValuedModelPart (ColumnReference) sqlExpressionResolver.resolveSqlExpression( SqlExpressionResolver.createColumnReferenceKey( tableReference, - keyMappings.get( i ).getSelectionExpression() + keyMappings.get( i ).getSelectionExpression(), + keyMappings.get( i ).getJdbcMapping() ), state -> new ColumnReference( tableReference, @@ -371,7 +372,8 @@ public class AnonymousTupleEntityValuedModelPart sqlExpressionResolver.resolveSqlExpression( SqlExpressionResolver.createColumnReferenceKey( tableReference, - targetMappings.get( i ).getSelectionExpression() + targetMappings.get( i ).getSelectionExpression(), + targetMappings.get( i ).getJdbcMapping() ), state -> new ColumnReference( tableReference, diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingSqlSelection.java b/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingSqlSelection.java index 8dd6028825..f4dc7e1c92 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingSqlSelection.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/ResultSetMappingSqlSelection.java @@ -66,11 +66,6 @@ public class ResultSetMappingSqlSelection implements SqlSelection, Expression, S return valuesArrayPosition; } - @Override - public int getJdbcResultSetIndex() { - return valuesArrayPosition + 1; - } - @Override public Expression getExpression() { return this; @@ -81,6 +76,11 @@ public class ResultSetMappingSqlSelection implements SqlSelection, Expression, S return valueMapping; } + @Override + public boolean isVirtual() { + return false; + } + @Override public void accept(SqlAstWalker sqlAstWalker) { throw new UnsupportedOperationException(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingFunctionSqlAstExpression.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingFunctionSqlAstExpression.java index 0c28806466..3065d0cb0a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingFunctionSqlAstExpression.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/function/SelfRenderingFunctionSqlAstExpression.java @@ -91,7 +91,22 @@ public class SelfRenderingFunctionSqlAstExpression return new SqlSelectionImpl( jdbcPosition, valuesArrayPosition, - this + this, + false + ); + } + @Override + public SqlSelection createSqlSelection( + int jdbcPosition, + int valuesArrayPosition, + JavaType javaType, + boolean virtual, + TypeConfiguration typeConfiguration) { + return new SqlSelectionImpl( + jdbcPosition, + valuesArrayPosition, + this, + virtual ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java index 43f3e6bf2b..9e39658ac3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SimpleDeleteQueryPlan.java @@ -165,7 +165,7 @@ public class SimpleDeleteQueryPlan implements NonSelectQueryPlan { sqmInterpretation.getSqlExpressionResolver(), factory ); - matchingIdSubQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( 1, 0, fkTargetColumnExpression ) ); + matchingIdSubQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( 0, fkTargetColumnExpression ) ); matchingIdSubQuery.getFromClause().addRoot( tableGroup diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java index 612fce4a03..293f0d3686 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java @@ -169,7 +169,6 @@ public class MatchingIdSelectionHelper { idSelectionQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( position, - position + 1, expression ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java index 1122b2de6b..2bba1396b1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/AbstractCteMutationHandler.java @@ -174,7 +174,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler ( (SqlExpressible) count).getJdbcMapping() ) ); - querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 1, 0, count ) ); + querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 0, count ) ); querySpec.getFromClause().addRoot( new CteTableGroup( new NamedTableReference( @@ -280,7 +280,6 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler final CteColumn cteColumn = cteColumns.get( i ); subQuerySelectClause.addSqlSelection( new SqlSelectionImpl( - i + 1, i, new ColumnReference( idSelectTableReference, @@ -295,7 +294,6 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler fkModelPart.forEachSelectable( (selectionIndex, selectableMapping) -> subQuerySelectClause.addSqlSelection( new SqlSelectionImpl( - selectionIndex + 1, selectionIndex, new ColumnReference( idSelectTableReference, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java index f381f2f18e..9f659dde74 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java @@ -282,7 +282,6 @@ public class CteInsertHandler implements InsertHandler { if ( !assignsId && entityDescriptor.getGenerator().generatedOnExecution() ) { querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, SqmInsertStrategyHelper.createRowNumberingExpression( querySpec, @@ -312,7 +311,6 @@ public class CteInsertHandler implements InsertHandler { columnNames.add( columnReference.getColumnExpression() ); querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, columnReference.getQualifier().equals( valuesAlias ) ? columnReference @@ -420,7 +418,6 @@ public class CteInsertHandler implements InsertHandler { ); rowsWithSequenceQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, rowNumberColumnReference ) @@ -431,7 +428,6 @@ public class CteInsertHandler implements InsertHandler { ); rowsWithSequenceQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 2, 1, new SelfRenderingSqlFragmentExpression( fragment ) ) @@ -502,7 +498,6 @@ public class CteInsertHandler implements InsertHandler { entityQuery.getFromClause().addRoot( baseTableGroup ); entityQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, new BinaryArithmeticExpression( new ColumnReference( @@ -546,7 +541,6 @@ public class CteInsertHandler implements InsertHandler { final CteColumn cteColumn = cteColumns.get( i ); entityQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( - i + 1, i, new ColumnReference( "e", @@ -622,7 +616,7 @@ public class CteInsertHandler implements InsertHandler { ( (SqlExpressible) count).getJdbcMapping() ) ); - querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 1, 0, count ) ); + querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 0, count ) ); querySpec.getFromClause().addRoot( new CteTableGroup( new NamedTableReference( @@ -877,7 +871,6 @@ public class CteInsertHandler implements InsertHandler { final CteColumn idCteColumn = cteColumns.get( 0 ); querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, new ColumnReference( "t", @@ -893,7 +886,6 @@ public class CteInsertHandler implements InsertHandler { final CteColumn cteColumn = cteColumns.get( j ); querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, new ColumnReference( "e", @@ -933,14 +925,12 @@ public class CteInsertHandler implements InsertHandler { ); finalResultQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, idColumnReference ) ); finalResultQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, SqmInsertStrategyHelper.createRowNumberingExpression( querySpec, @@ -987,7 +977,6 @@ public class CteInsertHandler implements InsertHandler { ); insertSelectSpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, new ColumnReference( "e", @@ -1021,7 +1010,6 @@ public class CteInsertHandler implements InsertHandler { final ColumnReference columnReference = assignmentReferences.get( j ); insertSelectSpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, new ColumnReference( "e", diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteUpdateHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteUpdateHandler.java index 5201cc9788..d1f2c45329 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteUpdateHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteUpdateHandler.java @@ -203,8 +203,6 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda final QuerySpec existsQuerySpec = new QuerySpec( false ); existsQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - -1, - 0, new QueryLiteral<>( 1, factory.getTypeConfiguration().getBasicTypeForJavaType( Integer.class ) @@ -243,8 +241,6 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda targetColumnReferences.addAll( assignment.getAssignable().getColumnReferences() ); querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 0, - -1, assignment.getAssignedValue() ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineUpdateHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineUpdateHandler.java index 6106c32563..1204e147be 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineUpdateHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineUpdateHandler.java @@ -450,7 +450,7 @@ public class InlineUpdateHandler implements UpdateHandler { ) ); querySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( 1, 0, valuesColumnReference ) + new SqlSelectionImpl( 0, valuesColumnReference ) ); } ); @@ -482,7 +482,7 @@ public class InlineUpdateHandler implements UpdateHandler { ) ); querySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( 1, 0, valuesColumnReference ) + new SqlSelectionImpl( 0, valuesColumnReference ) ); } final ValuesTableGroup valuesTableGroup = new ValuesTableGroup( @@ -528,8 +528,6 @@ public class InlineUpdateHandler implements UpdateHandler { targetColumnReferences.addAll( assignment.getAssignable().getColumnReferences() ); querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 0, - -1, assignment.getAssignedValue() ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithTemporaryTableHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithTemporaryTableHelper.java index dffb384204..90f3f688ee 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithTemporaryTableHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithTemporaryTableHelper.java @@ -91,15 +91,14 @@ public final class ExecuteWithTemporaryTableHelper { matchingIdSelection.getFromClause().addRoot( mutatingTableGroup ); mutatingEntityDescriptor.getIdentifierMapping().forEachSelectable( - (jdbcPosition, selection) -> { + (selectionIndex, selection) -> { final TableReference tableReference = mutatingTableGroup.resolveTableReference( mutatingTableGroup.getNavigablePath(), selection.getContainingTableExpression() ); matchingIdSelection.getSelectClause().addSqlSelection( new SqlSelectionImpl( - jdbcPosition, - jdbcPosition + 1, + selectionIndex, sqmConverter.getSqlExpressionResolver().resolveSqlExpression( tableReference, selection @@ -114,7 +113,6 @@ public final class ExecuteWithTemporaryTableHelper { matchingIdSelection.getSelectClause().addSqlSelection( new SqlSelectionImpl( jdbcPosition, - jdbcPosition + 1, new QueryLiteral<>( UUID.fromString( sessionUidAccess.apply( executionContext.getSession() ) ), (BasicValuedMapping) idTable.getSessionUidColumn().getJdbcMapping() @@ -222,7 +220,6 @@ public final class ExecuteWithTemporaryTableHelper { if ( temporaryTableColumn != idTable.getSessionUidColumn() ) { querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - i + 1, i, new ColumnReference( tableReference, @@ -241,7 +238,6 @@ public final class ExecuteWithTemporaryTableHelper { (i, selectableMapping) -> { querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - i + 1, i, new ColumnReference( tableReference, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithoutIdTableHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithoutIdTableHelper.java index f3c2892086..9dd91bcd20 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithoutIdTableHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/ExecuteWithoutIdTableHelper.java @@ -58,7 +58,6 @@ public final class ExecuteWithoutIdTableHelper { final SqlSelection sqlSelection = new SqlSelectionImpl( // irrelevant 0, - 0, columnReference ); matchingIdSelect.getSelectClause().addSqlSelection( sqlSelection ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java index 271a8af668..d24ad452f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java @@ -339,7 +339,6 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio for ( ColumnReference columnReference : assignable.getColumnReferences() ) { querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, new ColumnReference( updatingTableReference.getIdentificationVariable(), @@ -369,7 +368,7 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio identifierMapping.getJdbcMapping() ); idSelectQuerySpec.getSelectClause() - .addSqlSelection( new SqlSelectionImpl( 1, 0, columnReference ) ); + .addSqlSelection( new SqlSelectionImpl( 0, columnReference ) ); idSelectQuerySpec.addSortSpecification( new SortSpecification( columnReference, @@ -528,7 +527,6 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio ); querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, new ColumnReference( updatingTableReference.getIdentificationVariable(), @@ -689,7 +687,6 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio for ( ColumnReference columnReference : assignment.getAssignable().getColumnReferences() ) { querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, new ColumnReference( updatingTableReference.getIdentificationVariable(), @@ -734,7 +731,6 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio ); querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, new ColumnReference( updatingTableReference.getIdentificationVariable(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java index d5d234df3f..48b617c4ec 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/TableBasedInsertHandler.java @@ -219,7 +219,6 @@ public class TableBasedInsertHandler implements InsertHandler { targetPathColumns.add( new Assignment( columnReference, columnReference ) ); querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, SqmInsertStrategyHelper.createRowNumberingExpression( querySpec, @@ -237,13 +236,12 @@ public class TableBasedInsertHandler implements InsertHandler { null, sessionUidColumn.getJdbcMapping() ); - insertStatement.getTargetColumns().add( sessionUidColumnReference ); - targetPathColumns.add( new Assignment( sessionUidColumnReference, sessionUidParameter ) ); querySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( insertStatement.getTargetColumns().size(), - insertStatement.getTargetColumns().size() - 1, sessionUidParameter ) ); + insertStatement.getTargetColumns().add( sessionUidColumnReference ); + targetPathColumns.add( new Assignment( sessionUidColumnReference, sessionUidParameter ) ); } } ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/UpdateExecutionDelegate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/UpdateExecutionDelegate.java index e8f4088bd8..97a8bd7322 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/UpdateExecutionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/UpdateExecutionDelegate.java @@ -345,7 +345,7 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio for ( Assignment assignment : assignments ) { targetColumnReferences.addAll( assignment.getAssignable().getColumnReferences() ); insertSourceSelectQuerySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( 0, -1, assignment.getAssignedValue() ) + new SqlSelectionImpl( assignment.getAssignedValue() ) ); } @@ -380,8 +380,6 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio final QuerySpec existsSubQuerySpec = new QuerySpec( false ); existsSubQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - -1, - 0, new QueryLiteral<>( 1, sessionFactory.getTypeConfiguration().getBasicTypeForJavaType( Integer.class ) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 50b141b06e..681fba496c 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -1478,14 +1478,14 @@ public abstract class BaseSqmToSqlAstConverter extends Base if ( versionExpression != null ) { if ( versionSelection == null ) { // The position is irrelevant as this is only needed for insert - versionSelection = new SqlSelectionImpl( 1, 0, versionExpression ); + versionSelection = new SqlSelectionImpl( 0, versionExpression ); } selectClause.addSqlSelection( versionSelection ); } if ( discriminatorExpression != null ) { if ( discriminatorSelection == null ) { // The position is irrelevant as this is only needed for insert - discriminatorSelection = new SqlSelectionImpl( 1, 0, discriminatorExpression ); + discriminatorSelection = new SqlSelectionImpl( 0, discriminatorExpression ); } selectClause.addSqlSelection( discriminatorSelection ); } @@ -1505,7 +1505,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base return false; } identifierSelection = new SqlSelectionImpl( - 1, 0, SqmInsertStrategyHelper.createRowNumberingExpression( querySpec, sessionFactory ) ); @@ -1519,7 +1518,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base ); // The position is irrelevant as this is only needed for insert identifierSelection = new SqlSelectionImpl( - 1, 0, new SelfRenderingSqlFragmentExpression( fragment ) ); @@ -2369,7 +2367,8 @@ public abstract class BaseSqmToSqlAstConverter extends Base new QueryLiteral<>( selection.getValuesArrayPosition(), basicType( Integer.class ) - ) + ), + false ) ) ); @@ -4469,7 +4468,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base integerType, integerType ); - subQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 1, 0, expression ) ); + subQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 0, expression ) ); subQuerySpec.applyPredicate( pluralAttributeMapping.getKeyDescriptor().generateJoinPredicate( @@ -4653,7 +4652,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base ).getJdbcMapping(), modelPart ); - subQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 1, 0, expression ) ); + subQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( 0, expression ) ); NavigablePath parent = pluralPartPath.getPluralDomainPath().getNavigablePath().getParent(); subQuerySpec.applyPredicate( @@ -4787,7 +4786,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base for ( int i = 0; i < subQueryColumns.size(); i++ ) { subQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - i + 1, i, subQueryColumns.get( i ) ) @@ -4829,7 +4827,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base subQuerySpec.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, expression ) @@ -7444,7 +7441,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base final JdbcLiteral jdbcLiteral = new JdbcLiteral<>( 1, basicType( Integer.class ) ); subQuerySpec.getSelectClause().addSqlSelection( - new SqlSelectionImpl( 1, 0, jdbcLiteral ) + new SqlSelectionImpl( 0, jdbcLiteral ) ); return new ExistsPredicate( subQuerySpec, !predicate.isNegated(), getBooleanType() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqlAstQueryPartProcessingStateImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqlAstQueryPartProcessingStateImpl.java index 6968c12478..cecc5c540e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqlAstQueryPartProcessingStateImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/SqlAstQueryPartProcessingStateImpl.java @@ -6,6 +6,7 @@ */ package org.hibernate.query.sqm.sql.internal; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.function.Function; @@ -109,6 +110,7 @@ public class SqlAstQueryPartProcessingStateImpl // SqlExpressionResolver private Map sqlSelectionMap; + private int nextJdbcPosition = 1; @Override public SqlSelection resolveSqlSelection( @@ -128,29 +130,21 @@ public class SqlAstQueryPartProcessingStateImpl -1, nestingFetchParent.getReferencedMappingType().getSelectableIndex( selectableName ), javaType, + true, typeConfiguration ); } - final Map selectionMap; + final Map selectionMap; if ( deduplicateSelectionItems ) { - final SqlSelection existing; if ( sqlSelectionMap == null ) { sqlSelectionMap = new HashMap<>(); - existing = null; - } - else { - existing = (SqlSelection) sqlSelectionMap.get( expression ); - } - - if ( existing != null ) { - return existing; } //noinspection unchecked - selectionMap = (Map) sqlSelectionMap; + selectionMap = (Map) sqlSelectionMap; } else if ( fetchParent != null ) { // De-duplicate selection items within the root of a fetch parent - final Map> fetchParentSqlSelectionMap; + final Map> fetchParentSqlSelectionMap; final FetchParent root = fetchParent.getRoot(); if ( sqlSelectionMap == null ) { sqlSelectionMap = fetchParentSqlSelectionMap = new HashMap<>(); @@ -158,8 +152,8 @@ public class SqlAstQueryPartProcessingStateImpl } else { //noinspection unchecked - fetchParentSqlSelectionMap = (Map>) sqlSelectionMap; - final Map map = fetchParentSqlSelectionMap.get( root ); + fetchParentSqlSelectionMap = (Map>) sqlSelectionMap; + final Map map = fetchParentSqlSelectionMap.get( root ); if ( map == null ) { fetchParentSqlSelectionMap.put( root, selectionMap = new HashMap<>() ); } @@ -167,31 +161,60 @@ public class SqlAstQueryPartProcessingStateImpl selectionMap = map; } } - final SqlSelection sqlSelection = selectionMap.get( expression ); - if ( sqlSelection != null ) { - return sqlSelection; - } } else { selectionMap = null; } + final int jdbcPosition; + final Object existingSelection; + if ( selectionMap != null ) { + existingSelection = selectionMap.get( expression ); + if ( existingSelection != null ) { + if ( existingSelection instanceof SqlSelection ) { + final SqlSelection sqlSelection = (SqlSelection) existingSelection; + if ( sqlSelection.getExpressionType() == expression.getExpressionType() ) { + return sqlSelection; + } + jdbcPosition = sqlSelection.getJdbcResultSetIndex(); + } + else { + final SqlSelection[] selections = (SqlSelection[]) existingSelection; + for ( SqlSelection sqlSelection : selections ) { + if ( sqlSelection.getExpressionType() == expression.getExpressionType() ) { + return sqlSelection; + } + } + jdbcPosition = selections[0].getJdbcResultSetIndex(); + } + } + else { + jdbcPosition = nextJdbcPosition++; + } + } + else { + jdbcPosition = nextJdbcPosition++; + existingSelection = null; + } + final boolean virtual = existingSelection != null; final SelectClause selectClause = ( (QuerySpec) queryPart ).getSelectClause(); final int valuesArrayPosition = selectClause.getSqlSelections().size(); final SqlSelection sqlSelection; if ( isTopLevel() ) { sqlSelection = expression.createDomainResultSqlSelection( - valuesArrayPosition + 1, + jdbcPosition, valuesArrayPosition, javaType, + virtual, typeConfiguration ); } else { sqlSelection = expression.createSqlSelection( - valuesArrayPosition + 1, + jdbcPosition, valuesArrayPosition, javaType, + virtual, typeConfiguration ); } @@ -199,7 +222,23 @@ public class SqlAstQueryPartProcessingStateImpl selectClause.addSqlSelection( sqlSelection ); if ( selectionMap != null ) { - selectionMap.put( expression, sqlSelection ); + if ( virtual ) { + final SqlSelection[] selections; + if ( existingSelection instanceof SqlSelection ) { + selections = new SqlSelection[2]; + selections[0] = (SqlSelection) existingSelection; + } + else { + final SqlSelection[] existingSelections = (SqlSelection[]) existingSelection; + selections = new SqlSelection[existingSelections.length + 1]; + System.arraycopy( existingSelections, 0, selections, 0, existingSelections.length ); + } + selections[selections.length - 1] = sqlSelection; + selectionMap.put( expression, selections ); + } + else { + selectionMap.put( expression, sqlSelection ); + } } return sqlSelection; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index 54271d45f9..669b0ec0a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -2917,7 +2917,6 @@ public abstract class AbstractSqlAstTranslator implemen for ( int i = 0; i < sqlSelectionsSize; i++ ) { syntheticSelectClause.addSqlSelection( new SqlSelectionImpl( - i + 1, i, new ColumnReference( queryGroupAlias, @@ -4591,6 +4590,9 @@ public abstract class AbstractSqlAstTranslator implemen int offset = 0; for ( int i = 0; i < size; i++ ) { final SqlSelection sqlSelection = sqlSelections.get( i ); + if ( sqlSelection.isVirtual() ) { + continue; + } if ( selectItemsToInline != null && selectItemsToInline.get( i ) ) { parameterRenderingMode = SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS; } @@ -4641,6 +4643,9 @@ public abstract class AbstractSqlAstTranslator implemen String separator = NO_SEPARATOR; for ( int i = 0; i < size; i++ ) { final SqlSelection sqlSelection = sqlSelections.get( i ); + if ( sqlSelection.isVirtual() ) { + continue; + } appendSql( separator ); if ( selectItemsToInline != null && selectItemsToInline.get( i ) ) { parameterRenderingMode = SqlAstNodeRenderingMode.INLINE_ALL_PARAMETERS; @@ -5332,7 +5337,6 @@ public abstract class AbstractSqlAstTranslator implemen columnNames.add( "sort_col_" + i ); sqlSelections.add( new SqlSelectionImpl( - sqlSelections.size() + 1, sqlSelections.size(), sortSpecification.getSortExpression() ) @@ -5646,7 +5650,6 @@ public abstract class AbstractSqlAstTranslator implemen for ( ColumnReference columnReference : columnReferences ) { lhsReferencesQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, columnReference ) @@ -5696,7 +5699,6 @@ public abstract class AbstractSqlAstTranslator implemen final QuerySpec existsQuery = new QuerySpec( false, 1 ); existsQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, new QueryLiteral<>( 1, getIntegerType() ) ) @@ -5749,7 +5751,6 @@ public abstract class AbstractSqlAstTranslator implemen existsQuery.setHavingClauseRestrictions( querySpec.getHavingClauseRestrictions() ); existsQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, new QueryLiteral<>( 1, getIntegerType() ) ) @@ -5786,7 +5787,6 @@ public abstract class AbstractSqlAstTranslator implemen countQuery.setHavingClauseRestrictions( querySpec.getHavingClauseRestrictions() ); countQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( - 1, 0, new SelfRenderingAggregateFunctionSqlAstExpression( "count", diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlExpressionResolver.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlExpressionResolver.java index d383308784..cc823f7df9 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlExpressionResolver.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlExpressionResolver.java @@ -8,12 +8,14 @@ package org.hibernate.sql.ast.spi; import java.util.function.Function; +import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectablePath; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.results.graph.FetchParent; +import org.hibernate.type.NullType; import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.spi.TypeConfiguration; @@ -42,41 +44,42 @@ public interface SqlExpressionResolver { * * @see #resolveSqlExpression */ - static ColumnReferenceKey createColumnReferenceKey(String tableExpression, String columnExpression) { - return new ColumnReferenceKey(tableExpression, new SelectablePath( columnExpression ) ); + static ColumnReferenceKey createColumnReferenceKey(String tableExpression, String columnExpression, JdbcMapping jdbcMapping) { + return createColumnReferenceKey( tableExpression, new SelectablePath( columnExpression ), jdbcMapping ); } /** * Helper for generating an expression key for a column reference. * * @see #resolveSqlExpression */ - static ColumnReferenceKey createColumnReferenceKey(TableReference tableReference, String columnExpression) { - return createColumnReferenceKey( tableReference, new SelectablePath( columnExpression ) ); + static ColumnReferenceKey createColumnReferenceKey(TableReference tableReference, String columnExpression, JdbcMapping jdbcMapping) { + return createColumnReferenceKey( tableReference, new SelectablePath( columnExpression ), jdbcMapping ); } - static ColumnReferenceKey createColumnReferenceKey(TableReference tableReference, SelectablePath selectablePath) { + static ColumnReferenceKey createColumnReferenceKey(TableReference tableReference, SelectablePath selectablePath, JdbcMapping jdbcMapping) { assert tableReference != null : "tableReference expected to be non-null"; assert selectablePath != null : "selectablePath expected to be non-null"; assert tableReference.getIdentificationVariable() != null : "tableReference#identificationVariable expected to be non-null"; final String qualifier = tableReference.getIdentificationVariable(); - return createColumnReferenceKey( qualifier, selectablePath ); + return createColumnReferenceKey( qualifier, selectablePath, jdbcMapping ); } - static ColumnReferenceKey createColumnReferenceKey(String qualifier, SelectablePath selectablePath) { + static ColumnReferenceKey createColumnReferenceKey(String qualifier, SelectablePath selectablePath, JdbcMapping jdbcMapping) { assert qualifier != null : "qualifier expected to be non-null"; assert selectablePath != null : "selectablePath expected to be non-null"; - return new ColumnReferenceKey( qualifier, selectablePath ); + assert jdbcMapping != null : "jdbcMapping expected to be non-null"; + return new ColumnReferenceKey( qualifier, selectablePath, jdbcMapping ); } static ColumnReferenceKey createColumnReferenceKey(String columnExpression) { assert columnExpression != null : "columnExpression expected to be non-null"; - return new ColumnReferenceKey( "", new SelectablePath( columnExpression ) ); + return createColumnReferenceKey( "", new SelectablePath( columnExpression ), NullType.INSTANCE ); } /** * Convenience form for creating a key from table expression and SelectableMapping */ static ColumnReferenceKey createColumnReferenceKey(String tableExpression, SelectableMapping selectable) { - return createColumnReferenceKey( tableExpression, selectable.getSelectablePath() ); + return createColumnReferenceKey( tableExpression, selectable.getSelectablePath(), selectable.getJdbcMapping() ); } /** @@ -85,7 +88,7 @@ public interface SqlExpressionResolver { static ColumnReferenceKey createColumnReferenceKey(TableReference tableReference, SelectableMapping selectable) { assert tableReference.containsAffectedTableName( selectable.getContainingTableExpression() ) : String.format( ROOT, "Expecting tables to match between TableReference (%s) and SelectableMapping (%s)", tableReference.getTableId(), selectable.getContainingTableExpression() ); - return createColumnReferenceKey( tableReference, selectable.getSelectablePath() ); + return createColumnReferenceKey( tableReference, selectable.getSelectablePath(), selectable.getJdbcMapping() ); } default Expression resolveSqlExpression(TableReference tableReference, SelectableMapping selectableMapping) { @@ -117,10 +120,12 @@ public interface SqlExpressionResolver { final class ColumnReferenceKey { private final String tableQualifier; private final SelectablePath selectablePath; + private final JdbcMapping jdbcMapping; - public ColumnReferenceKey(String tableQualifier, SelectablePath selectablePath) { + public ColumnReferenceKey(String tableQualifier, SelectablePath selectablePath, JdbcMapping jdbcMapping) { this.tableQualifier = tableQualifier; this.selectablePath = selectablePath; + this.jdbcMapping = jdbcMapping; } @Override @@ -132,18 +137,17 @@ public interface SqlExpressionResolver { return false; } - ColumnReferenceKey that = (ColumnReferenceKey) o; - - if ( !tableQualifier.equals( that.tableQualifier ) ) { - return false; - } - return selectablePath.equals( that.selectablePath ); + final ColumnReferenceKey that = (ColumnReferenceKey) o; + return tableQualifier.equals( that.tableQualifier ) + && selectablePath.equals( that.selectablePath ) + && jdbcMapping.equals( that.jdbcMapping ); } @Override public int hashCode() { int result = tableQualifier.hashCode(); result = 31 * result + selectablePath.hashCode(); + result = 31 * result + jdbcMapping.hashCode(); return result; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlSelection.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlSelection.java index ca4037574a..0740695e8d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlSelection.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlSelection.java @@ -56,6 +56,12 @@ public interface SqlSelection extends SqlAstNode { */ JdbcMappingContainer getExpressionType(); + /** + * Whether this is a virtual or a real selection item. + * Virtual selection items are not rendered into the SQL select clause. + */ + boolean isVirtual(); + void accept(SqlAstWalker sqlAstWalker); SqlSelection resolve(JdbcValuesMetadata jdbcResultsMetadata, SessionFactoryImplementor sessionFactory); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlSelectionProducer.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlSelectionProducer.java index e2b0e868a1..e4aef2b578 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlSelectionProducer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/SqlSelectionProducer.java @@ -21,10 +21,28 @@ public interface SqlSelectionProducer { * @param valuesArrayPosition The position in our {@linkplain RowProcessingState#getJdbcValue(SqlSelection) "current JDBC values array"} * @param javaType The descriptor for the Java type to read the value as * @param typeConfiguration The associated TypeConfiguration + * @deprecated Use {@link #createSqlSelection(int, int, JavaType, boolean, TypeConfiguration)} instead */ + @Deprecated(forRemoval = true) SqlSelection createSqlSelection( int jdbcPosition, int valuesArrayPosition, JavaType javaType, TypeConfiguration typeConfiguration); + + /** + * Create a SqlSelection for the given JDBC ResultSet position + * + * @param jdbcPosition The index position used to read values from JDBC + * @param valuesArrayPosition The position in our {@linkplain RowProcessingState#getJdbcValue(SqlSelection) "current JDBC values array"} + * @param javaType The descriptor for the Java type to read the value as + * @param virtual Whether the select is virtual or real. See {@link SqlSelection#isVirtual()} + * @param typeConfiguration The associated TypeConfiguration + */ + SqlSelection createSqlSelection( + int jdbcPosition, + int valuesArrayPosition, + JavaType javaType, + boolean virtual, + TypeConfiguration typeConfiguration); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Expression.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Expression.java index dbfcf91677..cef05335e0 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Expression.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/expression/Expression.java @@ -39,14 +39,41 @@ public interface Expression extends SqlAstNode, SqlSelectionProducer { jdbcPosition, valuesArrayPosition, javaType, - this + this, + false ); } + @Override + default SqlSelection createSqlSelection( + int jdbcPosition, + int valuesArrayPosition, + JavaType javaType, + boolean virtual, + TypeConfiguration typeConfiguration) { + return new SqlSelectionImpl( + jdbcPosition, + valuesArrayPosition, + javaType, + this, + virtual + ); + } + + @Deprecated(forRemoval = true) + default SqlSelection createDomainResultSqlSelection( + int jdbcPosition, + int valuesArrayPosition, + JavaType javaType, + TypeConfiguration typeConfiguration) { + return createDomainResultSqlSelection( jdbcPosition, valuesArrayPosition, javaType, false, typeConfiguration ); + } + default SqlSelection createDomainResultSqlSelection( int jdbcPosition, int valuesArrayPosition, JavaType javaType, + boolean virtual, TypeConfiguration typeConfiguration) { // Apply possible jdbc type wrapping final Expression expression; @@ -58,7 +85,7 @@ public interface Expression extends SqlAstNode, SqlSelectionProducer { expression = expressionType.getJdbcMapping( 0 ).getJdbcType().wrapTopLevelSelectionExpression( this ); } return expression == this - ? createSqlSelection( jdbcPosition, valuesArrayPosition, javaType, typeConfiguration ) - : new SqlSelectionImpl( jdbcPosition, valuesArrayPosition, expression ); + ? createSqlSelection( jdbcPosition, valuesArrayPosition, javaType, virtual, typeConfiguration ) + : new SqlSelectionImpl( jdbcPosition, valuesArrayPosition, expression, virtual ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java index 081d4b717b..2189dc6c32 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java @@ -58,6 +58,22 @@ public abstract class DelegatingTableGroup implements TableGroup { ); } + @Override + public SqlSelection createSqlSelection( + int jdbcPosition, + int valuesArrayPosition, + JavaType javaType, + boolean virtual, + TypeConfiguration typeConfiguration) { + return getTableGroup().createSqlSelection( + jdbcPosition, + valuesArrayPosition, + javaType, + virtual, + typeConfiguration + ); + } + @Override public TableReference getTableReference( NavigablePath navigablePath, diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java index 307ba1bb14..45ad8e8805 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/internal/AbstractJdbcParameter.java @@ -71,7 +71,8 @@ public abstract class AbstractJdbcParameter jdbcPosition, valuesArrayPosition, javaType, - this + this, + false ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResolvedSqlSelection.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResolvedSqlSelection.java index 4058f977c8..49758299de 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResolvedSqlSelection.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/ResolvedSqlSelection.java @@ -22,12 +22,20 @@ public class ResolvedSqlSelection extends SqlSelectionImpl { private final BasicType resolvedType; + public ResolvedSqlSelection( + int valuesArrayPosition, + Expression sqlExpression, + BasicType resolvedType) { + super( valuesArrayPosition + 1, valuesArrayPosition, null, sqlExpression, false ); + this.resolvedType = resolvedType; + } + public ResolvedSqlSelection( int jdbcPosition, int valuesArrayPosition, Expression sqlExpression, BasicType resolvedType) { - super( jdbcPosition, valuesArrayPosition, null, sqlExpression ); + super( jdbcPosition, valuesArrayPosition, null, sqlExpression, false ); this.resolvedType = resolvedType; } @@ -41,6 +49,11 @@ public class ResolvedSqlSelection extends SqlSelectionImpl { return resolvedType; } + @Override + public boolean isVirtual() { + return false; + } + @Override public SqlSelection resolve(JdbcValuesMetadata jdbcResultsMetadata, SessionFactoryImplementor sessionFactory) { return this; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowProcessingStateStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowProcessingStateStandardImpl.java index 45468a343b..2086ac4368 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowProcessingStateStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/RowProcessingStateStandardImpl.java @@ -116,7 +116,7 @@ public class RowProcessingStateStandardImpl extends BaseExecutionContext impleme @Override public Object getJdbcValue(int position) { - return jdbcValues.getCurrentRowValuesArray()[ position ]; + return jdbcValues.getCurrentRowValue( position ); } @Override @@ -130,6 +130,7 @@ public class RowProcessingStateStandardImpl extends BaseExecutionContext impleme @Override public void finishRowProcessing() { + jdbcValues.finishRowProcessing( this ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/SqlSelectionImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/SqlSelectionImpl.java index 3ebc7049aa..a8ef5ca35d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/SqlSelectionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/SqlSelectionImpl.java @@ -46,16 +46,31 @@ public class SqlSelectionImpl implements SqlSelection, SqlExpressionAccess { private final int valuesArrayPosition; private final Expression sqlExpression; private final JavaType jdbcJavaType; + private final boolean virtual; - public SqlSelectionImpl(int jdbcPosition, int valuesArrayPosition, Expression sqlExpression) { - this( jdbcPosition, valuesArrayPosition, null, sqlExpression ); + public SqlSelectionImpl(Expression sqlExpression) { + this( 0, -1, null, sqlExpression, false ); } - public SqlSelectionImpl(int jdbcPosition, int valuesArrayPosition, JavaType jdbcJavaType, Expression sqlExpression) { + public SqlSelectionImpl(int valuesArrayPosition, Expression sqlExpression) { + this( valuesArrayPosition + 1, valuesArrayPosition, null, sqlExpression, false ); + } + + public SqlSelectionImpl(int jdbcPosition, int valuesArrayPosition, Expression sqlExpression, boolean virtual) { + this( jdbcPosition, valuesArrayPosition, null, sqlExpression, virtual ); + } + + public SqlSelectionImpl( + int jdbcPosition, + int valuesArrayPosition, + JavaType jdbcJavaType, + Expression sqlExpression, + boolean virtual) { this.jdbcPosition = jdbcPosition; this.valuesArrayPosition = valuesArrayPosition; this.jdbcJavaType = jdbcJavaType; this.sqlExpression = sqlExpression; + this.virtual = virtual; } @Override @@ -87,6 +102,11 @@ public class SqlSelectionImpl implements SqlSelection, SqlExpressionAccess { return getExpression().getExpressionType(); } + @Override + public boolean isVirtual() { + return virtual; + } + @Override public Expression getSqlExpression() { return sqlExpression; @@ -126,11 +146,12 @@ public class SqlSelectionImpl implements SqlSelection, SqlExpressionAccess { final SqlSelection that = (SqlSelection) o; return jdbcPosition == that.getJdbcResultSetIndex() && valuesArrayPosition == that.getValuesArrayPosition() && - Objects.equals( sqlExpression, that.getExpression() ); + Objects.equals( sqlExpression, that.getExpression() ) && + virtual == that.isVirtual(); } @Override public int hashCode() { - return Objects.hash( jdbcPosition, valuesArrayPosition, sqlExpression ); + return Objects.hash( jdbcPosition, valuesArrayPosition, sqlExpression, virtual ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractJdbcValues.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractJdbcValues.java index 168cbfb4e1..e93a9b37dd 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractJdbcValues.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/AbstractJdbcValues.java @@ -6,8 +6,6 @@ */ package org.hibernate.sql.results.jdbc.internal; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.sql.results.caching.QueryCachePutManager; import org.hibernate.sql.results.jdbc.spi.JdbcValues; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; @@ -15,22 +13,10 @@ import org.hibernate.sql.results.jdbc.spi.RowProcessingState; * @author Steve Ebersole */ public abstract class AbstractJdbcValues implements JdbcValues { - private final QueryCachePutManager queryCachePutManager; - - public AbstractJdbcValues(QueryCachePutManager queryCachePutManager) { - if ( queryCachePutManager == null ) { - throw new IllegalArgumentException( "QueryCachePutManager cannot be null" ); - } - this.queryCachePutManager = queryCachePutManager; - } @Override public final boolean next(RowProcessingState rowProcessingState) { - final boolean hadRow = processNext( rowProcessingState ); - if ( hadRow ) { - queryCachePutManager.registerJdbcRow( getCurrentRowValuesArray() ); - } - return hadRow; + return processNext( rowProcessingState ); } protected abstract boolean processNext(RowProcessingState rowProcessingState); @@ -64,12 +50,4 @@ public abstract class AbstractJdbcValues implements JdbcValues { } protected abstract boolean processPosition(int position, RowProcessingState rowProcessingState); - - @Override - public final void finishUp(SharedSessionContractImplementor session) { - queryCachePutManager.finishUp( session ); - release(); - } - - protected abstract void release(); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesCacheHit.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesCacheHit.java index ef04cd1487..d4b464369b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesCacheHit.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesCacheHit.java @@ -8,9 +8,9 @@ package org.hibernate.sql.results.jdbc.internal; import java.util.List; +import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.sql.results.ResultsLogger; -import org.hibernate.sql.results.caching.internal.QueryCachePutManagerDisabledImpl; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; @@ -29,12 +29,6 @@ public class JdbcValuesCacheHit extends AbstractJdbcValues { private int position = -1; public JdbcValuesCacheHit(Object[][] cachedData, JdbcValuesMapping resolvedMapping) { - // if we have a cache hit we should not be writing back to the cache. - // its silly because the state would always be the same. - // - // well actually, there are times when we want to write values back to the cache even though we had a hit... - // the case is related to the domain-data cache - super( QueryCachePutManagerDisabledImpl.INSTANCE ); this.cachedData = cachedData; this.numberOfRows = cachedData.length; this.resolvedMapping = resolvedMapping; @@ -237,7 +231,19 @@ public class JdbcValuesCacheHit extends AbstractJdbcValues { } @Override - protected void release() { + public Object getCurrentRowValue(int valueIndex) { + if ( position >= numberOfRows ) { + return null; + } + return cachedData[position][valueIndex]; + } + + @Override + public void finishRowProcessing(RowProcessingState rowProcessingState) { + } + + @Override + public void finishUp(SharedSessionContractImplementor session) { cachedData = null; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java index 25aa666fd0..87b2c4300d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesResultSetImpl.java @@ -9,6 +9,7 @@ package org.hibernate.sql.results.jdbc.internal; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; +import java.util.BitSet; import org.hibernate.cache.spi.QueryKey; import org.hibernate.cache.spi.QueryResultsCache; @@ -32,11 +33,14 @@ import org.hibernate.sql.results.jdbc.spi.RowProcessingState; */ public class JdbcValuesResultSetImpl extends AbstractJdbcValues { + private final QueryCachePutManager queryCachePutManager; private final ResultSetAccess resultSetAccess; private final JdbcValuesMapping valuesMapping; private final ExecutionContext executionContext; private final SqlSelection[] sqlSelections; + private final SqlSelection[] eagerSqlSelections; + private final BitSet initializedIndexes; private final Object[] currentRowJdbcValues; public JdbcValuesResultSetImpl( @@ -47,15 +51,61 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues { JdbcValuesMapping valuesMapping, JdbcValuesMetadata metadataForCache, ExecutionContext executionContext) { - super( resolveQueryCachePutManager( executionContext, queryOptions, queryCacheKey, queryIdentifier, metadataForCache ) ); + this.queryCachePutManager = resolveQueryCachePutManager( + executionContext, + queryOptions, + queryCacheKey, + queryIdentifier, + metadataForCache + ); this.resultSetAccess = resultSetAccess; this.valuesMapping = valuesMapping; this.executionContext = executionContext; this.sqlSelections = valuesMapping.getSqlSelections().toArray( new SqlSelection[0] ); + this.eagerSqlSelections = extractEagerSqlSelections( sqlSelections ); + this.initializedIndexes = new BitSet( valuesMapping.getRowSize() ); this.currentRowJdbcValues = new Object[ valuesMapping.getRowSize() ]; } + /** + * Determine the selections which are eager i.e. safe to always extract. + * If a virtual selection exists, we must extract the value for that JDBC position lazily. + */ + private SqlSelection[] extractEagerSqlSelections(SqlSelection[] sqlSelections) { + BitSet lazyValuesPositions = null; + for ( int i = 0; i < sqlSelections.length; i++ ) { + final SqlSelection sqlSelection = sqlSelections[i]; + if ( sqlSelection.isVirtual() ) { + if ( lazyValuesPositions == null ) { + lazyValuesPositions = new BitSet(); + } + lazyValuesPositions.set( sqlSelection.getValuesArrayPosition() ); + // Find the one preceding selection that refers to the same JDBC position + // and treat that as virtual to do lazy extraction + for ( int j = 0; j < i; j++ ) { + if ( sqlSelections[j].getJdbcResultSetIndex() == sqlSelection.getJdbcResultSetIndex() ) { + // There can only be a single selection which also has to be non-virtual + assert !sqlSelections[j].isVirtual(); + lazyValuesPositions.set( sqlSelections[j].getValuesArrayPosition() ); + break; + } + } + } + } + if ( lazyValuesPositions == null ) { + return sqlSelections; + } + final SqlSelection[] eagerSqlSelections = new SqlSelection[sqlSelections.length - lazyValuesPositions.cardinality()]; + int i = 0; + for ( SqlSelection sqlSelection : sqlSelections ) { + if ( !lazyValuesPositions.get( sqlSelection.getValuesArrayPosition() ) ) { + eagerSqlSelections[i++] = sqlSelection; + } + } + return eagerSqlSelections; + } + private static QueryCachePutManager resolveQueryCachePutManager( ExecutionContext executionContext, QueryOptions queryOptions, @@ -257,7 +307,9 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues { private void readCurrentRowValues() { final ResultSet resultSet = resultSetAccess.getResultSet(); final SharedSessionContractImplementor session = executionContext.getSession(); - for ( final SqlSelection sqlSelection : sqlSelections ) { + initializedIndexes.clear(); + for ( final SqlSelection sqlSelection : eagerSqlSelections ) { + initializedIndexes.set( sqlSelection.getValuesArrayPosition() ); try { currentRowJdbcValues[ sqlSelection.getValuesArrayPosition() ] = sqlSelection.getJdbcValueExtractor().extract( resultSet, @@ -276,7 +328,8 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues { } @Override - protected void release() { + public final void finishUp(SharedSessionContractImplementor session) { + queryCachePutManager.finishUp( session ); resultSetAccess.release(); } @@ -290,6 +343,34 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues { return currentRowJdbcValues; } + @Override + public void finishRowProcessing(RowProcessingState rowProcessingState) { + queryCachePutManager.registerJdbcRow( currentRowJdbcValues ); + } + + @Override + public Object getCurrentRowValue(int valueIndex) { + if ( !initializedIndexes.get( valueIndex ) ) { + initializedIndexes.set( valueIndex ); + final SqlSelection sqlSelection = sqlSelections[valueIndex]; + try { + currentRowJdbcValues[valueIndex] = sqlSelection.getJdbcValueExtractor().extract( + resultSetAccess.getResultSet(), + sqlSelection.getJdbcResultSetIndex(), + executionContext.getSession() + ); + } + catch ( SQLException e ) { + // do not want to wrap in ExecutionException here + throw executionContext.getSession().getJdbcServices().getSqlExceptionHelper().convert( + e, + "Could not extract column [" + sqlSelection.getJdbcResultSetIndex() + "] from JDBC ResultSet" + ); + } + } + return currentRowJdbcValues[valueIndex]; + } + @Override public void setFetchSize(int fetchSize) { try { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/spi/JdbcValues.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/spi/JdbcValues.java index 9f1dbcb382..381db7261b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/spi/JdbcValues.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/spi/JdbcValues.java @@ -21,7 +21,7 @@ public interface JdbcValues { /** * Advances the "cursor position" and returns a boolean indicating whether - * there is a row available to read via {@link #getCurrentRowValuesArray()}. + * there is a row available to read via {@link #getCurrentRowValue(int)}. * * @return {@code true} if there are results */ @@ -29,7 +29,7 @@ public interface JdbcValues { /** * Advances the "cursor position" in reverse and returns a boolean indicating whether - * there is a row available to read via {@link #getCurrentRowValuesArray()}. + * there is a row available to read via {@link #getCurrentRowValue(int)}. * * @return {@code true} if there are results available */ @@ -37,7 +37,7 @@ public interface JdbcValues { /** * Advances the "cursor position" the indicated number of rows and returns a boolean - * indicating whether there is a row available to read via {@link #getCurrentRowValuesArray()}. + * indicating whether there is a row available to read via {@link #getCurrentRowValue(int)}. * * @param numberOfRows The number of rows to advance. This can also be negative meaning to * move in reverse @@ -74,6 +74,17 @@ public interface JdbcValues { */ Object[] getCurrentRowValuesArray(); + /** + * Get the JDBC value at the given index for the row currently positioned at within + * this source. + * + * @return The current row's JDBC values, or {@code null} if the position + * is beyond the end of the available results. + */ + Object getCurrentRowValue(int valueIndex); + + void finishRowProcessing(RowProcessingState rowProcessingState); + /** * Give implementations a chance to finish processing */ diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/PolymorphicJsonTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/PolymorphicJsonTests.java new file mode 100644 index 0000000000..849760e997 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/PolymorphicJsonTests.java @@ -0,0 +1,170 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.mapping.basic; + +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.type.SqlTypes; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; + +/** + * @author Christian Beikov + */ +@DomainModel(annotatedClasses = { + PolymorphicJsonTests.EntityWithJson.class, + PolymorphicJsonTests.EntityWithJsonA.class, + PolymorphicJsonTests.EntityWithJsonB.class +}) +@SessionFactory +public abstract class PolymorphicJsonTests { + + @ServiceRegistry(settings = @Setting(name = AvailableSettings.JSON_FORMAT_MAPPER, value = "jsonb")) + public static class JsonB extends PolymorphicJsonTests { + + public JsonB() { + } + } + + @ServiceRegistry(settings = @Setting(name = AvailableSettings.JSON_FORMAT_MAPPER, value = "jackson")) + public static class Jackson extends PolymorphicJsonTests { + + public Jackson() { + } + } + + @BeforeEach + public void setup(SessionFactoryScope scope) { + scope.inTransaction( + (session) -> { + session.persist( new EntityWithJsonA( 1, new PropertyA( "e1" ) ) ); + session.persist( new EntityWithJsonB( 2, new PropertyB( 123 ) ) ); + } + ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + (session) -> { + session.remove( session.find( EntityWithJson.class, 1 ) ); + session.remove( session.find( EntityWithJson.class, 2 ) ); + } + ); + } + + @Test + public void verifyReadWorks(SessionFactoryScope scope) { + scope.inTransaction( + (session) -> { + EntityWithJson entityWithJsonA = session.find( EntityWithJson.class, 1 ); + EntityWithJson entityWithJsonB = session.find( EntityWithJson.class, 2 ); + assertThat( entityWithJsonA, instanceOf( EntityWithJsonA.class ) ); + assertThat( entityWithJsonB, instanceOf( EntityWithJsonB.class ) ); + assertThat( ( (EntityWithJsonA) entityWithJsonA ).property.value, is( "e1" ) ); + assertThat( ( (EntityWithJsonB) entityWithJsonB ).property.value, is( 123 ) ); + } + ); + } + + @Entity(name = "EntityWithJson") + @Table(name = "EntityWithJson") + public static abstract class EntityWithJson { + @Id + private Integer id; + + public EntityWithJson() { + } + + public EntityWithJson(Integer id) { + this.id = id; + } + } + + @Entity(name = "EntityWithJsonA") + public static class EntityWithJsonA extends EntityWithJson { + + @JdbcTypeCode( SqlTypes.JSON ) + private PropertyA property; + + public EntityWithJsonA() { + } + + public EntityWithJsonA(Integer id, PropertyA property) { + super( id ); + this.property = property; + } + } + + @Entity(name = "EntityWithJsonB") + public static class EntityWithJsonB extends EntityWithJson { + + @JdbcTypeCode( SqlTypes.JSON ) + private PropertyB property; + + public EntityWithJsonB() { + } + + public EntityWithJsonB(Integer id, PropertyB property) { + super( id ); + this.property = property; + } + } + + public static class PropertyA { + private String value; + + public PropertyA() { + } + + public PropertyA(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + public static class PropertyB { + private int value; + + public PropertyB() { + } + + public PropertyB(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + } +}