Search in sources :

Example 1 with JoinNode

use of com.blazebit.persistence.impl.JoinNode in project blaze-persistence by Blazebit.

the class SizeTransformationVisitor method generateSubquery.

private SubqueryExpression generateSubquery(PathExpression sizeArg) {
    JoinNode sizeArgJoin = (JoinNode) sizeArg.getBaseNode();
    final Type<?> nodeType = sizeArgJoin.getNodeType();
    if (!(nodeType instanceof EntityType<?>)) {
        throw new IllegalArgumentException("Size on a collection owned by a non-entity type is not supported yet: " + sizeArg);
    }
    final EntityType<?> startType = (EntityType<?>) nodeType;
    Subquery countSubquery = (Subquery) subqueryInitFactory.createSubqueryInitiator(null, new SubqueryBuilderListenerImpl<>(), false, getClause()).from(sizeArg.getPathReference().toString()).select("COUNT(*)");
    for (SingularAttribute<?, ?> idAttribute : JpaMetamodelUtils.getIdAttributes(startType)) {
        String groupByExprString = sizeArgJoin.getAlias() + "." + idAttribute.getName();
        ResolvedExpression groupByExpr = new ResolvedExpression(groupByExprString, null);
        Set<ClauseType> clauseTypes = subqueryGroupBys.get(groupByExpr);
        if (clauseTypes == null) {
            List<PathElementExpression> pathElementExpressions = new ArrayList<>(2);
            pathElementExpressions.add(new PropertyExpression(sizeArgJoin.getAlias()));
            pathElementExpressions.add(new PropertyExpression(idAttribute.getName()));
            PathExpression pathExpression = new PathExpression(pathElementExpressions);
            pathExpression.setPathReference(new SimplePathReference(sizeArgJoin, idAttribute.getName(), metamodel.type(JpaMetamodelUtils.resolveFieldClass(startType.getJavaType(), idAttribute))));
            groupByExpr = new ResolvedExpression(groupByExprString, pathExpression);
            subqueryGroupBys.put(groupByExpr, EnumSet.of(clause));
        } else {
            clauseTypes.add(clause);
        }
    }
    return new SubqueryExpression(countSubquery);
}
Also used : JoinNode(com.blazebit.persistence.impl.JoinNode) SubqueryBuilderListenerImpl(com.blazebit.persistence.impl.SubqueryBuilderListenerImpl) ResolvedExpression(com.blazebit.persistence.impl.ResolvedExpression) ArrayList(java.util.ArrayList) Subquery(com.blazebit.persistence.parser.expression.Subquery) SubqueryExpression(com.blazebit.persistence.parser.expression.SubqueryExpression) EntityType(javax.persistence.metamodel.EntityType) PathElementExpression(com.blazebit.persistence.parser.expression.PathElementExpression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) PropertyExpression(com.blazebit.persistence.parser.expression.PropertyExpression) SimplePathReference(com.blazebit.persistence.impl.SimplePathReference) ClauseType(com.blazebit.persistence.impl.ClauseType)

Example 2 with JoinNode

use of com.blazebit.persistence.impl.JoinNode in project blaze-persistence by Blazebit.

the class SizeTransformationVisitor method visit.

@Override
public Boolean visit(PathExpression expression) {
    if (orderBySelectClause) {
        LateJoinEntry lateJoinEntry = lateJoins.get(getJoinLookupKey(expression));
        if (lateJoinEntry != null) {
            lateJoinEntry.getClauseDependencies().add(ClauseType.ORDER_BY);
        }
    }
    if (clause == ClauseType.SELECT) {
        // for the select clause we blacklist all the join nodes that are required by other select items
        JoinNode current = (JoinNode) expression.getBaseNode();
        while (current != null) {
            joinNodeBlacklist.add(current);
            current = current.getParent();
        }
    }
    return super.visit(expression);
}
Also used : JoinNode(com.blazebit.persistence.impl.JoinNode)

Example 3 with JoinNode

use of com.blazebit.persistence.impl.JoinNode in project blaze-persistence by Blazebit.

