diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractInsertCollectionCriteriaBuilder.java b/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractInsertCollectionCriteriaBuilder.java index bd68f5bc0c..cdeb9a7130 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractInsertCollectionCriteriaBuilder.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractInsertCollectionCriteriaBuilder.java @@ -29,17 +29,23 @@ import com.blazebit.persistence.impl.query.QuerySpecification; import com.blazebit.persistence.impl.util.SqlUtils; import com.blazebit.persistence.parser.expression.ExpressionCopyContext; +import com.blazebit.persistence.spi.AttributePath; import com.blazebit.persistence.spi.DbmsModificationState; import com.blazebit.persistence.spi.ExtendedAttribute; import com.blazebit.persistence.spi.ExtendedManagedType; import com.blazebit.persistence.spi.ExtendedQuerySupport; import com.blazebit.persistence.spi.JoinTable; +import com.blazebit.persistence.spi.JpaMetamodelAccessor; import javax.persistence.Query; import javax.persistence.TypedQuery; +import javax.persistence.metamodel.Attribute; +import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.ListAttribute; import javax.persistence.metamodel.MapAttribute; +import javax.persistence.metamodel.SingularAttribute; import javax.persistence.metamodel.Type; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; @@ -265,6 +271,7 @@ private QuerySpecification getQuerySpecification(Query baseQuery, Query exam getInsertExecutorQuery(), insertSqlSb.toString(), cutoffColumns, + getForeignKeyParticipatingQueries(), mainQuery.getQueryConfiguration().isQueryPlanCacheEnabled() ); } @@ -307,4 +314,31 @@ protected Query getInsertExecutorQuery() { return em.createQuery(exampleQueryString); } + protected Collection getForeignKeyParticipatingQueries() { + Map map = null; + JpaMetamodelAccessor jpaMetamodelAccessor = mainQuery.jpaProvider.getJpaMetamodelAccessor(); + for (String attributeName : bindingMap.keySet()) { + ExtendedAttribute attribute = collectionAttributeEntries.get(attributeName); + if (attribute == null) { + continue; + } + for (Attribute attributePart : attribute.getAttributePath()) { + if (attributePart instanceof SingularAttribute) { + SingularAttribute singularAttribute = (SingularAttribute) attributePart; + if (map == null) { + map = new HashMap<>(); + } + if (singularAttribute.getType() instanceof EntityType) { + String entityName = ((EntityType) singularAttribute.getType()).getName(); + if (!map.containsKey(entityName)) { + map.put(entityName, em.createQuery("select e from " + entityName + " e")); + } + break; + } + } + } + } + return map == null ? Collections.emptyList() : map.values(); + } + } diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractUpdateCollectionCriteriaBuilder.java b/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractUpdateCollectionCriteriaBuilder.java index 4b20367106..a241bd5753 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractUpdateCollectionCriteriaBuilder.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/AbstractUpdateCollectionCriteriaBuilder.java @@ -39,6 +39,7 @@ import javax.persistence.Query; import javax.persistence.TypedQuery; +import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.ListAttribute; import javax.persistence.metamodel.ManagedType; @@ -47,6 +48,7 @@ import javax.persistence.metamodel.SingularAttribute; import javax.persistence.metamodel.Type; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; @@ -476,6 +478,7 @@ private QuerySpecification getQuerySpecification(Query baseQuery, Query exam collectionAlias, joinTableIdColumns.toArray(new String[0]), setColumns, + getForeignKeyParticipatingQueries(), aliasMapping, getUpdateExampleQuery(), columnExpressionRemappings @@ -496,4 +499,30 @@ protected List getSetColumns() { return setColumns; } + protected Collection getForeignKeyParticipatingQueries() { + Map map = null; + for (String attributeName : setAttributeBindingMap.keySet()) { + ExtendedAttribute attribute = collectionAttributeEntries.get(attributeName); + if (attribute == null) { + continue; + } + for (Attribute attributePart : attribute.getAttributePath()) { + if (attributePart instanceof SingularAttribute) { + SingularAttribute singularAttribute = (SingularAttribute) attributePart; + if (map == null) { + map = new HashMap<>(); + } + if (singularAttribute.getType() instanceof EntityType) { + String entityName = ((EntityType) singularAttribute.getType()).getName(); + if (!map.containsKey(entityName)) { + map.put(entityName, em.createQuery("select e from " + entityName + " e")); + } + break; + } + } + } + } + return map == null ? Collections.emptyList() : map.values(); + } + } diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/BaseUpdateCriteriaBuilderImpl.java b/core/impl/src/main/java/com/blazebit/persistence/impl/BaseUpdateCriteriaBuilderImpl.java index a2af9e2d1a..faf870a449 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/BaseUpdateCriteriaBuilderImpl.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/BaseUpdateCriteriaBuilderImpl.java @@ -37,6 +37,7 @@ import com.blazebit.persistence.parser.expression.Expression; import com.blazebit.persistence.parser.expression.ExpressionCopyContext; import com.blazebit.persistence.parser.expression.SubqueryExpression; +import com.blazebit.persistence.spi.AttributePath; import com.blazebit.persistence.spi.DbmsModificationState; import com.blazebit.persistence.spi.DbmsStatementType; import com.blazebit.persistence.spi.ExtendedManagedType; @@ -46,10 +47,15 @@ import javax.persistence.Query; import javax.persistence.TypedQuery; +import javax.persistence.metamodel.Attribute; +import javax.persistence.metamodel.EntityType; +import javax.persistence.metamodel.SingularAttribute; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.EnumSet; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; @@ -419,6 +425,7 @@ private QuerySpecification getQuerySpecification(Query baseQuery, Query exam tableAlias, idColumns, setColumns, + getForeignKeyParticipatingQueries(), aliasMapping, getUpdateExampleQuery() ); @@ -461,6 +468,30 @@ public String extract(StringBuilder sb, int index, int currentPosition) { })); } + protected Collection getForeignKeyParticipatingQueries() { + Map map = null; + JpaMetamodelAccessor jpaMetamodelAccessor = mainQuery.jpaProvider.getJpaMetamodelAccessor(); + for (String attributeName : setAttributeBindingMap.keySet()) { + AttributePath path = jpaMetamodelAccessor.getBasicAttributePath(getMetamodel(), entityType, attributeName); + for (Attribute attributePart : path.getAttributes()) { + if (attributePart instanceof SingularAttribute) { + SingularAttribute singularAttribute = (SingularAttribute) attributePart; + if (map == null) { + map = new HashMap<>(); + } + if (singularAttribute.getType() instanceof EntityType) { + String entityName = ((EntityType) singularAttribute.getType()).getName(); + if (!map.containsKey(entityName)) { + map.put(entityName, em.createQuery("select e from " + entityName + " e")); + } + break; + } + } + } + } + return map == null ? Collections.emptyList() : map.values(); + } + protected void appendSetClause(StringBuilder sbSelectFrom, boolean externalRepresentation) { sbSelectFrom.append(" SET "); diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/query/CollectionInsertModificationQuerySpecification.java b/core/impl/src/main/java/com/blazebit/persistence/impl/query/CollectionInsertModificationQuerySpecification.java index 508606225b..aa3cc686a2 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/query/CollectionInsertModificationQuerySpecification.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/query/CollectionInsertModificationQuerySpecification.java @@ -40,15 +40,17 @@ public class CollectionInsertModificationQuerySpecification extends Modificat private final Query insertExampleQuery; private final String insertSql; private final int cutoffColumns; + private final Collection foreignKeyParticipatingQueries; public CollectionInsertModificationQuerySpecification(AbstractCommonQueryBuilder commonQueryBuilder, Query baseQuery, Query exampleQuery, Collection> parameters, Set parameterListNames, List keyRestrictedLeftJoinAliases, List entityFunctionNodes, boolean recursive, List ctes, boolean shouldRenderCteNodes, - boolean isEmbedded, String[] returningColumns, ReturningObjectBuilder objectBuilder, Map includedModificationStates, Map returningAttributeBindingMap, Query insertExampleQuery, String insertSql, int cutoffColumns, - boolean queryPlanCacheEnabled) { + boolean isEmbedded, String[] returningColumns, ReturningObjectBuilder objectBuilder, Map includedModificationStates, Map returningAttributeBindingMap, + Query insertExampleQuery, String insertSql, int cutoffColumns, Collection foreignKeyParticipatingQueries, boolean queryPlanCacheEnabled) { super(commonQueryBuilder, baseQuery, exampleQuery, parameters, parameterListNames, keyRestrictedLeftJoinAliases, entityFunctionNodes, recursive, ctes, shouldRenderCteNodes, isEmbedded, returningColumns, objectBuilder, includedModificationStates, returningAttributeBindingMap, queryPlanCacheEnabled); this.insertExampleQuery = insertExampleQuery; this.insertSql = insertSql; this.cutoffColumns = cutoffColumns; + this.foreignKeyParticipatingQueries = foreignKeyParticipatingQueries; } @Override @@ -123,6 +125,7 @@ public String extract(StringBuilder sb, int index, int currentPosition) { participatingQueries.add(baseQuery); participatingQueries.add(exampleQuery); participatingQueries.add(insertExampleQuery); + participatingQueries.addAll(foreignKeyParticipatingQueries); // Some dbms like DB2 will need to wrap modification queries in select queries when using CTEs boolean hasCtes = withClause != null && withClause.length() != 0 || addedCtes != null && !addedCtes.isEmpty(); diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/query/CollectionUpdateModificationQuerySpecification.java b/core/impl/src/main/java/com/blazebit/persistence/impl/query/CollectionUpdateModificationQuerySpecification.java index 4a74cf85ab..d5b32a5e4f 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/query/CollectionUpdateModificationQuerySpecification.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/query/CollectionUpdateModificationQuerySpecification.java @@ -39,8 +39,8 @@ public class CollectionUpdateModificationQuerySpecification extends UpdateMod public CollectionUpdateModificationQuerySpecification(AbstractCommonQueryBuilder commonQueryBuilder, Query baseQuery, Query exampleQuery, Collection> parameters, Set parameterListNames, boolean recursive, List ctes, boolean shouldRenderCteNodes, boolean isEmbedded, String[] returningColumns, ReturningObjectBuilder objectBuilder, Map includedModificationStates, Map returningAttributeBindingMap, boolean queryPlanCacheEnabled, - String tableToUpdate, String tableAlias, String[] idColumns, List setColumns, Map aliasMapping, Query updateExampleQuery, Map columnExpressionRemappings) { - super(commonQueryBuilder, baseQuery, exampleQuery, parameters, parameterListNames, recursive, ctes, shouldRenderCteNodes, isEmbedded, returningColumns, objectBuilder, includedModificationStates, returningAttributeBindingMap, queryPlanCacheEnabled, tableToUpdate, tableAlias, idColumns, setColumns, aliasMapping, updateExampleQuery); + String tableToUpdate, String tableAlias, String[] idColumns, List setColumns, Collection foreignKeyParticipatingQueries, Map aliasMapping, Query updateExampleQuery, Map columnExpressionRemappings) { + super(commonQueryBuilder, baseQuery, exampleQuery, parameters, parameterListNames, recursive, ctes, shouldRenderCteNodes, isEmbedded, returningColumns, objectBuilder, includedModificationStates, returningAttributeBindingMap, queryPlanCacheEnabled, tableToUpdate, tableAlias, idColumns, setColumns, foreignKeyParticipatingQueries, aliasMapping, updateExampleQuery); this.columnExpressionRemappings = columnExpressionRemappings; } diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/query/UpdateModificationQuerySpecification.java b/core/impl/src/main/java/com/blazebit/persistence/impl/query/UpdateModificationQuerySpecification.java index 4adcef7909..6a03fec5e7 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/query/UpdateModificationQuerySpecification.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/query/UpdateModificationQuerySpecification.java @@ -42,17 +42,19 @@ public class UpdateModificationQuerySpecification extends ModificationQuerySp private final String tableAlias; private final String[] idColumns; private final List setColumns; + private final Collection foreignKeyParticipatingQueries; private final Map aliasMapping; private final Query updateExampleQuery; public UpdateModificationQuerySpecification(AbstractCommonQueryBuilder commonQueryBuilder, Query baseQuery, Query exampleQuery, Collection> parameters, Set parameterListNames, boolean recursive, List ctes, boolean shouldRenderCteNodes, boolean isEmbedded, String[] returningColumns, ReturningObjectBuilder objectBuilder, Map includedModificationStates, Map returningAttributeBindingMap, boolean queryPlanCacheEnabled, - String tableToUpdate, String tableAlias, String[] idColumns, List setColumns, Map aliasMapping, Query updateExampleQuery) { + String tableToUpdate, String tableAlias, String[] idColumns, List setColumns, Collection foreignKeyParticipatingQueries, Map aliasMapping, Query updateExampleQuery) { super(commonQueryBuilder, baseQuery, exampleQuery, parameters, parameterListNames, recursive, ctes, shouldRenderCteNodes, isEmbedded, returningColumns, objectBuilder, includedModificationStates, returningAttributeBindingMap, queryPlanCacheEnabled); this.tableToUpdate = tableToUpdate; this.tableAlias = tableAlias; this.idColumns = idColumns; this.setColumns = setColumns; + this.foreignKeyParticipatingQueries = foreignKeyParticipatingQueries; this.aliasMapping = aliasMapping; this.updateExampleQuery = updateExampleQuery; } @@ -151,6 +153,7 @@ protected void initialize() { participatingQueries.add(baseQuery); participatingQueries.add(exampleQuery); participatingQueries.add(updateExampleQuery); + participatingQueries.addAll(foreignKeyParticipatingQueries); boolean hasCtes = withClause != null && withClause.length() != 0 || addedCtes != null && !addedCtes.isEmpty(); if (hasCtes && returningAttributeBindingMap.isEmpty() && !dbmsDialect.usesExecuteUpdateWhenWithClauseInModificationQuery()) {