Search in sources :

Example 16 with ExtendedManagedType

use of com.blazebit.persistence.spi.ExtendedManagedType in project blaze-persistence by Blazebit.

the class FunctionalDependencyAnalyzerVisitor method visit.

@Override
public Boolean visit(PathExpression expr) {
    PathReference pathReference = expr.getPathReference();
    if (pathReference == null) {
        Expression aliasedExpression = ((SelectInfo) aliasManager.getAliasInfo(expr.toString())).getExpression();
        return aliasedExpression.accept(this);
    }
    JoinNode baseNode = (JoinNode) pathReference.getBaseNode();
    String field = pathReference.getField();
    if (field == null) {
        lastJoinNode = baseNode;
        functionalDependencyRootExpressions.put(baseNode, Collections.singletonList(currentResolvedExpression));
        // This is a basic element collection. The element is it's unique key
        if (baseNode.getType().getPersistenceType() == Type.PersistenceType.BASIC) {
            return true;
        }
        // The key of a collection is it's unique key
        if (inKey) {
            return true;
        }
        field = baseNode.getParentTreeNode().getRelationName();
        baseNode = baseNode.getParent();
    }
    // First we check if the target attribute is unique, if it isn't, we don't need to check the join structure
    ExtendedManagedType<?> managedType = metamodel.getManagedType(ExtendedManagedType.class, baseNode.getManagedType());
    Attribute attr = managedType.getAttribute(field).getAttribute();
    if (attr instanceof PluralAttribute<?, ?, ?>) {
        lastJoinNode = baseNode;
        if (inKey) {
            registerFunctionalDependencyRootExpression(baseNode);
            return true;
        }
        throw new IllegalArgumentException("Ordering by plural attribute '" + expr + "' does not make sense! Please order by it's id instead!");
    }
    // Right now we only support ids, but we actually should check for unique constraints
    boolean isEmbeddedIdPart = false;
    SingularAttribute<?, ?> singularAttr = (SingularAttribute<?, ?>) attr;
    if (!singularAttr.isId() && !(isEmbeddedIdPart = isEmbeddedIdPart(baseNode, field, singularAttr))) {
        registerFunctionalDependencyRootExpression(baseNode);
        return false;
    }
    Object baseNodeKey;
    // Check if we have a single valued id access
    int dotIndex = expr.getField().lastIndexOf('.');
    if (dotIndex == -1) {
        baseNodeKey = baseNode;
        if (singularAttr.getType() instanceof EmbeddableType<?>) {
            expressionToSplit = expr;
        }
    } else if (isEmbeddedIdPart) {
        baseNodeKey = baseNode;
    } else {
        // We have to correct the base node for single valued id paths
        String associationName = expr.getField().substring(0, dotIndex);
        ExtendedManagedType<?> extendedManagedType = metamodel.getManagedType(ExtendedManagedType.class, baseNode.getManagedType());
        ExtendedAttribute<?, ?> extendedAttribute = extendedManagedType.getAttribute(associationName);
        Attribute<?, ?> attribute = extendedAttribute.getAttribute();
        baseNodeKey = new AbstractMap.SimpleEntry<>(baseNode, associationName);
        if (attribute.getPersistentAttributeType() != Attribute.PersistentAttributeType.ONE_TO_ONE) {
            boolean nonConstantParent = true;
            Map<String, Boolean> constantifiedAttributes = constantifiedJoinNodeAttributeCollector.getConstantifiedJoinNodeAttributes().get(baseNodeKey);
            if (constantifiedAttributes != null) {
                Map<String, Boolean> orderedAttributes = new HashMap<>();
                addAttributes(baseNode.getEntityType(), null, "", "", (SingularAttribute<?, ?>) attribute, orderedAttributes);
                initConstantifiedAttributes(orderedAttributes, constantifiedAttributes.keySet());
                orderedAttributes.remove(expr.getField());
                String singleNonConstantifiedAttribute = getSingleNonConstantifiedAttribute(orderedAttributes);
                // If the identifiers are constantified, we don't care if this is a one-to-one
                if (singleNonConstantifiedAttribute != null && (singleNonConstantifiedAttribute.isEmpty() || equalsAny(singleNonConstantifiedAttribute, extendedManagedType.getAttribute(expr.getField()).getColumnEquivalentAttributes()))) {
                    nonConstantParent = false;
                    orderedAttributes.clear();
                    managedType = metamodel.getManagedType(ExtendedManagedType.class, JpaMetamodelUtils.resolveFieldClass(baseNode.getJavaType(), attribute));
                }
            } else if (attribute instanceof SingularAttribute<?, ?> && ((SingularAttribute<?, ?>) attribute).isId()) {
                baseNodeKey = baseNode;
                // This is an id class attribute
                nonConstantParent = false;
                // This is not 100% correct as it is not an embedded id, but works because we only need to append the association name field part
                isEmbeddedIdPart = true;
            }
            if (nonConstantParent) {
                registerFunctionalDependencyRootExpression(baseNodeKey);
                return false;
            }
        } else {
            managedType = metamodel.getManagedType(ExtendedManagedType.class, JpaMetamodelUtils.resolveFieldClass(baseNode.getJavaType(), attribute));
        }
    }
    registerFunctionalDependencyRootExpression(baseNodeKey);
    // First we initialize the names of the id attributes as set for the join node
    Map<String, Boolean> orderedAttributes = getUniquenessMissingAttributes(baseNodeKey, managedType);
    // We remove for every id attribute from the initialized set of id attribute names
    String prefix;
    if (dotIndex == -1 && baseNode.getParentTreeNode() != null && !baseNode.getParentTreeNode().isCollection()) {
        prefix = baseNode.getParentTreeNode().getRelationName() + ".";
        JoinNode node = baseNode.getParent();
        while (node.getParentTreeNode() != null) {
            prefix = node.getParentTreeNode().getRelationName() + "." + prefix;
            node = node.getParent();
        }
    } else {
        prefix = isEmbeddedIdPart ? field.substring(0, dotIndex + 1) : "";
    }
    if (removeAttribute(prefix, singularAttr, orderedAttributes) && currentResolvedExpression != null) {
        List<ResolvedExpression> resolvedExpressions = uniquenessFormingJoinNodeExpressions.get(baseNodeKey);
        if (resolvedExpressions == null) {
            resolvedExpressions = new ArrayList<>(orderedAttributes.size() + 1);
            uniquenessFormingJoinNodeExpressions.put(baseNodeKey, resolvedExpressions);
        }
        resolvedExpressions.add(currentResolvedExpression);
    }
    lastJoinNode = baseNodeKey;
    // While there still are some attribute names left, we simply report that it isn't unique, yet
    if (hasNonConstantifiedAttribute(orderedAttributes)) {
        return false;
    }
    // But even now that we order by all id attribute parts, we still have to make sure this join node is uniqueness preserving
    String subPath = field;
    while (baseNode.getParent() != null) {
        if (baseNode.getParentTreeNode() == null) {
            // Only assume uniqueness when the encountered cross or entity join are constantified
            return constantifiedJoinNodeAttributeCollector.isConstantified(baseNode);
        } else {
            subPath = baseNode.getParentTreeNode().getRelationName() + "." + subPath;
            attr = baseNode.getParentTreeNode().getAttribute();
            // Only one-to-one relation joins i.e. joins having a unique key with unique key equality predicate are uniqueness preserving
            baseNode = baseNode.getParent();
            if (attr.getPersistentAttributeType() != Attribute.PersistentAttributeType.ONE_TO_ONE) {
                Map<String, Boolean> constantifiedAttributes = constantifiedJoinNodeAttributeCollector.getConstantifiedJoinNodeAttributes().get(baseNode);
                if (constantifiedAttributes != null) {
                    // If there are constantified attributes for the node, we check if they cover the identifier
                    // This is relevant for queries like `select e.manyToOne.id, e.manyToOne.name from Entity e order by e.manyToOne.id`
                    // Normally, the expression `e.manyToOne.id` wouldn't be considered unique, unless there is a unique key with constant equality predicate
                    // i.e. a predicate like `e.id = 1` that essentially "constantifies" the parent join node
                    ExtendedManagedType<?> extendedManagedType = metamodel.getManagedType(ExtendedManagedType.class, baseNode.getManagedType());
                    orderedAttributes = new HashMap<>();
                    addAttributes(baseNode.getEntityType(), null, "", "", (SingularAttribute<?, ?>) attr, orderedAttributes);
                    initConstantifiedAttributes(orderedAttributes, constantifiedAttributes.keySet());
                    orderedAttributes.remove(subPath);
                    String singleNonConstantifiedAttribute = getSingleNonConstantifiedAttribute(orderedAttributes);
                    // If the identifiers are constantified, we don't care if this is a one-to-one
                    if (singleNonConstantifiedAttribute != null && (singleNonConstantifiedAttribute.isEmpty() || extendedManagedType.getAttributes().containsKey(subPath) && equalsAny(singleNonConstantifiedAttribute, extendedManagedType.getAttribute(subPath).getColumnEquivalentAttributes()))) {
                        continue;
                    }
                }
                return false;
            }
        }
    }
    return true;
}
Also used : PathReference(com.blazebit.persistence.parser.expression.PathReference) SingularAttribute(javax.persistence.metamodel.SingularAttribute) Attribute(javax.persistence.metamodel.Attribute) PluralAttribute(javax.persistence.metamodel.PluralAttribute) ExtendedAttribute(com.blazebit.persistence.spi.ExtendedAttribute) EmbeddableType(javax.persistence.metamodel.EmbeddableType) PluralAttribute(javax.persistence.metamodel.PluralAttribute) ExtendedAttribute(com.blazebit.persistence.spi.ExtendedAttribute) ExtendedManagedType(com.blazebit.persistence.spi.ExtendedManagedType) SingularAttribute(javax.persistence.metamodel.SingularAttribute) ListIndexExpression(com.blazebit.persistence.parser.expression.ListIndexExpression) WhenClauseExpression(com.blazebit.persistence.parser.expression.WhenClauseExpression) Expression(com.blazebit.persistence.parser.expression.Expression) GeneralCaseExpression(com.blazebit.persistence.parser.expression.GeneralCaseExpression) TrimExpression(com.blazebit.persistence.parser.expression.TrimExpression) ParameterExpression(com.blazebit.persistence.parser.expression.ParameterExpression) ArithmeticExpression(com.blazebit.persistence.parser.expression.ArithmeticExpression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) TypeFunctionExpression(com.blazebit.persistence.parser.expression.TypeFunctionExpression) MapEntryExpression(com.blazebit.persistence.parser.expression.MapEntryExpression) MapValueExpression(com.blazebit.persistence.parser.expression.MapValueExpression) ArrayExpression(com.blazebit.persistence.parser.expression.ArrayExpression) NullExpression(com.blazebit.persistence.parser.expression.NullExpression) FunctionExpression(com.blazebit.persistence.parser.expression.FunctionExpression) SimpleCaseExpression(com.blazebit.persistence.parser.expression.SimpleCaseExpression) SubqueryExpression(com.blazebit.persistence.parser.expression.SubqueryExpression) MapKeyExpression(com.blazebit.persistence.parser.expression.MapKeyExpression) HashMap(java.util.HashMap) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map) AbstractMap(java.util.AbstractMap)