the class SizeTransformationVisitor method getSizeExpression.

private Expression getSizeExpression(ExpressionModifier parentModifier, PathExpression sizeArg) {
    JoinNode sizeArgJoin = (JoinNode) sizeArg.getBaseNode();
    String property = sizeArg.getPathReference().getField();
    final Type<?> nodeType = ((JoinNode) sizeArg.getBaseNode()).getNodeType();
    if (!(nodeType instanceof EntityType<?>)) {
        throw new IllegalArgumentException("Size on a collection owned by a non-entity type is not supported yet: " + sizeArg);
    }
    final EntityType<?> startType = (EntityType<?>) nodeType;
    AttributeHolder result = JpaUtils.getAttributeForJoining(metamodel, sizeArg);
    PluralAttribute<?, ?, ?> targetAttribute = (PluralAttribute<?, ?, ?>) result.getAttribute();
    if (targetAttribute == null) {
        throw new RuntimeException("Attribute [" + property + "] not found on class " + startType.getJavaType().getName());
    }
    final PluralAttribute.CollectionType collectionType = targetAttribute.getCollectionType();
    final boolean isElementCollection = jpaProvider.getJpaMetamodelAccessor().isElementCollection(targetAttribute);
    boolean subqueryRequired;
    if (isElementCollection) {
        subqueryRequired = false;
    } else {
        ManagedType<?> managedTargetType = (ManagedType<?>) result.getAttributeType();
        if (managedTargetType instanceof EntityType<?>) {
            // we could also generate counts for collections with embeddable id but we do not implement this for now
            subqueryRequired = ((EntityType<?>) managedTargetType).getIdType().getPersistenceType() == PersistenceType.EMBEDDABLE;
        } else {
            throw new RuntimeException("Path [" + sizeArg.toString() + "] does not refer to a collection");
        }
    }
    // build group by id clause
    List<PathExpression> groupByExprs = new ArrayList<>();
    for (SingularAttribute<?, ?> idAttribute : JpaMetamodelUtils.getIdAttributes(startType)) {
        List<PathElementExpression> pathElementExpr = new ArrayList<>(2);
        pathElementExpr.add(new PropertyExpression(sizeArgJoin.getAlias()));
        pathElementExpr.add(new PropertyExpression(idAttribute.getName()));
        PathExpression groupByExpr = new PathExpression(pathElementExpr);
        groupByExprs.add(groupByExpr);
    }
    subqueryRequired = subqueryRequired || // we could also generate counts for collections with IdClass attributes but we do not implement this for now
    !startType.hasSingleIdAttribute() || joinManager.getRoots().size() > 1 || clause == ClauseType.JOIN || !isCountTransformationEnabled() || // for now, we always generate a subquery when a bag is encountered
    jpaProvider.isBag((EntityType<?>) targetAttribute.getDeclaringType(), targetAttribute.getName()) || requiresBlacklistedNode(sizeArg) || aggregateFunctionContext;
    if (subqueryRequired) {
        return wrapSubqueryConditionally(generateSubquery(sizeArg), aggregateFunctionContext);
    } else {
        if (currentJoinNode != null && (!currentJoinNode.equals(sizeArgJoin))) {
            int currentJoinDepth = currentJoinNode.getJoinDepth();
            int sizeArgJoinDepth = sizeArgJoin.getJoinDepth();
            if (currentJoinDepth > sizeArgJoinDepth) {
                return wrapSubqueryConditionally(generateSubquery(sizeArg), aggregateFunctionContext);
            } else {
                // we have to change all transformed expressions to subqueries
                for (TransformedExpressionEntry transformedExpressionEntry : transformedExpressions) {
                    PathExpression originalSizeArg = transformedExpressionEntry.getOriginalSizeArg();
                    Expression subquery = wrapSubqueryConditionally(generateSubquery(originalSizeArg), transformedExpressionEntry.isAggregateFunctionContext());
                    transformedExpressionEntry.getParentModifier().set(subquery);
                }
                transformedExpressions.clear();
                requiredGroupBys.clear();
                lateJoins.clear();
                distinctRequired = false;
                if (currentJoinDepth == sizeArgJoinDepth) {
                    return wrapSubqueryConditionally(generateSubquery(sizeArg), aggregateFunctionContext);
                }
            }
        }
        for (PathExpression groupByExpr : groupByExprs) {
            joinManager.implicitJoin(groupByExpr, true, true, true, null, null, new HashSet<String>(), false, false, false, false);
        }
        PathExpression originalSizeArg = sizeArg.copy(ExpressionCopyContext.EMPTY);
        originalSizeArg.setPathReference(sizeArg.getPathReference());
        sizeArg.setUsedInCollectionFunction(false);
        List<Expression> countArguments = new ArrayList<>();
        String joinLookupKey = getJoinLookupKey(sizeArg);
        LateJoinEntry lateJoin = lateJoins.get(joinLookupKey);
        if (lateJoin == null) {
            lateJoin = new LateJoinEntry();
            lateJoins.put(joinLookupKey, lateJoin);
        }
        lateJoin.getExpressionsToJoin().add(sizeArg);
        lateJoin.getClauseDependencies().add(clause);
        if ((isElementCollection && collectionType != PluralAttribute.CollectionType.MAP) || collectionType == PluralAttribute.CollectionType.SET) {
            if (IDENTIFIABLE_PERSISTENCE_TYPES.contains(targetAttribute.getElementType().getPersistenceType()) && targetAttribute.isCollection()) {
                // append id attribute name of joinable size argument
                PluralAttribute<?, ?, ?> sizeArgTargetAttribute = (PluralAttribute<?, ?, ?>) JpaMetamodelUtils.getAttribute(startType, sizeArg.getPathReference().getField());
                for (Attribute<?, ?> idAttribute : JpaMetamodelUtils.getIdAttributes(((IdentifiableType<?>) sizeArgTargetAttribute.getElementType()))) {
                    List<PathElementExpression> pathElementExpressions = new ArrayList<>(sizeArg.getExpressions().size() + 1);
                    pathElementExpressions.addAll(sizeArg.getExpressions());
                    pathElementExpressions.add(new PropertyExpression(idAttribute.getName()));
                    PathExpression pathExpression = new PathExpression(pathElementExpressions);
                    countArguments.add(pathExpression);
                    lateJoin.getExpressionsToJoin().add(pathExpression);
                }
            } else {
                countArguments.add(sizeArg);
            }
        } else {
            sizeArg.setCollectionQualifiedPath(true);
            if (collectionType == PluralAttribute.CollectionType.LIST) {
                countArguments.add(new ListIndexExpression(sizeArg));
            } else {
                countArguments.add(new MapKeyExpression(sizeArg));
            }
        }
        AggregateExpression countExpr = createCountFunction(distinctRequired, countArguments);
        transformedExpressions.add(new TransformedExpressionEntry(countExpr, originalSizeArg, parentModifier, aggregateFunctionContext));
        currentJoinNode = (JoinNode) originalSizeArg.getBaseNode();
        if (!distinctRequired) {
            if (lateJoins.size() + joinManager.getCollectionJoins().size() > 1) {
                distinctRequired = true;
                /*
                     *  As soon as we encounter another collection join, set previously
                     *  performed transformations to distinct.
                     */
                for (TransformedExpressionEntry transformedExpressionEntry : transformedExpressions) {
                    AggregateExpression transformedExpr = transformedExpressionEntry.getTransformedExpression();
                    if (ExpressionUtils.isCustomFunctionInvocation(transformedExpr) && AbstractCountFunction.FUNCTION_NAME.equalsIgnoreCase(((StringLiteral) transformedExpr.getExpressions().get(0)).getValue())) {
                        Expression possibleDistinct = transformedExpr.getExpressions().get(1);
                        if (!(possibleDistinct instanceof StringLiteral) || !AbstractCountFunction.DISTINCT_QUALIFIER.equals(((StringLiteral) possibleDistinct).getValue())) {
                            transformedExpr.getExpressions().add(1, new StringLiteral(AbstractCountFunction.DISTINCT_QUALIFIER));
                        }
                    } else {
                        transformedExpr.setDistinct(true);
                    }
                }
            }
        }
        for (Expression groupByExpr : groupByExprs) {
            String groupByExprString = groupByExpr.toString();
            ResolvedExpression resolvedExpression = new ResolvedExpression(groupByExprString, groupByExpr);
            Set<ClauseType> clauseTypes = requiredGroupBys.get(resolvedExpression);
            if (clauseTypes == null) {
                requiredGroupBys.put(resolvedExpression, EnumSet.of(clause));
            } else {
                clauseTypes.add(clause);
            }
        }
        return countExpr;
    }
}
Also used : ArrayList(java.util.ArrayList) ResolvedExpression(com.blazebit.persistence.impl.ResolvedExpression) MapKeyExpression(com.blazebit.persistence.parser.expression.MapKeyExpression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) ListIndexExpression(com.blazebit.persistence.parser.expression.ListIndexExpression) PropertyExpression(com.blazebit.persistence.parser.expression.PropertyExpression) AttributeHolder(com.blazebit.persistence.impl.AttributeHolder) ManagedType(javax.persistence.metamodel.ManagedType) IdentifiableType(javax.persistence.metamodel.IdentifiableType) PluralAttribute(javax.persistence.metamodel.PluralAttribute) JoinNode(com.blazebit.persistence.impl.JoinNode) AggregateExpression(com.blazebit.persistence.parser.expression.AggregateExpression) EntityType(javax.persistence.metamodel.EntityType) PathElementExpression(com.blazebit.persistence.parser.expression.PathElementExpression) StringLiteral(com.blazebit.persistence.parser.expression.StringLiteral) ListIndexExpression(com.blazebit.persistence.parser.expression.ListIndexExpression) Expression(com.blazebit.persistence.parser.expression.Expression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) AggregateExpression(com.blazebit.persistence.parser.expression.AggregateExpression) PropertyExpression(com.blazebit.persistence.parser.expression.PropertyExpression) ResolvedExpression(com.blazebit.persistence.impl.ResolvedExpression) FunctionExpression(com.blazebit.persistence.parser.expression.FunctionExpression) SubqueryExpression(com.blazebit.persistence.parser.expression.SubqueryExpression) MapKeyExpression(com.blazebit.persistence.parser.expression.MapKeyExpression) PathElementExpression(com.blazebit.persistence.parser.expression.PathElementExpression) ClauseType(com.blazebit.persistence.impl.ClauseType)

