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;
}
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;
}
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;
}
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;
}
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;
}
Aggregations