Example 17 with ExtendedManagedType

use of com.blazebit.persistence.spi.ExtendedManagedType in project blaze-persistence by Blazebit.

the class JoinVisitor method getNaturalIdAttribute.

private String getNaturalIdAttribute(Expression expression) {
    // Hibernate fails to do this and instead compares the primary key with the natural key which might go by unnoticed
    if (expression instanceof PathExpression) {
        PathExpression pathExpression = (PathExpression) expression;
        visit(pathExpression, false);
        PathReference pathReference = (pathExpression).getPathReference();
        // We only attach the natural id to paths referring to entity types
        if (pathReference != null && pathReference.getField() != null && pathReference.getType() instanceof EntityType<?>) {
            JoinNode node = (JoinNode) pathReference.getBaseNode();
            // We need a parent tree node to determine the natural id attribute
            Collection<String> identifierOrUniqueKeyEmbeddedPropertyNames = metamodel.getJpaProvider().getJoinMappingPropertyNames(node.getEntityType(), null, pathReference.getField()).keySet();
            if (identifierOrUniqueKeyEmbeddedPropertyNames.size() == 1) {
                // This "fix" only works if we have a single id attribute
                String naturalIdAttribute = identifierOrUniqueKeyEmbeddedPropertyNames.iterator().next();
                ExtendedManagedType extendedManagedType = metamodel.getManagedType(ExtendedManagedType.class, (ManagedType<?>) pathReference.getType());
                if (!extendedManagedType.getIdAttribute().getName().equals(naturalIdAttribute)) {
                    // Now we finally know the natural id attribute name
                    return naturalIdAttribute;
                }
            }
        }
    }
    return null;
}
Also used : PathReference(com.blazebit.persistence.parser.expression.PathReference) PathExpression(com.blazebit.persistence.parser.expression.PathExpression) ExtendedManagedType(com.blazebit.persistence.spi.ExtendedManagedType)