Aggregations

JoinNode (com.blazebit.persistence.impl.JoinNode)3 ClauseType (com.blazebit.persistence.impl.ClauseType)2 ResolvedExpression (com.blazebit.persistence.impl.ResolvedExpression)2 PathElementExpression (com.blazebit.persistence.parser.expression.PathElementExpression)2 PathExpression (com.blazebit.persistence.parser.expression.PathExpression)2 PropertyExpression (com.blazebit.persistence.parser.expression.PropertyExpression)2 SubqueryExpression (com.blazebit.persistence.parser.expression.SubqueryExpression)2 ArrayList (java.util.ArrayList)2 EntityType (javax.persistence.metamodel.EntityType)2 AttributeHolder (com.blazebit.persistence.impl.AttributeHolder)1 SimplePathReference (com.blazebit.persistence.impl.SimplePathReference)1 SubqueryBuilderListenerImpl (com.blazebit.persistence.impl.SubqueryBuilderListenerImpl)1 AggregateExpression (com.blazebit.persistence.parser.expression.AggregateExpression)1 Expression (com.blazebit.persistence.parser.expression.Expression)1 FunctionExpression (com.blazebit.persistence.parser.expression.FunctionExpression)1 ListIndexExpression (com.blazebit.persistence.parser.expression.ListIndexExpression)1 MapKeyExpression (com.blazebit.persistence.parser.expression.MapKeyExpression)1 StringLiteral (com.blazebit.persistence.parser.expression.StringLiteral)1 Subquery (com.blazebit.persistence.parser.expression.Subquery)1 IdentifiableType (javax.persistence.metamodel.IdentifiableType)1