diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/AbstractSqmInsertStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/AbstractSqmInsertStatement.java index d1747a1b2eac..482f04daba0e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/AbstractSqmInsertStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/AbstractSqmInsertStatement.java @@ -72,6 +72,10 @@ protected List> copyInsertionTargetPaths(SqmCopyContext context) { } } + void setConflictClause(SqmConflictClause conflictClause) { + this.conflictClause = conflictClause; + } + protected void verifyInsertTypesMatch( List> insertionTargetPaths, List> expressions) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java index 7ed156101a8b..91e72de7f496 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmConflictClause.java @@ -53,7 +53,7 @@ private SqmConflictClause( this.insertStatement = insertStatement; this.excludedRoot = excludedRoot; this.constraintName = constraintName; - this.constraintPaths = Collections.unmodifiableList( constraintPaths ); + this.constraintPaths = constraintPaths == null ? null : Collections.unmodifiableList( constraintPaths ); this.updateAction = updateAction; } @@ -156,7 +156,7 @@ public SqmConflictClause copy(SqmCopyContext context) { insertStatement.copy( context ), excludedRoot.copy( context ), constraintName, - copyOf( constraintPaths, context ), + constraintPaths == null ? null : copyOf( constraintPaths, context ), updateAction == null ? null : updateAction.copy( context ) ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java index b1880dd02388..3afdf8164b93 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertSelectStatement.java @@ -82,19 +82,26 @@ public SqmInsertSelectStatement copy(SqmCopyContext context) { if ( existing != null ) { return existing; } - return context.registerCopy( - this, - new SqmInsertSelectStatement<>( - nodeBuilder(), - context.getQuerySource() == null ? getQuerySource() : context.getQuerySource(), - copyParameters( context ), - copyCteStatements( context ), - getTarget().copy( context ), - copyInsertionTargetPaths( context ), - getConflictClause() == null ? null : getConflictClause().copy( context ), - selectQueryPart.copy( context ) - ) + final SqmInsertSelectStatement sqmInsertSelectStatementCopy = new SqmInsertSelectStatement<>( + nodeBuilder(), + context.getQuerySource() == null ? getQuerySource() : context.getQuerySource(), + copyParameters( context ), + copyCteStatements( context ), + getTarget().copy( context ), + null, + null, + selectQueryPart.copy( context ) ); + + context.registerCopy( this, sqmInsertSelectStatementCopy ); + + sqmInsertSelectStatementCopy.setInsertionTargetPaths( copyInsertionTargetPaths( context ) ); + + if ( getConflictClause() != null ) { + sqmInsertSelectStatementCopy.setConflictClause( getConflictClause().copy( context ) ); + } + + return sqmInsertSelectStatementCopy; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java index c46c9deb4b64..e13c1aa06cb8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/insert/SqmInsertValuesStatement.java @@ -87,7 +87,8 @@ public SqmInsertValuesStatement copy(SqmCopyContext context) { valuesList.add( sqmValues.copy( context ) ); } } - return context.registerCopy( + + final SqmInsertValuesStatement sqmInsertValuesStatementCopy = context.registerCopy( this, new SqmInsertValuesStatement<>( nodeBuilder(), @@ -96,10 +97,16 @@ public SqmInsertValuesStatement copy(SqmCopyContext context) { copyCteStatements( context ), getTarget().copy( context ), copyInsertionTargetPaths( context ), - getConflictClause() == null ? null : getConflictClause().copy( context ), + null, valuesList ) ); + + if ( getConflictClause() != null ) { + sqmInsertValuesStatementCopy.setConflictClause( getConflictClause().copy( context ) ); + } + + return sqmInsertValuesStatementCopy; } public SqmInsertValuesStatement copyWithoutValues(SqmCopyContext context) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tuple/internal/AnonymousTupleTableGroupProducer.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tuple/internal/AnonymousTupleTableGroupProducer.java index 8333c3274f3a..3e770e4a7bbf 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tuple/internal/AnonymousTupleTableGroupProducer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tuple/internal/AnonymousTupleTableGroupProducer.java @@ -66,6 +66,8 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map private final JavaType javaTypeDescriptor; private final Map modelParts; private final Set compatibleTableExpressions; + private final SqlTypedMapping[] sqlTypedMappings; + private final int jdbcTypeCount; public AnonymousTupleTableGroupProducer( AnonymousTupleType tupleType, @@ -75,6 +77,7 @@ public AnonymousTupleTableGroupProducer( this.aliasStem = aliasStem; this.javaTypeDescriptor = tupleType.getExpressibleJavaType(); final Set compatibleTableExpressions = new HashSet<>(); + this.sqlTypedMappings = sqlTypedMappings; // The empty table expression is the default for derived model parts compatibleTableExpressions.add( "" ); @@ -127,6 +130,7 @@ else if ( sqlTypedMappings[selectionIndex] instanceof SelectableMapping selectab } this.modelParts = modelParts; this.compatibleTableExpressions = compatibleTableExpressions; + jdbcTypeCount = selectionIndex; } private ModelPart getModelPart(TableGroup tableGroup) { @@ -401,11 +405,16 @@ public int forEachDisassembledJdbcValue( @Override public JdbcMapping getJdbcMapping(int index) { - throw new UnsupportedOperationException( "Not yet implemented" ); + return sqlTypedMappings[index].getJdbcMapping(); } @Override public int forEachJdbcType(int offset, IndexedConsumer action) { throw new UnsupportedOperationException( "Not yet implemented" ); } + + @Override + public int getJdbcTypeCount() { + return jdbcTypeCount; + } } 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 40d511dad00f..d140524d8bd0 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 @@ -6738,8 +6738,8 @@ private int getSortSelectionIndex(QuerySpec querySpec, SortSpecification sortSpe private boolean isFetchFirstRowOnly(QueryPart queryPart) { return queryPart.getFetchClauseType() == FetchClauseType.ROWS_ONLY - && queryPart.getFetchClauseExpression() instanceof QueryLiteral queryLiteral - && Integer.valueOf( 1 ).equals( queryLiteral.getLiteralValue() ); + && queryPart.getFetchClauseExpression() != null + && Integer.valueOf( 1 ).equals( getLiteralValue( queryPart.getFetchClauseExpression() ) ); } private SelectStatement stripToSelectClause(SelectStatement statement) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java new file mode 100644 index 000000000000..5df94e23a97d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/InsertConflictWithCriteriaCopyTreeEnabledTests.java @@ -0,0 +1,92 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.query.hql; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Tuple; +import org.hibernate.cfg.QuerySettings; +import org.hibernate.query.criteria.HibernateCriteriaBuilder; +import org.hibernate.query.criteria.JpaCriteriaInsertSelect; +import org.hibernate.query.criteria.JpaCriteriaInsertValues; +import org.hibernate.query.criteria.JpaCriteriaQuery; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +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.Test; + + +@DomainModel( + annotatedClasses = { + InsertConflictWithCriteriaCopyTreeEnabledTests.TestEntity.class, + InsertConflictWithCriteriaCopyTreeEnabledTests.AnotherTestEntity.class, + } +) +@ServiceRegistry( + settings = {@Setting(name = QuerySettings.CRITERIA_COPY_TREE, value = "true")} +) +@SessionFactory +@JiraKey("HHH-19314") +public class InsertConflictWithCriteriaCopyTreeEnabledTests { + + @Test + void createCriteriaInsertValuesTest(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); + + JpaCriteriaInsertValues insertIntoItem = cb + .createCriteriaInsertValues( TestEntity.class ); + insertIntoItem.setInsertionTargetPaths( insertIntoItem.getTarget().get( "id" ) ); + insertIntoItem.values( cb.values( cb.value( 1L ) ) ); + insertIntoItem.onConflict().onConflictDoNothing(); + + session.createMutationQuery( insertIntoItem ).executeUpdate(); + } + ); + } + + @Test + void createCriteriaInsertSelectTest(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); + + JpaCriteriaInsertSelect insertIntoItem = cb + .createCriteriaInsertSelect( TestEntity.class ); + insertIntoItem.setInsertionTargetPaths( insertIntoItem.getTarget().get( "id" ) ); + + JpaCriteriaQuery cq = cb.createQuery( Tuple.class ); + cq.select( cb.tuple( cb.literal( 1 ) ) ); + cq.fetch( 1 ); + insertIntoItem.select( cq ); + insertIntoItem.onConflict().onConflictDoNothing(); + + session.createMutationQuery( insertIntoItem ).executeUpdate(); + } + ); + } + + @Entity(name = "TestEntity") + public static class TestEntity { + @Id + private Long id; + + private String name; + + } + + @Entity(name = "AnotherTestEntity") + public static class AnotherTestEntity { + @Id + private Long id; + + private String name; + + } +}