Example 18 with ExtendedManagedType

use of com.blazebit.persistence.spi.ExtendedManagedType in project blaze-persistence by Blazebit.

the class JpaUtils method getCollectionAttributeEntries.

public static Map<String, ExtendedAttribute<?, ?>> getCollectionAttributeEntries(EntityMetamodel metamodel, EntityType<?> entityType, ExtendedAttribute<?, ?> attribute) {
    Map<String, ExtendedAttribute<?, ?>> collectionAttributeEntries = new HashMap<>();
    JoinTable joinTable = attribute.getJoinTable();
    if (joinTable == null) {
        throw new IllegalArgumentException("Inserting into or updating an inverse collection via DML API is not supported!");
    }
    ExtendedManagedType<?> extendedManagedType = metamodel.getManagedType(ExtendedManagedType.class, entityType);
    for (String idAttributeName : joinTable.getIdAttributeNames()) {
        collectionAttributeEntries.put(idAttributeName, extendedManagedType.getAttribute(idAttributeName));
    }
    if (((PluralAttribute<?, ?, ?>) attribute.getAttribute()).getElementType() instanceof ManagedType<?>) {
        String prefix = attribute.getAttributePathString() + ".";
        for (Map.Entry<String, ? extends ExtendedAttribute<?, ?>> entry : extendedManagedType.getAttributes().entrySet()) {
            if (entry.getKey().startsWith(prefix)) {
                collectionAttributeEntries.put(entry.getKey(), entry.getValue());
            }
        }
    }
    collectionAttributeEntries.put(attribute.getAttributePathString(), attribute);
    return collectionAttributeEntries;
}
Also used : ExtendedManagedType(com.blazebit.persistence.spi.ExtendedManagedType) ManagedType(javax.persistence.metamodel.ManagedType) HashMap(java.util.HashMap) ExtendedAttribute(com.blazebit.persistence.spi.ExtendedAttribute) HashMap(java.util.HashMap) Map(java.util.Map) JoinTable(com.blazebit.persistence.spi.JoinTable)

