use of org.hibernate.sql.ast.tree.from.TableGroup in project hibernate-orm by hibernate.
the class MatchingIdSelectionHelper method selectMatchingIds.
/**
* Centralized selection of ids matching the restriction of the DELETE
* or UPDATE SQM query
*/
public static List<Object> selectMatchingIds(SqmDeleteOrUpdateStatement sqmMutationStatement, DomainParameterXref domainParameterXref, DomainQueryExecutionContext executionContext) {
final SessionFactoryImplementor factory = executionContext.getSession().getFactory();
final EntityMappingType entityDescriptor = factory.getRuntimeMetamodels().getEntityMappingType(sqmMutationStatement.getTarget().getModel().getHibernateEntityName());
final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter(entityDescriptor, sqmMutationStatement, sqmMutationStatement.getTarget(), domainParameterXref, executionContext.getQueryOptions(), executionContext.getSession().getLoadQueryInfluencers(), executionContext.getQueryParameterBindings(), factory);
final Map<SqmParameter, List<JdbcParameter>> parameterResolutions;
if (domainParameterXref.getSqmParameterCount() == 0) {
parameterResolutions = Collections.emptyMap();
} else {
parameterResolutions = new IdentityHashMap<>();
}
final Predicate restriction = sqmConverter.visitWhereClause(sqmMutationStatement.getWhereClause(), columnReference -> {
}, (sqmParam, mappingType, jdbcParameters) -> parameterResolutions.put(sqmParam, jdbcParameters));
final SelectStatement matchingIdSelection = generateMatchingIdSelectStatement(entityDescriptor, sqmMutationStatement, true, restriction, sqmConverter, executionContext, factory);
sqmConverter.getProcessingStateStack().push(new SqlAstQueryPartProcessingStateImpl(matchingIdSelection.getQuerySpec(), sqmConverter.getCurrentProcessingState(), sqmConverter.getSqlAstCreationState(), sqmConverter.getCurrentClauseStack()::getCurrent, true));
entityDescriptor.visitSubTypeAttributeMappings(attribute -> {
if (attribute instanceof PluralAttributeMapping) {
final PluralAttributeMapping pluralAttribute = (PluralAttributeMapping) attribute;
if (pluralAttribute.getSeparateCollectionTable() != null) {
// Ensure that the FK target columns are available
final boolean useFkTarget = !(pluralAttribute.getKeyDescriptor().getTargetPart() instanceof EntityIdentifierMapping);
if (useFkTarget) {
final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup();
pluralAttribute.getKeyDescriptor().getTargetPart().applySqlSelections(mutatingTableGroup.getNavigablePath(), mutatingTableGroup, sqmConverter, (selection, jdbcMapping) -> {
matchingIdSelection.getDomainResultDescriptors().add(new BasicResult<>(selection.getValuesArrayPosition(), null, jdbcMapping.getJavaTypeDescriptor()));
});
}
}
}
});
sqmConverter.getProcessingStateStack().pop();
final JdbcServices jdbcServices = factory.getJdbcServices();
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
final SqlAstTranslator<JdbcSelect> sqlAstSelectTranslator = jdbcEnvironment.getSqlAstTranslatorFactory().buildSelectTranslator(factory, matchingIdSelection);
final JdbcParameterBindings jdbcParameterBindings = SqmUtil.createJdbcParameterBindings(executionContext.getQueryParameterBindings(), domainParameterXref, SqmUtil.generateJdbcParamsXref(domainParameterXref, sqmConverter), factory.getRuntimeMetamodels().getMappingMetamodel(), navigablePath -> sqmConverter.getMutatingTableGroup(), new SqmParameterMappingModelResolutionAccess() {
@Override
@SuppressWarnings("unchecked")
public <T> MappingModelExpressible<T> getResolvedMappingModelType(SqmParameter<T> parameter) {
return (MappingModelExpressible<T>) sqmConverter.getSqmParameterMappingModelExpressibleResolutions().get(parameter);
}
}, executionContext.getSession());
final LockOptions lockOptions = executionContext.getQueryOptions().getLockOptions();
final LockMode lockMode = lockOptions.getLockMode();
// Acquire a WRITE lock for the rows that are about to be modified
lockOptions.setLockMode(LockMode.WRITE);
// Visit the table joins and reset the lock mode if we encounter OUTER joins that are not supported
if (!jdbcEnvironment.getDialect().supportsOuterJoinForUpdate()) {
matchingIdSelection.getQuerySpec().getFromClause().visitTableJoins(tableJoin -> {
if (tableJoin.getJoinType() != SqlAstJoinType.INNER) {
lockOptions.setLockMode(lockMode);
}
});
}
final JdbcSelect idSelectJdbcOperation = sqlAstSelectTranslator.translate(jdbcParameterBindings, executionContext.getQueryOptions());
lockOptions.setLockMode(lockMode);
return jdbcServices.getJdbcSelectExecutor().list(idSelectJdbcOperation, jdbcParameterBindings, SqmJdbcExecutionContextAdapter.omittingLockingAndPaging(executionContext), row -> row[0], ListResultsConsumer.UniqueSemantic.FILTER);
}
use of org.hibernate.sql.ast.tree.from.TableGroup in project hibernate-orm by hibernate.
the class MatchingIdSelectionHelper method generateMatchingIdSelectQuery.
/**
* @asciidoc
*
* Generates a query-spec for selecting all ids matching the restriction defined as part
* of the user's update/delete query. This query-spec is generally used:
*
* * to select all the matching ids via JDBC - see {@link MatchingIdSelectionHelper#selectMatchingIds}
* * as a sub-query restriction to insert rows into an "id table"
*/
public static QuerySpec generateMatchingIdSelectQuery(EntityMappingType targetEntityDescriptor, SqmDeleteOrUpdateStatement sqmStatement, DomainParameterXref domainParameterXref, Predicate restriction, MultiTableSqmMutationConverter sqmConverter, SessionFactoryImplementor sessionFactory) {
final EntityDomainType entityDomainType = sqmStatement.getTarget().getModel();
if (log.isTraceEnabled()) {
log.tracef("Starting generation of entity-id SQM selection - %s", entityDomainType.getHibernateEntityName());
}
final QuerySpec idSelectionQuery = new QuerySpec(true, 1);
final TableGroup mutatingTableGroup = sqmConverter.getMutatingTableGroup();
idSelectionQuery.getFromClause().addRoot(mutatingTableGroup);
targetEntityDescriptor.getIdentifierMapping().forEachSelectable((position, selection) -> {
final TableReference tableReference = mutatingTableGroup.resolveTableReference(mutatingTableGroup.getNavigablePath(), selection.getContainingTableExpression());
final Expression expression = sqmConverter.getSqlExpressionResolver().resolveSqlExpression(SqlExpressionResolver.createColumnReferenceKey(tableReference, selection.getSelectionExpression()), sqlAstProcessingState -> new ColumnReference(tableReference, selection, sessionFactory));
idSelectionQuery.getSelectClause().addSqlSelection(new SqlSelectionImpl(position, position + 1, expression));
});
idSelectionQuery.applyPredicate(restriction);
return idSelectionQuery;
}
use of org.hibernate.sql.ast.tree.from.TableGroup in project hibernate-orm by hibernate.
the class CountFunction method canReplaceWithStar.
private boolean canReplaceWithStar(SqlAstNode arg, SqlAstTranslator<?> translator) {
// To determine if we can replace the argument with a star, we must know if the argument is nullable
if (arg instanceof AbstractSqmPathInterpretation<?>) {
final AbstractSqmPathInterpretation<?> pathInterpretation = (AbstractSqmPathInterpretation<?>) arg;
final TableGroup tableGroup = pathInterpretation.getTableGroup();
final Expression sqlExpression = pathInterpretation.getSqlExpression();
final JdbcMappingContainer expressionType = sqlExpression.getExpressionType();
// The entity identifier mapping is always considered non-nullable
final boolean isNonNullable = expressionType instanceof EntityIdentifierMapping;
// But we also have to check if it contains joins that could alter the nullability (RIGHT or FULL)
if (isNonNullable && tableGroup.canUseInnerJoins() && !hasJoinsAlteringNullability(tableGroup)) {
// COUNT can only be used in query specs as query groups can only refer positionally in the order by
final QuerySpec querySpec = (QuerySpec) translator.getCurrentQueryPart();
// On top of this, we also have to ensure that there are no neighbouring joins that alter nullability
for (TableGroup root : querySpec.getFromClause().getRoots()) {
final Boolean result = hasNeighbouringJoinsAlteringNullability(root, tableGroup);
if (result != null) {
return !result;
}
}
return true;
}
}
return false;
}
use of org.hibernate.sql.ast.tree.from.TableGroup in project hibernate-orm by hibernate.
the class AbstractEntityPersister method selectFragment.
@Override
public String selectFragment(String alias, String suffix) {
final QuerySpec rootQuerySpec = new QuerySpec(true);
final String rootTableName = getRootTableName();
final LoaderSqlAstCreationState sqlAstCreationState = new LoaderSqlAstCreationState(rootQuerySpec, new SqlAliasBaseManager(), new SimpleFromClauseAccessImpl(), LockOptions.NONE, (fetchParent, querySpec, creationState) -> {
final List<Fetch> fetches = new ArrayList<>();
fetchParent.getReferencedMappingContainer().visitFetchables(fetchable -> {
// Ignore plural attributes
if (fetchable instanceof PluralAttributeMapping) {
return;
}
FetchTiming fetchTiming = fetchable.getMappedFetchOptions().getTiming();
final boolean selectable;
if (fetchable instanceof StateArrayContributorMapping) {
final int propertyNumber = ((StateArrayContributorMapping) fetchable).getStateArrayPosition();
final int tableNumber = getSubclassPropertyTableNumber(propertyNumber);
selectable = !isSubclassTableSequentialSelect(tableNumber) && propertySelectable[propertyNumber];
} else {
selectable = true;
}
if (fetchable instanceof BasicValuedModelPart) {
// Ignore lazy basic columns
if (fetchTiming == FetchTiming.DELAYED) {
return;
}
} else if (fetchable instanceof Association) {
final Association association = (Association) fetchable;
// Ignore the fetchable if the FK is on the other side
if (association.getSideNature() == ForeignKeyDescriptor.Nature.TARGET) {
return;
}
// Ensure the FK comes from the root table
if (!rootTableName.equals(association.getForeignKeyDescriptor().getKeyTable())) {
return;
}
fetchTiming = FetchTiming.DELAYED;
}
if (selectable) {
final NavigablePath navigablePath = fetchParent.resolveNavigablePath(fetchable);
final Fetch fetch = fetchParent.generateFetchableFetch(fetchable, navigablePath, fetchTiming, true, null, creationState);
fetches.add(fetch);
}
}, null);
return fetches;
}, true, getFactory());
final NavigablePath entityPath = new NavigablePath(getRootPathName());
final TableGroup rootTableGroup = createRootTableGroup(true, entityPath, null, () -> p -> {
}, new SqlAliasBaseConstant(alias), sqlAstCreationState.getSqlExpressionResolver(), sqlAstCreationState.getFromClauseAccess(), getFactory());
rootQuerySpec.getFromClause().addRoot(rootTableGroup);
sqlAstCreationState.getFromClauseAccess().registerTableGroup(entityPath, rootTableGroup);
createDomainResult(entityPath, rootTableGroup, null, sqlAstCreationState);
// Wrap expressions with aliases
final SelectClause selectClause = rootQuerySpec.getSelectClause();
final List<SqlSelection> sqlSelections = selectClause.getSqlSelections();
int i = 0;
for (String identifierAlias : identifierAliases) {
sqlSelections.set(i, new SqlSelectionImpl(i, i + 1, new AliasedExpression(sqlSelections.get(i).getExpression(), identifierAlias + suffix)));
i++;
}
if (entityMetamodel.hasSubclasses()) {
sqlSelections.set(i, new SqlSelectionImpl(i, i + 1, new AliasedExpression(sqlSelections.get(i).getExpression(), getDiscriminatorAlias() + suffix)));
i++;
}
if (hasRowId()) {
sqlSelections.set(i, new SqlSelectionImpl(i, i + 1, new AliasedExpression(sqlSelections.get(i).getExpression(), ROWID_ALIAS + suffix)));
i++;
}
final String[] columnAliases = getSubclassColumnAliasClosure();
final String[] formulaAliases = getSubclassFormulaAliasClosure();
int columnIndex = 0;
int formulaIndex = 0;
for (; i < sqlSelections.size(); i++) {
final SqlSelection sqlSelection = sqlSelections.get(i);
final ColumnReference columnReference = (ColumnReference) sqlSelection.getExpression();
final String selectAlias;
if (!columnReference.isColumnExpressionFormula()) {
// Skip over columns that are not selectable like in the fetch generation
while (!subclassColumnSelectableClosure[columnIndex]) {
columnIndex++;
}
selectAlias = columnAliases[columnIndex++] + suffix;
} else {
selectAlias = formulaAliases[formulaIndex++] + suffix;
}
sqlSelections.set(i, new SqlSelectionImpl(sqlSelection.getValuesArrayPosition(), sqlSelection.getJdbcResultSetIndex(), new AliasedExpression(sqlSelection.getExpression(), selectAlias)));
}
final String sql = getFactory().getJdbcServices().getDialect().getSqlAstTranslatorFactory().buildSelectTranslator(getFactory(), new SelectStatement(rootQuerySpec)).translate(null, QueryOptions.NONE).getSql();
final int fromIndex = sql.lastIndexOf(" from");
final String expression;
if (fromIndex != -1) {
expression = sql.substring("select ".length(), fromIndex);
} else {
expression = sql.substring("select ".length());
}
return expression;
}
use of org.hibernate.sql.ast.tree.from.TableGroup in project hibernate-orm by hibernate.
the class EntityValuedPathInterpretation method from.
public static <T> EntityValuedPathInterpretation<T> from(SqmEntityValuedSimplePath<T> sqmPath, MappingModelExpressible<?> inferredMapping, SqmToSqlAstConverter sqlAstCreationState) {
final TableGroup tableGroup = sqlAstCreationState.getFromClauseAccess().findTableGroup(sqmPath.getLhs().getNavigablePath());
final EntityValuedModelPart pathMapping = (EntityValuedModelPart) sqlAstCreationState.getFromClauseAccess().findTableGroup(sqmPath.getLhs().getNavigablePath()).getModelPart().findSubPart(sqmPath.getReferencedPathSource().getPathName(), null);
final EntityValuedModelPart mapping;
if (inferredMapping instanceof EntityAssociationMapping) {
final EntityAssociationMapping inferredAssociation = (EntityAssociationMapping) inferredMapping;
if (pathMapping instanceof EntityAssociationMapping && inferredMapping != pathMapping) {
// In here, the inferred mapping and the actual path mapping are association mappings,
// but for different associations, so we have to check if both associations point to the same target
final EntityAssociationMapping pathAssociation = (EntityAssociationMapping) pathMapping;
final ModelPart pathTargetPart = pathAssociation.getForeignKeyDescriptor().getPart(pathAssociation.getSideNature().inverse());
final ModelPart inferredTargetPart = inferredAssociation.getForeignKeyDescriptor().getPart(inferredAssociation.getSideNature().inverse());
// which will render the FK of the path association
if (pathTargetPart == inferredTargetPart) {
mapping = pathMapping;
} else {
// Otherwise, we need to use the entity mapping type to force rendering the PK
// for e.g. `a.assoc1 = a.assoc2` when both associations have different target join columns
mapping = pathMapping.getEntityMappingType();
}
} else {
// This is the case when the inferred mapping is an association, but the path mapping is not,
// or the path mapping and the inferred mapping are for the same association
mapping = (EntityValuedModelPart) inferredMapping;
}
} else {
mapping = pathMapping;
}
final ModelPart resultModelPart;
if (mapping instanceof EntityAssociationMapping) {
final EntityAssociationMapping associationMapping = (EntityAssociationMapping) mapping;
final ModelPart keyTargetMatchPart = associationMapping.getKeyTargetMatchPart();
if (keyTargetMatchPart instanceof ToOneAttributeMapping) {
resultModelPart = ((ToOneAttributeMapping) keyTargetMatchPart).getKeyTargetMatchPart();
} else {
resultModelPart = keyTargetMatchPart;
}
} else {
resultModelPart = mapping.getEntityMappingType().getIdentifierMapping();
}
return from(sqmPath.getNavigablePath(), tableGroup, resultModelPart, mapping, mapping, sqlAstCreationState);
}
Aggregations