diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c97654dc0..468fc53ff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,9 @@ None yet ### Bug fixes -None yet +* Fix missing cascading deletes for deletes that contain joins or CTEs +* Fix some issues with PostgreSQL and Oracle multiset SQL rendering with Hibernate ORM 6+ +* Fix returning clause rendering for PostgreSQL with Hibernate ORM 6+ ### Backwards-incompatible changes diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/ResolvingQueryGenerator.java b/core/impl/src/main/java/com/blazebit/persistence/impl/ResolvingQueryGenerator.java index 42bf21cfd5..de2842bff7 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/ResolvingQueryGenerator.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/ResolvingQueryGenerator.java @@ -950,7 +950,7 @@ private void renderEquality(Expression left, Expression right, boolean negated, rewriteToIdParam(right); } } else { - if (expressionToSplit == null || dbmsDialect.supportsAnsiRowValueConstructor() || !(left instanceof ParameterExpression) && !(right instanceof ParameterExpression)) { + if (expressionToSplit == null || !(left instanceof ParameterExpression) && !(right instanceof ParameterExpression)) { left.accept(this); sb.append(operator); if (quantifier != PredicateQuantifier.ONE) { diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/function/tostringjson/OracleToStringJsonFunction.java b/core/impl/src/main/java/com/blazebit/persistence/impl/function/tostringjson/OracleToStringJsonFunction.java index c2e44130a2..40c1fe5fb0 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/function/tostringjson/OracleToStringJsonFunction.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/function/tostringjson/OracleToStringJsonFunction.java @@ -38,8 +38,7 @@ public void render(FunctionRenderContext context, String[] fields, String[] sele context.addChunk(postChunk); context.addChunk(subquery.substring(fromIndex)); } else { - int limitIndex = SqlUtils.indexOfLimit(subquery, orderByIndex); - if (limitIndex == -1) { + if (SqlUtils.indexOfLimit(subquery, orderByIndex) == -1 && SqlUtils.indexOfFetchFirst(subquery, orderByIndex) == -1) { context.addChunk(preChunk); StringBuilder sb = new StringBuilder(fromIndex); diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/function/tostringjson/PostgreSQLToStringJsonFunction.java b/core/impl/src/main/java/com/blazebit/persistence/impl/function/tostringjson/PostgreSQLToStringJsonFunction.java index 8a0fefca71..dde6fc1696 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/function/tostringjson/PostgreSQLToStringJsonFunction.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/function/tostringjson/PostgreSQLToStringJsonFunction.java @@ -26,8 +26,7 @@ public void render(FunctionRenderContext context, String[] fields, String[] sele context.addChunk("))"); context.addChunk(subquery.substring(fromIndex)); } else { - int limitIndex = SqlUtils.indexOfLimit(subquery, orderByIndex); - if (limitIndex == -1) { + if (SqlUtils.indexOfLimit(subquery, orderByIndex) == -1 && SqlUtils.indexOfFetchFirst(subquery, orderByIndex) == -1) { renderJsonObjectArguments(context, fields, selectItemExpressions); context.addChunk("))"); context.addChunk(" OVER ("); diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/function/tostringxml/PostgreSQLToStringXmlFunction.java b/core/impl/src/main/java/com/blazebit/persistence/impl/function/tostringxml/PostgreSQLToStringXmlFunction.java index d88768c7c5..df7851f706 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/function/tostringxml/PostgreSQLToStringXmlFunction.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/function/tostringxml/PostgreSQLToStringXmlFunction.java @@ -26,8 +26,7 @@ public void render(FunctionRenderContext context, String[] fields, String[] sele context.addChunk("))"); context.addChunk(subquery.substring(fromIndex)); } else { - int limitIndex = SqlUtils.indexOfLimit(subquery, orderByIndex); - if (limitIndex == -1) { + if (SqlUtils.indexOfLimit(subquery, orderByIndex) == -1 && SqlUtils.indexOfFetchFirst(subquery, orderByIndex) == -1) { renderXmlElementArguments(context, fields, selectItemExpressions); context.addChunk("))"); context.addChunk(" OVER ("); diff --git a/core/testsuite-hibernate/pom.xml b/core/testsuite-hibernate/pom.xml index 9197e9d580..69687dfbbe 100644 --- a/core/testsuite-hibernate/pom.xml +++ b/core/testsuite-hibernate/pom.xml @@ -13,7 +13,7 @@ com.blazebit.persistence.core.testsuite.hibernate - com.blazebit.persistence.testsuite + com.blazebit.persistence.testsuite.hibernate 0 @@ -634,5 +634,20 @@ + + + eclipselink + + + + maven-compiler-plugin + + true + true + + + + + diff --git a/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate6/entity/IntegerProperty.java b/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate/entity/IntegerProperty.java similarity index 93% rename from core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate6/entity/IntegerProperty.java rename to core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate/entity/IntegerProperty.java index fb1590cbb2..80b894d573 100644 --- a/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate6/entity/IntegerProperty.java +++ b/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate/entity/IntegerProperty.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Blazebit */ -package com.blazebit.persistence.testsuite.hibernate6.entity; +package com.blazebit.persistence.testsuite.hibernate.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate6/entity/Property.java b/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate/entity/Property.java similarity index 69% rename from core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate6/entity/Property.java rename to core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate/entity/Property.java index 0642001176..f09bf115ff 100644 --- a/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate6/entity/Property.java +++ b/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate/entity/Property.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Blazebit */ -package com.blazebit.persistence.testsuite.hibernate6.entity; +package com.blazebit.persistence.testsuite.hibernate.entity; public interface Property { diff --git a/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate6/entity/PropertyHolder.java b/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate/entity/PropertyHolder.java similarity index 95% rename from core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate6/entity/PropertyHolder.java rename to core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate/entity/PropertyHolder.java index ce28991a94..2a0fa10966 100644 --- a/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate6/entity/PropertyHolder.java +++ b/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate/entity/PropertyHolder.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Blazebit */ -package com.blazebit.persistence.testsuite.hibernate6.entity; +package com.blazebit.persistence.testsuite.hibernate.entity; import jakarta.persistence.Column; import jakarta.persistence.DiscriminatorType; diff --git a/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate6/entity/StringProperty.java b/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate/entity/StringProperty.java similarity index 93% rename from core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate6/entity/StringProperty.java rename to core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate/entity/StringProperty.java index 232b1b7fbc..fc98840bd5 100644 --- a/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate6/entity/StringProperty.java +++ b/core/testsuite-hibernate/src/main/java/com/blazebit/persistence/testsuite/hibernate/entity/StringProperty.java @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright Blazebit */ -package com.blazebit.persistence.testsuite.hibernate6.entity; +package com.blazebit.persistence.testsuite.hibernate.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/core/testsuite-hibernate/src/test/java/com/blazebit/persistence/testsuite/hibernate/AnyMappingTest.java b/core/testsuite-hibernate/src/test/java/com/blazebit/persistence/testsuite/hibernate/AnyMappingTest.java index 46726c751d..f9a6e80c1e 100644 --- a/core/testsuite-hibernate/src/test/java/com/blazebit/persistence/testsuite/hibernate/AnyMappingTest.java +++ b/core/testsuite-hibernate/src/test/java/com/blazebit/persistence/testsuite/hibernate/AnyMappingTest.java @@ -6,7 +6,7 @@ import com.blazebit.persistence.CriteriaBuilder; import com.blazebit.persistence.testsuite.AbstractCoreTest; -import com.blazebit.persistence.testsuite.hibernate6.entity.PropertyHolder; +import com.blazebit.persistence.testsuite.hibernate.entity.PropertyHolder; import org.junit.jupiter.api.Test; import static org.junit.Assert.assertTrue; diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/update/remove/cascade/AbstractEntityViewRemoveDocumentTest.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/update/remove/cascade/AbstractEntityViewRemoveDocumentTest.java index f5b2a4a3a0..8160a3e061 100644 --- a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/update/remove/cascade/AbstractEntityViewRemoveDocumentTest.java +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/update/remove/cascade/AbstractEntityViewRemoveDocumentTest.java @@ -205,6 +205,9 @@ protected AssertStatementBuilder deleteDocumentOwned(AssertStatementBuilder buil .assertDelete().forRelation(Document.class, "peopleListBag").and() .assertDelete().forRelation(Document.class, "peopleCollectionBag").and(); } + if (isQueryStrategy() && !simpleDelete && dbmsDialect.supportsModificationQueryInWithClause()) { + return builder; + } if (supportsNestedEmbeddables()) { builder .assertDelete().forRelation(Document.class, "nameContainerMap").and() @@ -228,7 +231,9 @@ protected AssertStatementBuilder deletePersonOwned(AssertStatementBuilder builde .assertDelete().forRelation(Person.class, "favoriteDocuments").and() ; } - + if (isQueryStrategy() && !simpleDelete && dbmsDialect.supportsModificationQueryInWithClause()) { + return builder; + } return builder .assertDelete().forRelation(Person.class, "localized").and() ; diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/update/subview/inverse/onetoone/entity/EntityViewUpdateSubviewInverseOneToOneEntityTest.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/update/subview/inverse/onetoone/entity/EntityViewUpdateSubviewInverseOneToOneEntityTest.java index 7f72397d45..e9e67a94f3 100644 --- a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/update/subview/inverse/onetoone/entity/EntityViewUpdateSubviewInverseOneToOneEntityTest.java +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/update/subview/inverse/onetoone/entity/EntityViewUpdateSubviewInverseOneToOneEntityTest.java @@ -13,6 +13,7 @@ import com.blazebit.persistence.testsuite.base.jpa.assertion.AssertStatementBuilder; import com.blazebit.persistence.testsuite.base.jpa.category.NoEclipselink; +import com.blazebit.persistence.testsuite.base.jpa.category.NoHibernate; import com.blazebit.persistence.testsuite.entity.Document; import com.blazebit.persistence.testsuite.entity.DocumentForSimpleOneToOne; import com.blazebit.persistence.testsuite.entity.DocumentInfoSimple; @@ -32,7 +33,7 @@ */ @RunWith(Parameterized.class) // NOTE: Hibernate has a bug when selecting inverse one-to-ones: https://hibernate.atlassian.net/browse/HHH-12885 -@Category({ NoEclipselink.class}) +@Category({ NoHibernate.class, NoEclipselink.class}) public class EntityViewUpdateSubviewInverseOneToOneEntityTest extends AbstractEntityViewUpdateTest { private DocumentForSimpleOneToOne doc1; diff --git a/integration/hibernate6-base/src/main/java/com/blazebit/persistence/integration/hibernate/base/HibernateExtendedQuerySupport.java b/integration/hibernate6-base/src/main/java/com/blazebit/persistence/integration/hibernate/base/HibernateExtendedQuerySupport.java index 31a9e8d1e9..3d0ef26ffb 100644 --- a/integration/hibernate6-base/src/main/java/com/blazebit/persistence/integration/hibernate/base/HibernateExtendedQuerySupport.java +++ b/integration/hibernate6-base/src/main/java/com/blazebit/persistence/integration/hibernate/base/HibernateExtendedQuerySupport.java @@ -21,17 +21,23 @@ import org.hibernate.HibernateException; import org.hibernate.NonUniqueResultException; import org.hibernate.ScrollMode; +import org.hibernate.action.internal.BulkOperationCleanupAction; +import org.hibernate.dialect.DmlTargetColumnQualifierSupport; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.engine.spi.SubselectFetch; import org.hibernate.internal.FilterJdbcParameter; +import org.hibernate.internal.util.MutableObject; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.BoundedConcurrentHashMap; import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.MappingModelExpressible; +import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; +import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.IllegalQueryOperationException; import org.hibernate.query.TupleTransformer; @@ -58,6 +64,7 @@ import org.hibernate.query.sqm.sql.SqmTranslation; import org.hibernate.query.sqm.sql.SqmTranslator; import org.hibernate.query.sqm.sql.SqmTranslatorFactory; +import org.hibernate.query.sqm.tree.SqmDmlStatement; import org.hibernate.query.sqm.tree.SqmStatement; import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement; import org.hibernate.query.sqm.tree.expression.SqmParameter; @@ -81,12 +88,16 @@ import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.delete.DeleteStatement; +import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.from.CollectionTableGroup; import org.hibernate.sql.ast.tree.from.LazyTableGroup; +import org.hibernate.sql.ast.tree.from.MutatingTableReferenceGroupWrapper; import org.hibernate.sql.ast.tree.from.NamedTableReference; import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.insert.InsertSelectStatement; import org.hibernate.sql.ast.tree.insert.InsertStatement; +import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate; import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.select.QueryGroup; import org.hibernate.sql.ast.tree.select.QueryPart; @@ -108,6 +119,7 @@ import org.hibernate.sql.results.internal.RowTransformerSingularReturnImpl; import org.hibernate.sql.results.internal.RowTransformerStandardImpl; import org.hibernate.sql.results.internal.RowTransformerTupleTransformerAdapter; +import org.hibernate.sql.results.internal.SqlSelectionImpl; import org.hibernate.sql.results.internal.TupleMetadata; import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping; import org.hibernate.sql.results.spi.ListResultsConsumer; @@ -131,6 +143,7 @@ import java.util.Spliterator; import java.util.Spliterators; import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.logging.Logger; import java.util.stream.Stream; @@ -228,43 +241,205 @@ public boolean getSqlContainsLimit() { @Override public List getCascadingDeleteSql(EntityManager em, Query query) { - SessionFactoryImplementor sfi = em.unwrap(SessionImplementor.class).getSessionFactory(); QuerySqmImpl hqlQuery = query.unwrap(QuerySqmImpl.class); if (hqlQuery.getSqmStatement() instanceof SqmDeleteStatement) { - SqmDeleteStatement deleteStatement = (SqmDeleteStatement) hqlQuery.getSqmStatement(); - String mutatingEntityName = deleteStatement.getTarget().getModel().getHibernateEntityName(); - EntityMappingType entityDescriptor = sfi.getMappingMetamodel().getEntityDescriptor(mutatingEntityName); - SqlAstTranslatorFactory sqlAstTranslatorFactory = sfi.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory(); - List deleteSqls = new ArrayList<>(); - entityDescriptor.visitConstraintOrderedTables( - (tableExpression, tableKeyColumnsVisitationSupplier) -> { - - // final TableReference targetTableReference = new TableReference( - // tableExpression, - // null, - // false, - // sfi - // ); - - final Predicate matchingIdsPredicate = null;//new InSubQueryPredicate(); - // matchingIdsPredicateProducer.produceRestriction( - // ids, - // entityDescriptor, - // targetTableReference, - // tableKeyColumnsVisitationSupplier, - // query - // ); - - // final SqlAstDeleteTranslator sqlAstTranslator = sqlAstTranslatorFactory.buildDeleteTranslator( sfi ); - // final JdbcDelete jdbcOperation = sqlAstTranslator.translate( new DeleteStatement( targetTableReference, matchingIdsPredicate ) ); - } - ); + List deletes = getCollectionTableDeletes( hqlQuery ).deletes; + + List deleteSqls = new ArrayList<>( deletes.size() ); + for ( JdbcOperationQueryMutation delete : deletes ) { + deleteSqls.add( delete.getSqlString() ); + } return deleteSqls; } return Collections.EMPTY_LIST; } + private static class CollectionTableDeleteInfo { + private final List deletes; + private final JdbcParameterBindings parameterBindings; + + public CollectionTableDeleteInfo( + List deletes, + JdbcParameterBindings parameterBindings) { + this.deletes = deletes; + this.parameterBindings = parameterBindings; + } + } + + private static CollectionTableDeleteInfo getCollectionTableDeletes(QuerySqmImpl hqlQuery) { + SessionFactoryImplementor sfi = hqlQuery.getSessionFactory(); + SqmDeleteStatement deleteStatement = (SqmDeleteStatement) hqlQuery.getSqmStatement(); + String mutatingEntityName = deleteStatement.getTarget().getModel().getHibernateEntityName(); + EntityMappingType entityDescriptor = sfi.getMappingMetamodel().getEntityDescriptor(mutatingEntityName); + + CacheableSqmInterpretation sqmInterpretation = buildQueryPlan( hqlQuery ); + + Map, Map, List>> jdbcParamsXref = SqmUtil.generateJdbcParamsXref( + hqlQuery.getDomainParameterXref(), + sqmInterpretation.getSqmTranslation()::getJdbcParamsBySqmParam + ); + + final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings( + hqlQuery.getQueryParameterBindings(), + hqlQuery.getDomainParameterXref(), + jdbcParamsXref, + sfi.getRuntimeMetamodels().getMappingMetamodel(), + sqmInterpretation.getSqmTranslation().getFromClauseAccess()::findTableGroup, + new SqmParameterMappingModelResolutionAccess() { + @Override @SuppressWarnings("unchecked") + public MappingModelExpressible getResolvedMappingModelType(SqmParameter parameter) { + return (MappingModelExpressible) sqmInterpretation.getSqmTranslation().getSqmParameterMappingModelTypeResolutions().get(parameter); + } + }, + hqlQuery.getSession() + ); + DeleteStatement sqlAst = (DeleteStatement) sqmInterpretation.getSqmTranslation().getSqlAst(); + final boolean missingRestriction = sqlAst.getRestriction() == null; + return new CollectionTableDeleteInfo( getCollectionTableDeletes( + entityDescriptor, + (tableReference, attributeMapping) -> { + final TableGroup collectionTableGroup = new MutatingTableReferenceGroupWrapper( + new NavigablePath( attributeMapping.getRootPathName() ), + attributeMapping, + (NamedTableReference) tableReference + ); + + final MutableObject additionalPredicate = new MutableObject<>(); + attributeMapping.applyBaseRestrictions( + p -> additionalPredicate.set( Predicate.combinePredicates( additionalPredicate.get(), p ) ), + collectionTableGroup, + sfi.getJdbcServices().getDialect().getDmlTargetColumnQualifierSupport() == DmlTargetColumnQualifierSupport.TABLE_ALIAS, + hqlQuery.getSession().getLoadQueryInfluencers().getEnabledFilters(), + false, + null, + null + ); + + if ( missingRestriction ) { + return additionalPredicate.get(); + } + + final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor(); + final Expression fkColumnExpression = MappingModelCreationHelper.buildColumnReferenceExpression( + collectionTableGroup, + fkDescriptor.getKeyPart(), + null, + sfi + ); + + final QuerySpec matchingIdSubQuery = new QuerySpec( false ); + + final MutatingTableReferenceGroupWrapper tableGroup = new MutatingTableReferenceGroupWrapper( + new NavigablePath( attributeMapping.getRootPathName() ), + attributeMapping, + sqlAst.getTargetTable() + ); + final Expression fkTargetColumnExpression = MappingModelCreationHelper.buildColumnReferenceExpression( + tableGroup, + fkDescriptor.getTargetPart(), + sqmInterpretation.getSqmTranslation().getSqlExpressionResolver(), + sfi + ); + matchingIdSubQuery.getSelectClause().addSqlSelection( new SqlSelectionImpl( 0, fkTargetColumnExpression ) ); + + matchingIdSubQuery.getFromClause().addRoot( + tableGroup + ); + + matchingIdSubQuery.applyPredicate( sqlAst.getRestriction() ); + + return Predicate.combinePredicates( + additionalPredicate.get(), + new InSubQueryPredicate( fkColumnExpression, matchingIdSubQuery, false ) + ); + }, + jdbcParameterBindings, + SqmJdbcExecutionContextAdapter.usingLockingAndPaging( hqlQuery ) + ), jdbcParameterBindings); + } + + private static List getCollectionTableDeletes( + EntityMappingType entityDescriptor, + BiFunction restrictionProducer, + JdbcParameterBindings jdbcParameterBindings, + ExecutionContext executionContext) { + List pluralAttributeMappings = collectCollectionTables( entityDescriptor ); + List deletes = new ArrayList<>( pluralAttributeMappings.size() ); + for ( PluralAttributeMapping attributeMapping : pluralAttributeMappings ) { + final String separateCollectionTable = attributeMapping.getSeparateCollectionTable(); + + final SessionFactoryImplementor sessionFactory = executionContext.getSession().getFactory(); + final JdbcServices jdbcServices = sessionFactory.getJdbcServices(); + + final NamedTableReference tableReference = new NamedTableReference( + separateCollectionTable, + DeleteStatement.DEFAULT_ALIAS, + true + ); + + final DeleteStatement sqlAstDelete = new DeleteStatement( + tableReference, + restrictionProducer.apply( tableReference, attributeMapping ) + ); + + deletes.add( jdbcServices.getJdbcEnvironment() + .getSqlAstTranslatorFactory() + .buildMutationTranslator( sessionFactory, sqlAstDelete ) + .translate( jdbcParameterBindings, executionContext.getQueryOptions() ) + ); + } + return deletes; + } + + private static List collectCollectionTables(EntityMappingType entityDescriptor) { + List pluralAttributeMappings = new ArrayList<>(); + collectCollectionTables( entityDescriptor, pluralAttributeMappings ); + return pluralAttributeMappings; + } + + private static void collectCollectionTables(EntityMappingType entityDescriptor, List pluralAttributeMappings) { + if ( ! entityDescriptor.getEntityPersister().hasCollections() ) { + // none to clean-up + return; + } + + entityDescriptor.visitSubTypeAttributeMappings( + attributeMapping -> { + if ( attributeMapping instanceof PluralAttributeMapping ) { + PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) attributeMapping; + if (pluralAttributeMapping.getSeparateCollectionTable() != null) { + pluralAttributeMappings.add( pluralAttributeMapping ); + } + } else if ( attributeMapping instanceof EmbeddedAttributeMapping ) { + collectCollectionTables( + (EmbeddedAttributeMapping) attributeMapping, + pluralAttributeMappings + ); + } + } + ); + } + + private static void collectCollectionTables(EmbeddedAttributeMapping attributeMapping, List pluralAttributeMappings) { + attributeMapping.visitSubParts( + modelPart -> { + if ( modelPart instanceof PluralAttributeMapping ) { + PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) attributeMapping; + if (pluralAttributeMapping.getSeparateCollectionTable() != null) { + pluralAttributeMappings.add( pluralAttributeMapping ); + } + } else if ( modelPart instanceof EmbeddedAttributeMapping ) { + collectCollectionTables( + (EmbeddedAttributeMapping) modelPart, + pluralAttributeMappings + ); + } + }, + null + ); + } + @Override public String getSqlAlias(EntityManager em, Query query, String alias, int queryPartNumber) { QuerySqmImpl hqlQuery = query.unwrap(QuerySqmImpl.class); @@ -1017,12 +1192,6 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter queryStrings = new ArrayList<>(participatingQueries.size()); - // Set querySpaces = new HashSet<>(); - // QueryParamEntry queryParametersEntry = createQueryParameters(em, participatingQueries, queryStrings, querySpaces); - // QueryParameters queryParameters = queryParametersEntry.queryParameters; - // Create plan for example query JdbcOperationQuerySelect exampleQueryJdbcOperation = (JdbcOperationQuerySelect) getJdbcOperation(exampleQuery); @@ -1036,37 +1205,12 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter returningResult = new HibernateReturningResult(); - // if (!queryPlanEntry.isFromCache()) { - // prepareQueryPlan(queryPlan, queryParametersEntry.specifications, finalSql, session, modificationBaseQuery, true, dbmsDialect); - // queryPlan = putQueryPlanIfAbsent(sfi, cacheKey, queryPlan); - // } - // - // if (queryPlan.getTranslators().length > 1) { - // throw new IllegalArgumentException("No support for multiple translators yet!"); - // } - // - // QueryTranslator queryTranslator = queryPlan.getTranslators()[0]; - // - // // If the DBMS doesn't support inclusion of cascading deletes in a with clause, we have to execute them manually - // StatementExecutor executor = getExecutor(queryTranslator, session, modificationBaseQuery); - // List originalDeletes = Collections.emptyList(); - // - // if (executor != null && executor instanceof DeleteExecutor) { - // originalDeletes = getField(executor, "deletes"); - // } - // - // // Extract query loader for native listing - // QueryLoader queryLoader = getField(queryTranslator, "queryLoader"); // Do the native list operation with custom session and combined parameters /* * NATIVE LIST START */ - // hibernateAccess.checkTransactionSynchStatus(session); - // queryParameters.validateParameters(); - // autoFlush(querySpaces, session); - List results = Collections.EMPTY_LIST; boolean success = false; @@ -1076,7 +1220,7 @@ public MappingModelExpressible getResolvedMappingModelType(SqmParameter querySqm = modificationBaseQuery.unwrap( QuerySqmImpl.class ); + SqmStatement modificationSqmStatement = querySqm.getSqmStatement(); + if ( modificationSqmStatement instanceof SqmDmlStatement) { + SqmDmlStatement sqmDmlStatement = (SqmDmlStatement) modificationSqmStatement; + BulkOperationCleanupAction.schedule( executionContext.getSession(), sqmDmlStatement ); + if ( !dbmsDialect.supportsModificationQueryInWithClause() && sqmDmlStatement instanceof SqmDeleteStatement ) { + Function statementCreator = sql -> session.getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( sql ); + BiConsumer expectationCheck = (integer, preparedStatement) -> { }; + SqmJdbcExecutionContextAdapter executionContextAdapter = SqmJdbcExecutionContextAdapter.usingLockingAndPaging( + modificationBaseQuery.unwrap( DomainQueryExecutionContext.class ) ); + CollectionTableDeleteInfo collectionTableDeletes = getCollectionTableDeletes( querySqm ); + for ( JdbcOperationQueryMutation delete : collectionTableDeletes.deletes ) { + session.getFactory().getJdbcServices().getJdbcMutationExecutor().execute( + delete, + collectionTableDeletes.parameterBindings, + statementCreator, + expectationCheck, + executionContextAdapter + ); + } + } + } + } results = session.getFactory().getJdbcServices().getJdbcSelectExecutor().list( realJdbcSelect, @@ -1157,7 +1282,7 @@ public boolean hasQueryExecutionToBeAddedToStatistics() { ListResultsConsumer.UniqueSemantic.FILTER ); } catch (HibernateException e) { - LOG.severe("Could not execute the following SQL query: " + sqlOverride); + LOG.severe("Could not execute the following SQL query: " + finalSql); if (session.getFactory().getSessionFactoryOptions().isJpaBootstrap()) { throw session.getExceptionConverter().convert(e); } else { @@ -1166,23 +1291,6 @@ public boolean hasQueryExecutionToBeAddedToStatistics() { } finally { interpretation.domainParameterXref.clearExpansions(); } -// try { -// // for (String delete : originalDeletes) { -// // hibernateAccess.doExecute(executor, delete, queryParameters, session, queryParametersEntry.specifications); -// // } -// results = getResultList(serviceProvider, participatingQueries, exampleQuery, finalSql, queryPlanCacheEnabled, hibernateAccess.wrapExecutionContext(exampleQuery, dbmsDialect, returningColumnTypes, returningResult)); -// // results = hibernateAccess.list(queryLoader, wrapSession(session, dbmsDialect, returningColumns, returningColumnTypes, returningResult), queryParameters); -// success = true; -// } catch (HibernateException e) { -// LOG.severe("Could not execute the following SQL query: " + sqlOverride); -// if (session.getFactory().getSessionFactoryOptions().isJpaBootstrap()) { -// throw session.getExceptionConverter().convert(e); -// } else { -// throw e; -// } -// } finally { -// // hibernateAccess.afterTransaction(session, success); -// } /* * NATIVE LIST END */ diff --git a/integration/hibernate6-base/src/main/java/com/blazebit/persistence/integration/hibernate/base/HibernateJpaProvider.java b/integration/hibernate6-base/src/main/java/com/blazebit/persistence/integration/hibernate/base/HibernateJpaProvider.java index 9f67519a3e..93fe562d99 100644 --- a/integration/hibernate6-base/src/main/java/com/blazebit/persistence/integration/hibernate/base/HibernateJpaProvider.java +++ b/integration/hibernate6-base/src/main/java/com/blazebit/persistence/integration/hibernate/base/HibernateJpaProvider.java @@ -1636,7 +1636,7 @@ public Object getIdentifier(Object entity) { if (entity instanceof HibernateProxy) { return ((HibernateProxy) entity).getHibernateLazyInitializer().getIdentifier(); } - return persistenceUnitUtil.getIdentifier(entity); + return entity == null ? null : persistenceUnitUtil.getIdentifier(entity); } @Override diff --git a/parent/pom.xml b/parent/pom.xml index 61bc9f2ce2..f309d2a4a9 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -81,7 +81,7 @@ 5.1.2.Final 3.2.12.Final 4.8.89 - 2.16.2 + 2.15.4 9.5 6.4.1