Example 19 with ExtendedManagedType

use of com.blazebit.persistence.spi.ExtendedManagedType in project blaze-persistence by Blazebit.

the class EmbeddableSplittingVisitor method collectSplittedOffExpressions.

protected boolean collectSplittedOffExpressions(Expression expression) {
    splittedOffExpressions.clear();
    if (expressionToSplit != null) {
        JoinNode baseNode;
        String field;
        if (expressionToSplit instanceof PathExpression) {
            PathReference pathReference = ((PathExpression) expressionToSplit).getPathReference();
            baseNode = (JoinNode) pathReference.getBaseNode();
            field = pathReference.getField();
        } else if (expressionToSplit instanceof MapKeyExpression) {
            baseNode = ((JoinNode) ((MapKeyExpression) expressionToSplit).getPath().getBaseNode()).getKeyJoinNode();
            field = null;
        } else {
            // This should never happen
            return false;
        }
        String fieldPrefix = field == null ? "" : field + ".";
        ExtendedManagedType<?> managedType = metamodel.getManagedType(ExtendedManagedType.class, baseNode.getManagedType());
        Map<String, Boolean> orderedAttributes = new TreeMap<>();
        EntityType<?> ownerType;
        if ((baseNode.getParentTreeNode() == null || splitEntity && baseNode.getManagedType() instanceof EntityType<?>) && field == null) {
            ownerType = baseNode.getEntityType();
            for (SingularAttribute<?, ?> idAttribute : managedType.getIdAttributes()) {
                addAttributes(ownerType, null, fieldPrefix, "", idAttribute, orderedAttributes);
            }
        } else {
            Map<String, ? extends ExtendedAttribute<?, ?>> ownedAttributes;
            String prefix = field;
            if (baseNode.getParentTreeNode() != null && jpaProvider.getJpaMetamodelAccessor().isElementCollection(baseNode.getParentTreeNode().getAttribute())) {
                String elementCollectionPath = baseNode.getParentTreeNode().getRelationName();
                ExtendedManagedType entityManagedType = metamodel.getManagedType(ExtendedManagedType.class, baseNode.getParent().getEntityType());
                ownedAttributes = entityManagedType.getAttributes();
                if (prefix == null) {
                    prefix = elementCollectionPath;
                } else {
                    prefix = elementCollectionPath + "." + prefix;
                }
            } else {
                ownedAttributes = managedType.getOwnedSingularAttributes();
            }
            for (String embeddedPropertyPath : JpaUtils.getEmbeddedPropertyPaths((Map<String, ExtendedAttribute<?, ?>>) ownedAttributes, prefix, false, false)) {
                orderedAttributes.put(embeddedPropertyPath, Boolean.FALSE);
            }
        }
        // Signal the caller that the expression was eliminated
        if (orderedAttributes.isEmpty()) {
            return true;
        }
        for (String orderedAttribute : orderedAttributes.keySet()) {
            splittedOffExpressions.add(splittingVisitor.splitOff(expression, expressionToSplit, orderedAttribute));
        }
    }
    return false;
}
Also used : PathReference(com.blazebit.persistence.parser.expression.PathReference) ExtendedAttribute(com.blazebit.persistence.spi.ExtendedAttribute) TreeMap(java.util.TreeMap) ExtendedManagedType(com.blazebit.persistence.spi.ExtendedManagedType) EntityType(javax.persistence.metamodel.EntityType) MapKeyExpression(com.blazebit.persistence.parser.expression.MapKeyExpression) PathExpression(com.blazebit.persistence.parser.expression.PathExpression)

