use of com.blazebit.persistence.spi.ExtendedManagedType in project blaze-persistence by Blazebit.
the class JoinManager method implicitJoin.
private JoinResult implicitJoin(JoinNode current, List<String> resultFields, PathExpression pathExpression, ClauseType fromClause, JoinType joinType, JoinNode currentJoinNode, Set<String> currentlyResolvingAliases, int start, int end, boolean allowParentAliases, boolean joinAllowed, boolean singularJoinAllowed, boolean idRemovable) {
List<PathElementExpression> pathElements = pathExpression.getExpressions();
PathElementExpression elementExpr;
int singleValuedAssociationNameStartIndex = -1;
int singleValuedAssociationNameEndIndex = -1;
for (int i = start; i < end; i++) {
AliasInfo aliasInfo;
elementExpr = pathElements.get(i);
if (elementExpr instanceof ArrayExpression) {
ArrayExpression arrayExpr = (ArrayExpression) elementExpr;
String joinRelationName;
List<String> joinRelationAttributes;
if (!resultFields.isEmpty()) {
resultFields.add(arrayExpr.getBase().toString());
joinRelationAttributes = resultFields;
resultFields = new ArrayList<>();
joinRelationName = StringUtils.join(".", joinRelationAttributes);
} else {
joinRelationName = arrayExpr.getBase().toString();
joinRelationAttributes = Arrays.asList(joinRelationName);
}
current = current == null ? getRootNodeOrFail("Ambiguous join path [", joinRelationName, "] because of multiple root nodes!") : current;
implicitJoinIndex(arrayExpr);
// Find a node by a predicate match
JoinNode matchingNode = findNode(current, joinRelationName, arrayExpr);
if (matchingNode != null) {
current = matchingNode;
} else if (i == 0 && (aliasInfo = aliasManager.getAliasInfoForBottomLevel(joinRelationName)) != null) {
// The first node is allowed to be a join alias
if (aliasInfo instanceof SelectInfo) {
throw new IllegalArgumentException("Illegal reference to the select alias '" + joinRelationName + "'");
}
current = ((JoinAliasInfo) aliasInfo).getJoinNode();
generateAndApplyOnPredicate(current, arrayExpr);
} else {
String joinAlias = getJoinAlias(arrayExpr);
if (arrayExpr.getBase() instanceof PropertyExpression) {
final JoinResult result = createOrUpdateNode(current, joinRelationAttributes, null, joinAlias, joinType, currentJoinNode, true, false, joinAllowed, singularJoinAllowed);
current = result.baseNode;
resultFields = result.addToList(resultFields);
} else {
joinAlias = aliasManager.generateJoinAlias(joinAlias);
Class<?> entityClass = ((EntityLiteral) arrayExpr.getBase()).getValue();
joinOn(null, current.getAlias(), entityClass, joinAlias, JoinType.LEFT, false).end();
current = ((JoinAliasInfo) aliasManager.getAliasInfo(joinAlias)).getJoinNode();
}
generateAndApplyOnPredicate(current, arrayExpr);
}
} else if (elementExpr instanceof TreatExpression) {
if (i != 0 || current != null) {
throw new IllegalArgumentException("A treat expression should be the first element in a path!");
}
TreatExpression treatExpression = (TreatExpression) elementExpr;
boolean fromSubquery = false;
boolean fromSelectAlias = false;
boolean joinRequired = false;
boolean fetch = false;
current = implicitJoinTreatExpression((TreatExpression) elementExpr, joinAllowed, singularJoinAllowed, fromClause, joinType, currentJoinNode, currentlyResolvingAliases, fromSubquery, fromSelectAlias, true, false, fetch, false);
} else if (elementExpr instanceof MapKeyExpression) {
MapKeyExpression mapKeyExpression = (MapKeyExpression) elementExpr;
boolean fromSubquery = false;
boolean fromSelectAlias = false;
boolean joinRequired = true;
boolean fetch = false;
current = joinMapKey(mapKeyExpression, null, fromClause, currentlyResolvingAliases, fromSubquery, fromSelectAlias, joinRequired, fetch, true, true);
} else if (elementExpr instanceof MapValueExpression) {
MapValueExpression mapValueExpression = (MapValueExpression) elementExpr;
boolean fromSubquery = false;
boolean fromSelectAlias = false;
boolean joinRequired = true;
boolean fetch = false;
implicitJoin(mapValueExpression.getPath(), joinAllowed, singularJoinAllowed, true, null, fromClause, currentlyResolvingAliases, fromSubquery, fromSelectAlias, joinRequired, fetch);
current = (JoinNode) mapValueExpression.getPath().getBaseNode();
} else if (pathElements.size() == 1 && (aliasInfo = aliasManager.getAliasInfoForBottomLevel(elementExpr.toString())) != null) {
if (aliasInfo instanceof SelectInfo) {
throw new IllegalArgumentException("Can't dereference a select alias");
} else {
// Join alias usage like in "joinAlias.relationName"
current = ((JoinAliasInfo) aliasInfo).getJoinNode();
}
} else {
String elementExpressionString = elementExpr.toString();
if (current == null) {
// When no base is given, check if the attribute name is an alias
aliasInfo = allowParentAliases ? aliasManager.getAliasInfo(elementExpressionString) : aliasManager.getAliasInfoForBottomLevel(elementExpressionString);
if (aliasInfo instanceof JoinAliasInfo) {
current = ((JoinAliasInfo) aliasInfo).getJoinNode();
// We can only "consider" this path a single valued association id when we are about to "remove" the id part
if (idRemovable && current.getNodeType() instanceof ManagedType<?> && pathElements.size() == i + 2) {
ExtendedManagedType<?> managedType = metamodel.getManagedType(ExtendedManagedType.class, current.getManagedType());
if (contains(managedType.getIdAttributes(), pathElements.get(i + 1))) {
singleValuedAssociationNameStartIndex = 0;
singleValuedAssociationNameEndIndex = 0;
break;
}
}
continue;
} else {
current = getRootNodeOrFail("Ambiguous join path [", elementExpressionString, "] because of multiple root nodes!");
}
}
int pathElementsSize = pathElements.size();
if (joinType != JoinType.INNER && (idRemovable || mainQuery.jpaProvider.supportsSingleValuedAssociationIdExpressions()) && (current.getManagedType().getPersistenceType() != Type.PersistenceType.EMBEDDABLE || current.getValuesLikeAttribute() != null) && i + 1 < pathElementsSize) {
// If the current type is not an embeddable, we check if the path elements access a singular association id
List<PathElementExpression> pathElementExpressions = new ArrayList<>(pathElementsSize - i);
for (int j = i; j < pathElementsSize; j++) {
PathElementExpression pathElementExpression = pathElements.get(j);
if (!(pathElementExpression instanceof PropertyExpression)) {
break;
}
pathElementExpressions.add(pathElementExpression);
}
ExtendedManagedType<?> extendedManagedType = metamodel.getManagedType(ExtendedManagedType.class, current.getManagedType());
// We collect and check if we have only property expressions
if (pathElementExpressions.size() == pathElementsSize - i) {
// Only if all path elements are property expressions, we check if this is single valued association id
PathExpression pathRestExpression = new PathExpression(pathElementExpressions);
String pathRestString = pathRestExpression.toString();
int idx = 0;
ExtendedAttribute<?, ?> extendedAttribute;
if (current.getValuesLikeAttribute() == null) {
extendedAttribute = extendedManagedType.getOwnedSingularAttributes().get(pathRestString);
} else {
extendedAttribute = extendedManagedType.getAttributes().get(pathRestString);
}
if (extendedAttribute != null && !JpaMetamodelUtils.isAssociation(extendedAttribute.getAttribute())) {
ExtendedAttribute<?, ?> associationAttribute = null;
ExtendedAttribute<?, ?> attr;
singleValuedAssociationNameStartIndex = i;
List<String> newResultFields = new ArrayList<>();
for (int j = i; j < end; j++) {
idx = pathRestString.indexOf('.', idx + 1);
if (idx != -1 && JpaMetamodelUtils.isAssociation((attr = extendedManagedType.getAttribute(pathRestString.substring(0, idx))).getAttribute())) {
associationAttribute = attr;
singleValuedAssociationNameEndIndex = j;
}
newResultFields.add(pathElements.get(j).toString());
}
if (singleValuedAssociationNameEndIndex == -1) {
// The expression ends at an association, so this can't be a single valued association id expression
singleValuedAssociationNameStartIndex = -1;
} else if (current.getValueType() == null && mainQuery.jpaProvider.isForeignJoinColumn((EntityType<?>) current.getManagedType(), new PathExpression(pathElements.subList(singleValuedAssociationNameStartIndex, singleValuedAssociationNameEndIndex + 1)).toString()) || current.getValueType() != null && mainQuery.jpaProvider.isForeignJoinColumn(current.getValueType(), current.getValuesLikeAttribute() + "." + new PathExpression(pathElements.subList(singleValuedAssociationNameStartIndex, singleValuedAssociationNameEndIndex + 1)).toString())) {
// If the column is "foreign", we can't do any optimizations
singleValuedAssociationNameStartIndex = -1;
} else if (!mainQuery.jpaProvider.supportsSingleValuedAssociationNaturalIdExpressions() && !contains(metamodel.getManagedType(ExtendedManagedType.class, associationAttribute.getElementClass()), new PathExpression(pathElements.subList(singleValuedAssociationNameEndIndex + 1, pathElementsSize)))) {
// If the jpa provider doesn't support any optimizations, we are done
singleValuedAssociationNameStartIndex = -1;
} else {
resultFields.addAll(newResultFields);
break;
}
}
}
}
if (resultFields.isEmpty()) {
final JoinResult result = implicitJoinSingle(current, elementExpressionString, null, joinType, currentJoinNode, allowParentAliases, joinAllowed, singularJoinAllowed);
if (current != result.baseNode) {
current = result.baseNode;
}
resultFields = result.addToList(resultFields);
} else {
resultFields.add(elementExpressionString);
JoinResult currentResult = createOrUpdateNode(current, resultFields, null, null, joinType, currentJoinNode, true, true, joinAllowed, singularJoinAllowed);
current = currentResult.baseNode;
if (!currentResult.hasField()) {
resultFields.clear();
}
}
}
}
if (resultFields.isEmpty()) {
return new JoinResult(current, null, current == null ? null : current.getNodeType(), singleValuedAssociationNameStartIndex, singleValuedAssociationNameEndIndex);
} else {
List<PathElementExpression> pathElementExpressions = new ArrayList<>(resultFields.size());
for (int i = 0; i < resultFields.size(); i++) {
pathElementExpressions.add(new PropertyExpression(resultFields.get(i)));
}
Expression expression = new PathExpression(pathElementExpressions);
Type<?> type = JpaUtils.getAttributeForJoining(metamodel, current.getNodeType(), expression, null).getAttributeType();
return new JoinResult(current, resultFields, type, singleValuedAssociationNameStartIndex, singleValuedAssociationNameEndIndex);
}
}
use of com.blazebit.persistence.spi.ExtendedManagedType in project blaze-persistence by Blazebit.
the class ResolvingQueryGenerator method visit.
@Override
public void visit(PathExpression expression) {
if (resolveSelectAliases && expression.getExpressions().size() == 1) {
AliasInfo aliasInfo;
String potentialAlias = expression.getExpressions().get(0).toString();
try {
if (currentlyResolvingAliases.add(potentialAlias) && (aliasInfo = aliasManager.getAliasInfo(potentialAlias)) != null) {
if (aliasInfo instanceof SelectInfo) {
SelectInfo selectAliasInfo = (SelectInfo) aliasInfo;
if (selectAliasInfo.getExpression() instanceof PathExpression) {
PathExpression aliasedExpression = (PathExpression) selectAliasInfo.getExpression();
boolean collectionKeyPath = aliasedExpression.isCollectionQualifiedPath();
boolean usedInCollectionFunction = aliasedExpression.isUsedInCollectionFunction();
aliasedExpression.setCollectionQualifiedPath(expression.isCollectionQualifiedPath());
aliasedExpression.setUsedInCollectionFunction(expression.isUsedInCollectionFunction());
try {
selectAliasInfo.getExpression().accept(this);
} finally {
aliasedExpression.setCollectionQualifiedPath(collectionKeyPath);
aliasedExpression.setUsedInCollectionFunction(usedInCollectionFunction);
}
} else {
selectAliasInfo.getExpression().accept(this);
}
return;
}
}
} finally {
currentlyResolvingAliases.remove(potentialAlias);
}
}
JoinNode baseNode;
String field;
if ((baseNode = (JoinNode) expression.getBaseNode()) == null) {
super.visit(expression);
} else {
String collectionValueFunction = jpaProvider.getCollectionValueFunction();
if ((field = expression.getField()) == null) {
if (expression.isUsedInCollectionFunction() || renderAbsolutePath(expression)) {
super.visit(expression);
} else {
// NOTE: Hibernate uses the column from a join table if VALUE is used which is wrong, so drop the VALUE here
boolean valueFunction = collectionValueFunction != null && needsValueFunction(expression, baseNode, field);
if (valueFunction) {
sb.append(collectionValueFunction);
sb.append('(');
}
if (aliasPrefix != null) {
sb.append(aliasPrefix);
}
baseNode.appendAlias(sb, externalRepresentation);
if (valueFunction) {
sb.append(')');
}
}
} else {
List<JoinNode> treatedJoinNodes = baseNode.getJoinNodesForTreatConstraint();
Type<?> baseNodeType = baseNode.getBaseType();
boolean addTypeCaseWhen = false;
if (!treatedJoinNodes.isEmpty() && baseNodeType instanceof EntityType<?> && baseNode.getTreatType() != null && (jpaProvider.supportsSubtypePropertyResolving() || !jpaProvider.supportsRootTreat())) {
ExtendedManagedType<?> extendedManagedType = entityMetamodel.getManagedType(ExtendedManagedType.class, baseNode.getTreatType().getName());
ExtendedAttribute<?, ?> extendedAttribute = extendedManagedType.getAttributes().get(field);
if (extendedAttribute.isColumnShared()) {
// To disambiguate shared column access, we also must use the type constraint
addTypeCaseWhen = jpaProvider.needsTypeConstraintForColumnSharing();
} else {
// If the attribute is declared by an entity sub-type of the treat target type, we don't need the case when
ExtendedAttribute<?, ?> baseTypeAttribute = (ExtendedAttribute<?, ?>) entityMetamodel.getManagedType(ExtendedManagedType.class, ((EntityType<?>) baseNode.getBaseType()).getName()).getAttributes().get(field);
addTypeCaseWhen = baseTypeAttribute != null && !baseNode.getTreatType().getJavaType().isAssignableFrom(baseTypeAttribute.getAttributePath().get(0).getDeclaringType().getJavaType());
}
}
if (addTypeCaseWhen) {
sb.append("CASE WHEN ");
boolean first = true;
for (int i = 0; i < treatedJoinNodes.size(); i++) {
JoinNode treatedJoinNode = treatedJoinNodes.get(i);
// we skip the type constraint as that is already applied through the join
if (jpaProvider.supportsTreatJoin() && treatedJoinNode.isTreatJoinNode()) {
continue;
}
if (first) {
first = false;
} else {
sb.append(" AND ");
}
sb.append("TYPE(");
treatedJoinNode.appendAlias(sb, externalRepresentation);
sb.append(") IN (");
for (EntityType<?> entitySubtype : entityMetamodel.getEntitySubtypes(treatedJoinNode.getTreatType())) {
sb.append(entitySubtype.getName());
sb.append(", ");
}
sb.setLength(sb.length() - 2);
sb.append(')');
}
if (first) {
sb.setLength(sb.length() - "CASE WHEN ".length());
addTypeCaseWhen = false;
} else {
sb.append(" THEN ");
}
}
boolean valueFunction = collectionValueFunction != null && needsValueFunction(expression, baseNode, field);
// NOTE: There is no need to check for whether the JPA provider support implicit downcasting here
// If it didn't, the query building would have already failed before. Here we just decide whether to render the treat or not
boolean renderTreat = jpaProvider.supportsRootTreat();
if (valueFunction) {
sb.append(collectionValueFunction);
sb.append('(');
if (aliasPrefix != null) {
sb.append(aliasPrefix);
}
baseNode.appendAlias(sb, renderTreat, externalRepresentation);
sb.append(')');
sb.append(".").append(field);
} else {
if (aliasPrefix != null) {
sb.append(aliasPrefix);
}
baseNode.appendDeReference(sb, field, renderTreat, externalRepresentation, jpaProvider.needsElementCollectionIdCutoff());
}
if (addTypeCaseWhen) {
if (jpaProvider.needsCaseWhenElseBranch()) {
sb.append(" ELSE NULL");
}
sb.append(" END");
}
}
}
}
use of com.blazebit.persistence.spi.ExtendedManagedType in project blaze-persistence by Blazebit.
the class BlazeCriteriaBuilderRenderer method serializeSubQuery.
private Object serializeSubQuery(Object criteriaBuilder, Expression<?> expression) {
Object result = expression.accept(new Visitor<Object, Object>() {
@Override
public Object visit(Constant<?> constant, Object criteriaBuilder) {
throw new UnsupportedOperationException();
}
@Override
public Object visit(FactoryExpression<?> factoryExpression, Object criteriaBuilder) {
throw new UnsupportedOperationException();
}
@Override
public Object visit(Operation<?> setOperation, Object criteriaBuilder) {
Expression<?> lhs = setOperation.getArg(0);
SubQueryExpression<?> lhsSubquery = lhs.accept(GetSubQueryVisitor.INSTANCE, null);
SetOperationFlag setOperationFlag = lhsSubquery != null ? getSetOperationFlag(lhsSubquery.getMetadata()) : null;
boolean lhsNestedSet = setOperationFlag != null && LEFT_NESTED_SET_OPERATIONS.contains(setOperation.getOperator());
if (lhsNestedSet) {
if (criteriaBuilder instanceof StartOngoingSetOperationBuilder) {
StartOngoingSetOperationBuilder<?, ?, ?> ob = (StartOngoingSetOperationBuilder<?, ?, ?>) criteriaBuilder;
criteriaBuilder = ob.startSet();
} else if (criteriaBuilder instanceof SubqueryInitiator) {
SubqueryInitiator<?> subqueryInitiator = (SubqueryInitiator<?>) criteriaBuilder;
criteriaBuilder = subqueryInitiator.startSet();
} else {
criteriaBuilder = criteriaBuilderFactory.startSet(entityManager, Object.class);
}
criteriaBuilder = setOperationFlag.getFlag().accept(this, criteriaBuilder);
if (criteriaBuilder instanceof OngoingSetOperationBuilder) {
criteriaBuilder = ((OngoingSetOperationBuilder<?, ?, ?>) criteriaBuilder).endSetWith();
renderOrderBy(lhsSubquery.getMetadata(), (OrderByBuilder<?>) criteriaBuilder);
renderModifiers(lhsSubquery.getMetadata().getModifiers(), (LimitBuilder<?>) criteriaBuilder);
criteriaBuilder = ((BaseOngoingFinalSetOperationBuilder) criteriaBuilder).endSet();
} else {
throw new UnsupportedOperationException();
}
} else {
criteriaBuilder = lhs.accept(this, criteriaBuilder);
}
Expression<?> rhs = setOperation.getArg(1);
SubQueryExpression<?> rhsSubquery = rhs.accept(GetSubQueryVisitor.INSTANCE, null);
setOperationFlag = rhsSubquery != null ? getSetOperationFlag(rhsSubquery.getMetadata()) : null;
boolean isNestedSet = setOperationFlag != null;
SetOperationBuilder<?, ?> setOperationBuilder = (SetOperationBuilder<?, ?>) criteriaBuilder;
switch((JPQLNextOps) setOperation.getOperator()) {
// CHECKSTYLE:OFF: FallThrough
case SET_UNION:
case LEFT_NESTED_SET_UNION:
criteriaBuilder = isNestedSet ? setOperationBuilder.startUnion() : setOperationBuilder.union();
break;
case SET_UNION_ALL:
case LEFT_NESTED_SET_UNION_ALL:
criteriaBuilder = isNestedSet ? setOperationBuilder.startUnionAll() : setOperationBuilder.unionAll();
break;
case SET_EXCEPT:
case LEFT_NESTED_SET_EXCEPT:
criteriaBuilder = isNestedSet ? setOperationBuilder.startExcept() : setOperationBuilder.except();
break;
case SET_EXCEPT_ALL:
case LEFT_NESTED_SET_EXCEPT_ALL:
criteriaBuilder = isNestedSet ? setOperationBuilder.startExceptAll() : setOperationBuilder.exceptAll();
break;
case SET_INTERSECT:
case LEFT_NESTED_SET_INTERSECT:
criteriaBuilder = isNestedSet ? setOperationBuilder.startIntersect() : setOperationBuilder.intersect();
break;
case SET_INTERSECT_ALL:
case LEFT_NESTED_SET_INTERSECT_ALL:
criteriaBuilder = isNestedSet ? setOperationBuilder.startIntersectAll() : setOperationBuilder.intersectAll();
break;
default:
throw new UnsupportedOperationException("No support for set operation " + setOperation.getOperator());
}
if (isNestedSet) {
criteriaBuilder = setOperationFlag.getFlag().accept(this, criteriaBuilder);
if (criteriaBuilder instanceof OngoingSetOperationBuilder) {
criteriaBuilder = ((OngoingSetOperationBuilder<?, ?, ?>) criteriaBuilder).endSetWith();
renderOrderBy(rhsSubquery.getMetadata(), (OrderByBuilder<?>) criteriaBuilder);
renderModifiers(rhsSubquery.getMetadata().getModifiers(), (LimitBuilder<?>) criteriaBuilder);
criteriaBuilder = ((BaseOngoingFinalSetOperationBuilder) criteriaBuilder).endSet();
} else {
throw new UnsupportedOperationException();
}
} else {
criteriaBuilder = rhs.accept(this, criteriaBuilder);
}
return criteriaBuilder;
}
@Override
public Object visit(ParamExpression<?> paramExpression, Object criteriaBuilder) {
throw new UnsupportedOperationException();
}
@Override
public Object visit(Path<?> path, Object criteriaBuilder) {
throw new UnsupportedOperationException();
}
@Override
public Object visit(SubQueryExpression<?> subQuery, Object criteriaBuilder) {
QueryMetadata subQueryMetadata = subQuery.getMetadata();
SetOperationFlag setOperationFlag = getSetOperationFlag(subQueryMetadata);
if (setOperationFlag != null) {
return setOperationFlag.getFlag().accept(this, criteriaBuilder);
}
renderCTEs(subQueryMetadata);
criteriaBuilder = renderJoins(subQueryMetadata, (FromBaseBuilder) criteriaBuilder);
criteriaBuilder = renderNamedWindows(subQueryMetadata, (WindowContainerBuilder) criteriaBuilder);
renderDistinct(subQueryMetadata, (DistinctBuilder<?>) criteriaBuilder);
renderWhere(subQueryMetadata, (WhereBuilder<?>) criteriaBuilder);
renderGroupBy(subQueryMetadata, (GroupByBuilder<?>) criteriaBuilder);
renderHaving(subQueryMetadata, (HavingBuilder<?>) criteriaBuilder);
Expression<?> select = subQueryMetadata.getProjection();
if (select instanceof FactoryExpression<?> && criteriaBuilder instanceof FullQueryBuilder<?, ?>) {
FactoryExpression<T> factoryExpression = (FactoryExpression<T>) select;
FullQueryBuilder<?, ?> fullQueryBuilder = (FullQueryBuilder<?, ?>) criteriaBuilder;
criteriaBuilder = fullQueryBuilder.selectNew(new FactoryExpressionObjectBuilder(factoryExpression));
} else {
List<? extends Expression<?>> projection = expandProjection(subQueryMetadata.getProjection());
if (criteriaBuilder instanceof SelectBaseCTECriteriaBuilder) {
SelectBaseCTECriteriaBuilder<?> selectBaseCriteriaBuilder = (SelectBaseCTECriteriaBuilder<?>) criteriaBuilder;
boolean bindEntity = projection.size() == 1 && subQueryMetadata.getJoins().get(0).getTarget().accept(new JoinTargetAliasPathResolver(), null).equals(projection.get(0));
if (bindEntity) {
EntityMetamodel metamodel = criteriaBuilderFactory.getService(EntityMetamodel.class);
Path<?> pathExpression = (Path<?>) projection.get(0);
ExtendedManagedType<?> managedType = metamodel.getManagedType(ExtendedManagedType.class, pathExpression.getType());
Map<String, ? extends ExtendedAttribute<?, ?>> ownedSingularAttributes = managedType.getOwnedSingularAttributes();
for (Map.Entry<String, ? extends ExtendedAttribute<?, ?>> ownedSingularAttribute : ownedSingularAttributes.entrySet()) {
String attributeName = ownedSingularAttribute.getKey();
ExtendedAttribute<?, ?> attribute = ownedSingularAttribute.getValue();
if (!JpaMetamodelUtils.isAssociation(attribute.getAttribute())) {
final SelectBuilder<?> bindBuilder = selectBaseCriteriaBuilder.bind(attributeName);
BeanPath<?> beanPath = new BeanPath<Object>(attribute.getElementClass(), pathExpression, attributeName);
setExpressionSubqueries(beanPath, null, bindBuilder, SelectBuilderExpressionSetter.INSTANCE);
}
}
} else {
for (int i = 0; i < projection.size(); i++) {
Expression<?> projExpression = projection.get(i);
BindResolverContext bindResolverContext = new BindResolverContext();
projExpression = projExpression.accept(BindResolver.INSTANCE, bindResolverContext);
Path<?> cteAttribute = bindResolverContext.getCteAttribute();
String alias = bindResolverContext.getAliasString();
if (cteAttribute == null && cteAliases != null) {
cteAttribute = cteAliases.get(i);
}
if (cteAttribute != null) {
Path<?> cteEntityPath = cteAttribute.getRoot();
String relativeCteAttributePath = relativePathString(cteEntityPath, cteAttribute);
final SelectBuilder<?> bindBuilder = selectBaseCriteriaBuilder.bind(relativeCteAttributePath);
setExpressionSubqueries(projExpression, alias, bindBuilder, SelectBuilderExpressionSetter.INSTANCE);
} else {
throw new UnsupportedOperationException("Select statement should be bound to any CTE attribute");
}
}
}
} else {
for (Expression<?> selection : projection) {
renderSingleSelect(selection, (SelectBuilder<?>) criteriaBuilder);
}
}
}
renderOrderBy(subQueryMetadata, (OrderByBuilder<?>) criteriaBuilder);
renderParameters(subQueryMetadata, (ParameterHolder<?>) criteriaBuilder);
// Limit / offset on full query is set outside of the renderer, based on whether we're rendering a full count query or not
if (!(criteriaBuilder instanceof Queryable)) {
renderModifiers(subQueryMetadata.getModifiers(), (LimitBuilder<?>) criteriaBuilder);
}
return criteriaBuilder;
}
@Override
public Object visit(TemplateExpression<?> templateExpression, Object criteriaBuilder) {
throw new UnsupportedOperationException();
}
}, criteriaBuilder);
if (result instanceof BaseOngoingSetOperationBuilder) {
result = ((BaseOngoingSetOperationBuilder<?, ?, ?>) result).endSet();
}
if ((result instanceof FinalSetOperationCriteriaBuilder || result instanceof FinalSetOperationCTECriteriaBuilder || result instanceof FinalSetOperationSubqueryBuilder) && expression instanceof SubQueryExpression<?>) {
QueryMetadata metadata = ((SubQueryExpression<?>) expression).getMetadata();
renderOrderBy(metadata, (OrderByBuilder<?>) result);
// Limit / offset on full query is set outside of the renderer, based on whether we're rendering a full count query or not
if (!(criteriaBuilder instanceof Queryable)) {
renderModifiers(metadata.getModifiers(), (LimitBuilder<?>) result);
}
}
return result;
}
use of com.blazebit.persistence.spi.ExtendedManagedType in project blaze-persistence by Blazebit.
the class EntityViewUpdaterImpl method createPluralAttributeSubFlusher.
private DirtyAttributeFlusher<?, ?, ?> createPluralAttributeSubFlusher(EntityViewManagerImpl evm, Map<Object, EntityViewUpdaterImpl> localCache, ManagedViewTypeImplementor<?> viewType, AbstractMethodAttribute<?, ?> attribute, String name, Type<?> type, EntityViewUpdaterImpl owner, String ownerMapping) {
EntityMetamodel entityMetamodel = evm.getMetamodel().getEntityMetamodel();
String attributeName = attribute.getName() + "_" + name;
String attributeMapping = attribute.getMapping();
String attributeLocation = attribute.getLocation();
Set<Type<?>> readOnlyAllowedSubtypes = attribute.getReadOnlyAllowedSubtypes();
Set<Type<?>> persistAllowedSubtypes = attribute.getPersistCascadeAllowedSubtypes();
Set<Type<?>> updateAllowedSubtypes = attribute.getUpdateCascadeAllowedSubtypes();
if (type instanceof ManagedViewType<?>) {
ManagedViewTypeImplementor<?> subviewType = (ManagedViewTypeImplementor<?>) type;
if (!(subviewType.getJpaManagedType() instanceof EntityType<?>)) {
// A singular attribute where the subview refers to an embeddable type
EmbeddableUpdaterBasedViewToEntityMapper viewToEntityMapper = new EmbeddableUpdaterBasedViewToEntityMapper(attributeLocation, evm, subviewType.getJavaType(), readOnlyAllowedSubtypes, persistAllowedSubtypes, updateAllowedSubtypes, EntityLoaders.referenceLoaderForAttribute(evm, localCache, subviewType, attribute), false, null, owner, ownerMapping == null ? attributeMapping : ownerMapping + "." + attributeMapping, localCache);
String parameterName = attributeName + "_";
String updateFragment = attributeMapping + ".";
return new EmbeddableAttributeFlusher<>(attributeName, attributeMapping, updateFragment, parameterName, false, false, false, null, null, viewToEntityMapper);
} else {
ViewTypeImplementor<?> attributeViewType = (ViewTypeImplementor<?>) subviewType;
InitialValueAttributeAccessor viewAttributeAccessor = Accessors.forMutableViewAttribute(evm, attribute);
AttributeAccessor subviewIdAccessor;
ManagedType<?> ownerManagedType = owner == null ? viewType.getJpaManagedType() : owner.managedViewType.getJpaManagedType();
EntityType<?> ownerEntityType = ownerManagedType instanceof EntityType<?> ? (EntityType<?>) ownerManagedType : null;
String attributeElementIdMapping;
if (ownerEntityType != null && attribute.getMapping() != null) {
ExtendedManagedType<?> extendedManagedType = evm.getMetamodel().getEntityMetamodel().getManagedType(ExtendedManagedType.class, attributeViewType.getEntityClass());
attributeElementIdMapping = TypeDescriptor.getAttributeElementIdentifier(evm, ownerEntityType, attribute.getName(), ownerMapping, attribute.getMapping(), extendedManagedType.getType());
} else {
attributeElementIdMapping = ((MappingAttribute<?, ?>) attributeViewType.getIdAttribute()).getMapping();
}
subviewIdAccessor = Accessors.forSubviewAssociationId(evm, attributeViewType, attributeElementIdMapping, true);
Attribute<?, ?> attributeIdAttribute = attributeViewType.getJpaManagedType().getAttribute(attributeElementIdMapping);
javax.persistence.metamodel.Type<?> attributeIdAttributeType = entityMetamodel.type(JpaMetamodelUtils.resolveFieldClass(attributeViewType.getEntityClass(), attributeIdAttribute));
List<String> idComponentMappings;
boolean requiresComponentWiseSetInUpdate = true;
if (requiresComponentWiseSetInUpdate && attributeIdAttributeType instanceof EmbeddableType<?>) {
// If the identifier used for the association is an embeddable, we must collect the individual attribute components since updates don't work on embeddables directly
Set<Attribute<?, ?>> idAttributeComponents = (Set<Attribute<?, ?>>) ((EmbeddableType<?>) attributeIdAttributeType).getAttributes();
idComponentMappings = new ArrayList<>(idAttributeComponents.size());
for (Attribute<?, ?> idAttributeComponent : idAttributeComponents) {
idComponentMappings.add(attributeMapping + "." + attributeElementIdMapping + "." + idAttributeComponent);
}
} else {
idComponentMappings = Collections.singletonList(attributeMapping + "." + attributeElementIdMapping);
}
String[] idAttributeMappings = idComponentMappings.toArray(new String[idComponentMappings.size()]);
ViewToEntityMapper viewToEntityMapper = createViewToEntityMapper(attributeLocation, evm, localCache, ownerEntityType, attributeName, attributeMapping, attributeViewType, false, false, readOnlyAllowedSubtypes, persistAllowedSubtypes, updateAllowedSubtypes, EntityLoaders.referenceLoaderForAttribute(evm, localCache, attributeViewType, attribute.getViewTypes(), attributeElementIdMapping), owner, ownerMapping);
String parameterName = attributeName;
return new SubviewAttributeFlusher<>(attributeName, attributeMapping, false, true, false, false, false, subviewType.getConverter(), false, idAttributeMappings, parameterName, false, owner != null, null, viewAttributeAccessor, subviewIdAccessor, viewToEntityMapper, null, null);
}
} else {
TypeDescriptor elementDescriptor = TypeDescriptor.forType(evm, localCache, this, attribute, type, owner, ownerMapping);
String parameterName = attributeName;
String updateFragment = attributeMapping;
// TODO: Why?
boolean supportsQueryFlush = !elementDescriptor.isJpaEmbeddable();
return new BasicAttributeFlusher<>(attributeName, attributeMapping, supportsQueryFlush, false, true, false, false, false, null, elementDescriptor, updateFragment, parameterName, null, null, null, null, null);
}
}
use of com.blazebit.persistence.spi.ExtendedManagedType in project blaze-persistence by Blazebit.
the class TypeDescriptor method forType.
public static TypeDescriptor forType(EntityViewManagerImpl evm, Map<Object, EntityViewUpdaterImpl> localCache, EntityViewUpdaterImpl updater, AbstractMethodAttribute<?, ?> attribute, Type<?> type, EntityViewUpdaterImpl owner, String ownerMapping) {
EntityMetamodel entityMetamodel = evm.getMetamodel().getEntityMetamodel();
String attributeLocation = attribute.getLocation();
boolean cascadePersist = attribute.isPersistCascaded();
boolean cascadeUpdate = attribute.isUpdateCascaded();
Set<Type<?>> readOnlyAllowedSubtypes = attribute.getReadOnlyAllowedSubtypes();
Set<Type<?>> persistAllowedSubtypes = attribute.getPersistCascadeAllowedSubtypes();
Set<Type<?>> updateAllowedSubtypes = attribute.getUpdateCascadeAllowedSubtypes();
EntityToEntityMapper entityToEntityMapper = null;
ViewToEntityMapper viewToEntityMapper = null;
ViewToEntityMapper loadOnlyViewToEntityMapper = null;
final ManagedType<?> managedType = entityMetamodel.getManagedType(type.getJavaType());
final boolean jpaEntity = managedType instanceof EntityType<?>;
final boolean jpaManaged = managedType != null;
final boolean mutable;
final boolean identifiable;
TypeConverter<Object, Object> converter = (TypeConverter<Object, Object>) type.getConverter();
BasicUserType<Object> basicUserType;
ManagedType<?> ownerEntityType = owner == null ? attribute.getDeclaringType().getJpaManagedType() : owner.getManagedViewType().getJpaManagedType();
String attributeIdAttributeName = null;
String entityIdAttributeName = null;
Class<?> jpaType;
// TODO: currently we only check if the declared type is mutable, but we have to let the collection flusher which types are considered updatable/creatable
if (type instanceof BasicType<?>) {
jpaType = type.getJavaType();
basicUserType = (BasicUserType<Object>) ((BasicType<?>) type).getUserType();
mutable = basicUserType.isMutable();
identifiable = jpaEntity || !jpaManaged;
if (jpaEntity) {
Map<String, Map<?, ?>> fetchGraph = null;
UnmappedAttributeCascadeDeleter deleter = null;
// We only need to know the fetch graph when we actually do updates
if (cascadeUpdate) {
fetchGraph = getFetchGraph(attribute.getFetches(), attribute.getMapping(), managedType);
}
if (ownerEntityType instanceof EntityType<?>) {
ExtendedManagedType<?> extendedManagedType = evm.getMetamodel().getEntityMetamodel().getManagedType(ExtendedManagedType.class, type.getJavaType());
entityIdAttributeName = extendedManagedType.getIdAttribute().getName();
attributeIdAttributeName = getAttributeElementIdentifier(evm, (EntityType<?>) ownerEntityType, attribute.getName(), ownerMapping, attribute.getMapping(), extendedManagedType.getType());
// Only construct when orphanRemoval or delete cascading is enabled, orphanRemoval implies delete cascading
if (attribute.isDeleteCascaded()) {
String mapping = attribute.getMapping();
ExtendedManagedType elementManagedType = entityMetamodel.getManagedType(ExtendedManagedType.class, attribute.getDeclaringType().getJpaManagedType());
deleter = new UnmappedBasicAttributeCascadeDeleter(evm, mapping, elementManagedType.getAttribute(mapping), mapping + "." + attributeIdAttributeName, false);
}
}
// TODO: EntityToEntityMapper should also use the attributeIdAttributeName
entityToEntityMapper = new DefaultEntityToEntityMapper(cascadePersist, cascadeUpdate, jpaType, basicUserType, new DefaultEntityLoaderFetchGraphNode(evm, attribute.getName(), (EntityType<?>) managedType, fetchGraph), deleter);
}
} else {
ManagedViewTypeImplementor<?> elementType = (ManagedViewTypeImplementor<?>) type;
jpaType = elementType.getEntityClass();
basicUserType = null;
mutable = elementType.isUpdatable() || elementType.isCreatable() || !persistAllowedSubtypes.isEmpty() || !updateAllowedSubtypes.isEmpty();
Set<ManagedViewType<?>> viewTypes = attribute.getViewTypes();
if (elementType.getJpaManagedType() instanceof EntityType<?> && ownerEntityType instanceof EntityType<?>) {
ExtendedManagedType<?> extendedManagedType = evm.getMetamodel().getEntityMetamodel().getManagedType(ExtendedManagedType.class, elementType.getEntityClass());
entityIdAttributeName = extendedManagedType.getIdAttribute().getName();
attributeIdAttributeName = getAttributeElementIdentifier(evm, (EntityType<?>) ownerEntityType, attribute.getName(), ownerMapping, attribute.getMapping(), extendedManagedType.getType());
}
viewToEntityMapper = createViewToEntityMapper(evm, localCache, updater, attributeIdAttributeName, attribute.getMapping(), attributeLocation, elementType, cascadePersist, cascadeUpdate, readOnlyAllowedSubtypes, persistAllowedSubtypes, updateAllowedSubtypes, EntityLoaders.referenceLoaderForAttribute(evm, localCache, elementType, viewTypes, attributeIdAttributeName), owner, ownerMapping);
loadOnlyViewToEntityMapper = createLoadOnlyViewToEntityMapper(attributeLocation, evm, localCache, attributeIdAttributeName, attribute.getMapping(), elementType, cascadePersist, cascadeUpdate, readOnlyAllowedSubtypes, persistAllowedSubtypes, updateAllowedSubtypes, EntityLoaders.referenceLoaderForAttribute(evm, localCache, elementType, viewTypes, attributeIdAttributeName), owner, ownerMapping);
identifiable = viewToEntityMapper.getViewIdAccessor() != null;
}
final boolean shouldJpaMerge = jpaEntity && mutable && cascadeUpdate;
final boolean shouldJpaPersist = jpaEntity && mutable && cascadePersist;
final boolean shouldFlushUpdates = cascadeUpdate && !updateAllowedSubtypes.isEmpty();
final boolean shouldFlushPersists = cascadePersist && !persistAllowedSubtypes.isEmpty();
return new TypeDescriptor(mutable, identifiable, jpaManaged, jpaEntity, shouldJpaMerge, shouldJpaPersist, shouldFlushPersists, shouldFlushUpdates, entityIdAttributeName, attributeIdAttributeName, jpaType, converter, basicUserType, entityToEntityMapper, viewToEntityMapper, loadOnlyViewToEntityMapper);
}
Aggregations