use of org.hibernate.query.spi.DomainQueryExecutionContext in project hibernate-orm by hibernate.
the class IdSelectionTests method testJoinedSubclassRestrictedOnNonPrimaryRootTable.
@Test
public void testJoinedSubclassRestrictedOnNonPrimaryRootTable(SessionFactoryScope scope) {
final SqmDeleteStatement<?> sqm = (SqmDeleteStatement<?>) scope.getSessionFactory().getQueryEngine().getHqlTranslator().translate("delete ForeignCustomer where name = :n");
final DomainParameterXref domainParameterXref = DomainParameterXref.from(sqm);
final ParameterMetadataImpl parameterMetadata = new ParameterMetadataImpl(domainParameterXref.getQueryParameters());
final QueryParameterBindingsImpl domainParamBindings = QueryParameterBindingsImpl.from(parameterMetadata, scope.getSessionFactory());
domainParamBindings.getBinding("n").setBindValue("Acme");
scope.inTransaction(session -> {
final DomainQueryExecutionContext executionContext = new TestExecutionContext(session, domainParamBindings);
MatchingIdSelectionHelper.selectMatchingIds(sqm, domainParameterXref, executionContext);
});
}
use of org.hibernate.query.spi.DomainQueryExecutionContext in project hibernate-orm by hibernate.
the class RestrictedDeleteExecutionDelegate method execute.
@Override
public int execute(DomainQueryExecutionContext executionContext) {
final EntityPersister entityDescriptor = sessionFactory.getRuntimeMetamodels().getMappingMetamodel().getEntityDescriptor(sqmDelete.getTarget().getEntityName());
final String hierarchyRootTableName = ((Joinable) entityDescriptor).getTableName();
final TableGroup deletingTableGroup = converter.getMutatingTableGroup();
final TableReference hierarchyRootTableReference = deletingTableGroup.resolveTableReference(deletingTableGroup.getNavigablePath(), hierarchyRootTableName);
assert hierarchyRootTableReference != null;
final Map<SqmParameter<?>, List<List<JdbcParameter>>> parameterResolutions;
final Map<SqmParameter<?>, MappingModelExpressible<?>> paramTypeResolutions;
if (domainParameterXref.getSqmParameterCount() == 0) {
parameterResolutions = Collections.emptyMap();
paramTypeResolutions = Collections.emptyMap();
} else {
parameterResolutions = new IdentityHashMap<>();
paramTypeResolutions = new LinkedHashMap<>();
}
// Use the converter to interpret the where-clause. We do this for 2 reasons:
// 1) the resolved Predicate is ultimately the base for applying restriction to the deletes
// 2) we also inspect each ColumnReference that is part of the where-clause to see which
// table it comes from. if all of the referenced columns (if any at all) are from the root table
// we can perform all of the deletes without using an id-table
final MutableBoolean needsIdTableWrapper = new MutableBoolean(false);
final Predicate specifiedRestriction = converter.visitWhereClause(sqmDelete.getWhereClause(), columnReference -> {
if (!hierarchyRootTableReference.getIdentificationVariable().equals(columnReference.getQualifier())) {
needsIdTableWrapper.setValue(true);
}
}, (sqmParameter, mappingType, jdbcParameters) -> {
parameterResolutions.computeIfAbsent(sqmParameter, k -> new ArrayList<>(1)).add(jdbcParameters);
paramTypeResolutions.put(sqmParameter, mappingType);
});
final PredicateCollector predicateCollector = new PredicateCollector(specifiedRestriction);
entityDescriptor.applyBaseRestrictions((filterPredicate) -> {
needsIdTableWrapper.setValue(true);
predicateCollector.applyPredicate(filterPredicate);
}, deletingTableGroup, true, executionContext.getSession().getLoadQueryInfluencers().getEnabledFilters(), null, converter);
converter.pruneTableGroupJoins();
// We need an id table if we want to delete from an intermediate table to avoid FK violations
// The intermediate table has a FK to the root table, so we can't delete from the root table first
// Deleting from the intermediate table first also isn't possible,
// because that is the source for deletion in other tables, hence we need an id table
final boolean needsIdTable = needsIdTableWrapper.getValue() || entityDescriptor != entityDescriptor.getRootEntityDescriptor();
final SqmJdbcExecutionContextAdapter executionContextAdapter = SqmJdbcExecutionContextAdapter.omittingLockingAndPaging(executionContext);
if (needsIdTable) {
return executeWithIdTable(predicateCollector.getPredicate(), deletingTableGroup, parameterResolutions, paramTypeResolutions, executionContextAdapter);
} else {
return executeWithoutIdTable(predicateCollector.getPredicate(), deletingTableGroup, parameterResolutions, paramTypeResolutions, converter.getSqlExpressionResolver(), executionContextAdapter);
}
}
use of org.hibernate.query.spi.DomainQueryExecutionContext in project hibernate-orm by hibernate.
the class TableBasedUpdateHandler method resolveDelegate.
private ExecutionDelegate resolveDelegate(DomainQueryExecutionContext executionContext) {
final SessionFactoryImplementor sessionFactory = getSessionFactory();
final MappingMetamodel domainModel = sessionFactory.getRuntimeMetamodels().getMappingMetamodel();
final EntityPersister entityDescriptor = domainModel.getEntityDescriptor(getSqmDeleteOrUpdateStatement().getTarget().getEntityName());
final String rootEntityName = entityDescriptor.getRootEntityName();
final EntityPersister rootEntityDescriptor = domainModel.getEntityDescriptor(rootEntityName);
final String hierarchyRootTableName = ((Joinable) rootEntityDescriptor).getTableName();
final MultiTableSqmMutationConverter converterDelegate = new MultiTableSqmMutationConverter(entityDescriptor, getSqmDeleteOrUpdateStatement(), getSqmDeleteOrUpdateStatement().getTarget(), domainParameterXref, executionContext.getQueryOptions(), executionContext.getSession().getLoadQueryInfluencers(), executionContext.getQueryParameterBindings(), sessionFactory);
final TableGroup updatingTableGroup = converterDelegate.getMutatingTableGroup();
final TableReference hierarchyRootTableReference = updatingTableGroup.resolveTableReference(updatingTableGroup.getNavigablePath(), hierarchyRootTableName);
assert hierarchyRootTableReference != null;
final Map<SqmParameter<?>, List<List<JdbcParameter>>> parameterResolutions;
if (domainParameterXref.getSqmParameterCount() == 0) {
parameterResolutions = Collections.emptyMap();
} else {
parameterResolutions = new IdentityHashMap<>();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// visit the set-clause using our special converter, collecting
// information about the assignments
final List<Assignment> assignments = new ArrayList<>();
final Map<SqmParameter<?>, MappingModelExpressible<?>> paramTypeResolutions = new LinkedHashMap<>();
converterDelegate.visitSetClause(getSqmDeleteOrUpdateStatement().getSetClause(), assignments::add, (sqmParameter, mappingType, jdbcParameters) -> {
parameterResolutions.computeIfAbsent(sqmParameter, k -> new ArrayList<>(1)).add(jdbcParameters);
paramTypeResolutions.put(sqmParameter, mappingType);
});
converterDelegate.addVersionedAssignment(assignments::add, getSqmDeleteOrUpdateStatement());
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// visit the where-clause using our special converter, collecting information
// about the restrictions
final Predicate providedPredicate;
final SqmWhereClause whereClause = getSqmUpdate().getWhereClause();
if (whereClause == null || whereClause.getPredicate() == null) {
providedPredicate = null;
} else {
providedPredicate = converterDelegate.visitWhereClause(whereClause, columnReference -> {
}, (sqmParameter, mappingType, jdbcParameters) -> {
parameterResolutions.computeIfAbsent(sqmParameter, k -> new ArrayList<>(1)).add(jdbcParameters);
paramTypeResolutions.put(sqmParameter, mappingType);
});
assert providedPredicate != null;
}
final PredicateCollector predicateCollector = new PredicateCollector(providedPredicate);
entityDescriptor.applyBaseRestrictions(predicateCollector::applyPredicate, updatingTableGroup, true, executionContext.getSession().getLoadQueryInfluencers().getEnabledFilters(), null, converterDelegate);
converterDelegate.pruneTableGroupJoins();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// cross-reference the TableReference by alias. The TableGroup already
// cross-references it by name, bu the ColumnReference only has the alias
final Map<String, TableReference> tableReferenceByAlias = CollectionHelper.mapOfSize(updatingTableGroup.getTableReferenceJoins().size() + 1);
collectTableReference(updatingTableGroup.getPrimaryTableReference(), tableReferenceByAlias::put);
for (int i = 0; i < updatingTableGroup.getTableReferenceJoins().size(); i++) {
collectTableReference(updatingTableGroup.getTableReferenceJoins().get(i), tableReferenceByAlias::put);
}
return new UpdateExecutionDelegate(getSqmUpdate(), converterDelegate, idTable, afterUseAction, sessionUidAccess, domainParameterXref, updatingTableGroup, hierarchyRootTableReference, tableReferenceByAlias, assignments, predicateCollector.getPredicate(), parameterResolutions, paramTypeResolutions, executionContext);
}
use of org.hibernate.query.spi.DomainQueryExecutionContext in project hibernate-orm by hibernate.
the class TableBasedInsertHandler method resolveDelegate.
private ExecutionDelegate resolveDelegate(DomainQueryExecutionContext executionContext) {
final EntityPersister entityDescriptor = sessionFactory.getRuntimeMetamodels().getMappingMetamodel().getEntityDescriptor(getSqmInsertStatement().getTarget().getEntityName());
final MultiTableSqmMutationConverter converterDelegate = new MultiTableSqmMutationConverter(entityDescriptor, getSqmInsertStatement(), getSqmInsertStatement().getTarget(), domainParameterXref, executionContext.getQueryOptions(), executionContext.getSession().getLoadQueryInfluencers(), executionContext.getQueryParameterBindings(), sessionFactory);
final TableGroup insertingTableGroup = converterDelegate.getMutatingTableGroup();
final Map<SqmParameter<?>, List<List<JdbcParameter>>> parameterResolutions;
if (domainParameterXref.getSqmParameterCount() == 0) {
parameterResolutions = Collections.emptyMap();
} else {
parameterResolutions = new IdentityHashMap<>();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// visit the insertion target using our special converter, collecting
// information about the target paths
final List<Assignment> targetPathColumns = new ArrayList<>();
final Map<SqmParameter<?>, MappingModelExpressible<?>> paramTypeResolutions = new LinkedHashMap<>();
final NamedTableReference entityTableReference = new NamedTableReference(entityTable.getTableExpression(), TemporaryTable.DEFAULT_ALIAS, true, sessionFactory);
final InsertStatement insertStatement = new InsertStatement(entityTableReference);
final BaseSqmToSqlAstConverter.AdditionalInsertValues additionalInsertValues = converterDelegate.visitInsertionTargetPaths((assigable, columnReferences) -> {
insertStatement.addTargetColumnReferences(columnReferences);
targetPathColumns.add(new Assignment(assigable, (Expression) assigable));
}, sqmInsertStatement, entityDescriptor, insertingTableGroup, (sqmParameter, mappingType, jdbcParameters) -> {
parameterResolutions.computeIfAbsent(sqmParameter, k -> new ArrayList<>(1)).add(jdbcParameters);
paramTypeResolutions.put(sqmParameter, mappingType);
});
if (sqmInsertStatement instanceof SqmInsertSelectStatement) {
final QueryPart queryPart = converterDelegate.visitQueryPart(((SqmInsertSelectStatement<?>) sqmInsertStatement).getSelectQueryPart());
queryPart.visitQuerySpecs(querySpec -> {
if (additionalInsertValues.applySelections(querySpec, sessionFactory)) {
final TemporaryTableColumn rowNumberColumn = entityTable.getColumns().get(entityTable.getColumns().size() - 1);
final ColumnReference columnReference = new ColumnReference((String) null, rowNumberColumn.getColumnName(), false, null, null, rowNumberColumn.getJdbcMapping(), sessionFactory);
insertStatement.getTargetColumnReferences().set(insertStatement.getTargetColumnReferences().size() - 1, columnReference);
targetPathColumns.set(targetPathColumns.size() - 1, new Assignment(columnReference, columnReference));
} else if (entityDescriptor.getIdentifierGenerator() instanceof OptimizableGenerator) {
final Optimizer optimizer = ((OptimizableGenerator) entityDescriptor.getIdentifierGenerator()).getOptimizer();
if (optimizer != null && optimizer.getIncrementSize() > 1) {
if (!sessionFactory.getJdbcServices().getDialect().supportsWindowFunctions()) {
return;
}
final TemporaryTableColumn rowNumberColumn = entityTable.getColumns().get(entityTable.getColumns().size() - 1);
final ColumnReference columnReference = new ColumnReference((String) null, rowNumberColumn.getColumnName(), false, null, null, rowNumberColumn.getJdbcMapping(), sessionFactory);
insertStatement.getTargetColumnReferences().add(columnReference);
targetPathColumns.add(new Assignment(columnReference, columnReference));
final BasicType<Integer> rowNumberType = sessionFactory.getTypeConfiguration().getBasicTypeForJavaType(Integer.class);
querySpec.getSelectClause().addSqlSelection(new SqlSelectionImpl(1, 0, new Over<>(new SelfRenderingFunctionSqlAstExpression("row_number", (appender, args, walker) -> appender.appendSql("row_number()"), Collections.emptyList(), rowNumberType, rowNumberType), Collections.emptyList(), Collections.emptyList())));
}
}
});
insertStatement.setSourceSelectStatement(queryPart);
} else {
// Add the row number column if there is one
final IdentifierGenerator generator = entityDescriptor.getIdentifierGenerator();
final BasicType<?> rowNumberType;
if (generator instanceof OptimizableGenerator) {
final Optimizer optimizer = ((OptimizableGenerator) generator).getOptimizer();
if (optimizer != null && optimizer.getIncrementSize() > 1) {
final TemporaryTableColumn rowNumberColumn = entityTable.getColumns().get(entityTable.getColumns().size() - 1);
rowNumberType = (BasicType<?>) rowNumberColumn.getJdbcMapping();
final ColumnReference columnReference = new ColumnReference((String) null, rowNumberColumn.getColumnName(), false, null, null, rowNumberColumn.getJdbcMapping(), sessionFactory);
insertStatement.getTargetColumnReferences().add(columnReference);
targetPathColumns.add(new Assignment(columnReference, columnReference));
} else {
rowNumberType = null;
}
} else {
rowNumberType = null;
}
final List<SqmValues> sqmValuesList = ((SqmInsertValuesStatement<?>) sqmInsertStatement).getValuesList();
final List<Values> valuesList = new ArrayList<>(sqmValuesList.size());
for (int i = 0; i < sqmValuesList.size(); i++) {
final Values values = converterDelegate.visitValues(sqmValuesList.get(i));
additionalInsertValues.applyValues(values);
if (rowNumberType != null) {
values.getExpressions().add(new QueryLiteral<>(i + 1, rowNumberType));
}
valuesList.add(values);
}
insertStatement.setValuesList(valuesList);
}
converterDelegate.pruneTableGroupJoins();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// cross-reference the TableReference by alias. The TableGroup already
// cross-references it by name, but the ColumnReference only has the alias
final Map<String, TableReference> tableReferenceByAlias = CollectionHelper.mapOfSize(insertingTableGroup.getTableReferenceJoins().size() + 1);
collectTableReference(insertingTableGroup.getPrimaryTableReference(), tableReferenceByAlias::put);
for (int i = 0; i < insertingTableGroup.getTableReferenceJoins().size(); i++) {
collectTableReference(insertingTableGroup.getTableReferenceJoins().get(i), tableReferenceByAlias::put);
}
return new InsertExecutionDelegate(sqmInsertStatement, converterDelegate, entityTable, afterUseAction, sessionUidAccess, domainParameterXref, insertingTableGroup, tableReferenceByAlias, targetPathColumns, insertStatement, parameterResolutions, paramTypeResolutions, executionContext);
}
use of org.hibernate.query.spi.DomainQueryExecutionContext in project hibernate-orm by hibernate.
the class IdSelectionTests method testSecondaryTableRestrictedOnNonRootTable.
@Test
public void testSecondaryTableRestrictedOnNonRootTable(SessionFactoryScope scope) {
final SqmDeleteStatement<?> sqm = (SqmDeleteStatement<?>) scope.getSessionFactory().getQueryEngine().getHqlTranslator().translate("delete SimpleEntityWithSecondaryTables where data = :d");
final DomainParameterXref domainParameterXref = DomainParameterXref.from(sqm);
final ParameterMetadataImpl parameterMetadata = new ParameterMetadataImpl(domainParameterXref.getQueryParameters());
final QueryParameterBindingsImpl domainParamBindings = QueryParameterBindingsImpl.from(parameterMetadata, scope.getSessionFactory());
domainParamBindings.getBinding("d").setBindValue("123");
scope.inTransaction(session -> {
final DomainQueryExecutionContext executionContext = new TestExecutionContext(session, domainParamBindings);
MatchingIdSelectionHelper.selectMatchingIds(sqm, domainParameterXref, executionContext);
});
}
Aggregations