Example 20 with ExtendedManagedType

use of com.blazebit.persistence.spi.ExtendedManagedType in project blaze-persistence by Blazebit.

the class ExpressionUtils method isNullable.

private static boolean isNullable(EntityMetamodel metamodel, ConstantifiedJoinNodeAttributeCollector constantifiedJoinNodeAttributeCollector, Map<String, Type<?>> rootTypes, PathExpression expr) {
    JoinNode baseNode = ((JoinNode) expr.getBaseNode());
    if (baseNode == null) {
        List<PathElementExpression> expressions = expr.getExpressions();
        PathElementExpression expression = expressions.get(0);
        if (!(expression instanceof PropertyExpression)) {
            // List or Map access, as well as Treat and Array access are always nullable
            return true;
        }
        int size = expressions.size();
        Type<?> baseType = rootTypes.get(((PropertyExpression) expression).getProperty());
        int i = 0;
        if (baseType != null) {
            if (size == 1) {
                // We have to assume that any base alias reference is nullable
                return true;
            }
            i = 1;
        } else {
            baseType = rootTypes.get("this");
        }
        for (; i < size; i++) {
            expression = expressions.get(i);
            if (!(expression instanceof PropertyExpression)) {
                // List or Map access, as well as Treat and Array access are always nullable
                return true;
            }
            ManagedType<?> managedType = (ManagedType<?>) baseType;
            Attribute<?, ?> attribute = managedType.getAttribute(((PropertyExpression) expression).getProperty());
            if (JpaMetamodelUtils.isNullable(attribute)) {
                return true;
            }
            baseType = ((SingularAttribute<?, ?>) attribute).getType();
        }
        return false;
    }
    // First we check if the target attribute is optional/nullable, because then we don't need to check the join structure
    if (expr.getField() != null) {
        // If the attribute is constantified i.e. appears in a top-level EQ predicate, we can be sure it is non-nullable as well
        if (constantifiedJoinNodeAttributeCollector != null && constantifiedJoinNodeAttributeCollector.isConstantifiedNonOptional(baseNode, expr.getField())) {
            return false;
        }
        ManagedType<?> managedType = baseNode.getManagedType();
        ExtendedManagedType extendedManagedType = metamodel.getManagedType(ExtendedManagedType.class, JpaMetamodelUtils.getTypeName(managedType));
        Attribute<?, ?> attr = extendedManagedType.getAttribute(expr.getField()).getAttribute();
        if (JpaMetamodelUtils.isNullable(attr)) {
            return true;
        }
        // Check if we have a single valued id access
        int dotIndex = expr.getField().lastIndexOf('.');
        if (dotIndex != -1) {
            // A single valued id path is nullable if the parent association is nullable
            String associationName = expr.getField().substring(0, dotIndex);
            Attribute<?, ?> associationAttribute = extendedManagedType.getAttribute(associationName).getAttribute();
            if (JpaMetamodelUtils.isNullable(associationAttribute)) {
                // Finally check if the association might have been inner joined
                JoinTreeNode associationNode = baseNode.getNodes().get(associationName);
                if (associationNode == null || associationNode.getDefaultNode().getJoinType() != JoinType.INNER) {
                    return true;
                }
            }
        }
    }
    // Note that a VALUES clause does not adhere to the nullability guarantees
    return baseNode.getValueCount() > 0 && baseNode.getValuesCastedParameter() == null || baseNode.getJoinType() == JoinType.LEFT || baseNode.getJoinType() == JoinType.FULL;
}
Also used : PathElementExpression(com.blazebit.persistence.parser.expression.PathElementExpression) ManagedType(javax.persistence.metamodel.ManagedType) ExtendedManagedType(com.blazebit.persistence.spi.ExtendedManagedType) PropertyExpression(com.blazebit.persistence.parser.expression.PropertyExpression) ExtendedManagedType(com.blazebit.persistence.spi.ExtendedManagedType)

Aggregations

ExtendedManagedType (com.blazebit.persistence.spi.ExtendedManagedType)20 ExtendedAttribute (com.blazebit.persistence.spi.ExtendedAttribute)13 EntityType (javax.persistence.metamodel.EntityType)8 PathExpression (com.blazebit.persistence.parser.expression.PathExpression)7 ManagedType (javax.persistence.metamodel.ManagedType)7 HashMap (java.util.HashMap)6 Map (java.util.Map)6 EntityMetamodel (com.blazebit.persistence.parser.EntityMetamodel)5 ArrayList (java.util.ArrayList)4 Attribute (javax.persistence.metamodel.Attribute)4 EmbeddableType (javax.persistence.metamodel.EmbeddableType)4 SingularAttribute (javax.persistence.metamodel.SingularAttribute)4 PathReference (com.blazebit.persistence.parser.expression.PathReference)3 LinkedHashMap (java.util.LinkedHashMap)3 MapKeyExpression (com.blazebit.persistence.parser.expression.MapKeyExpression)2 PathElementExpression (com.blazebit.persistence.parser.expression.PathElementExpression)2 PropertyExpression (com.blazebit.persistence.parser.expression.PropertyExpression)2 JpaProvider (com.blazebit.persistence.spi.JpaProvider)2 InverseRemoveStrategy (com.blazebit.persistence.view.InverseRemoveStrategy)2 AttributeAccessor (com.blazebit.persistence.view.impl.accessor.AttributeAccessor)2