Skip to content

Commit

Permalink
[#681] Allow multiple non-cascading parents for updatable entity views
Browse files Browse the repository at this point in the history
  • Loading branch information
beikov committed Nov 22, 2018
1 parent 53b1b07 commit 4f1f2da
Show file tree
Hide file tree
Showing 79 changed files with 1,777 additions and 559 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Not yet released
* Implemented creatability validation for creatable entity views
* Implemented `SET_NULL` inverse remove strategy validation for updatable entity views
* Add updatable entity view support for inverse collections without a mapped by i.e. explicit join columns
* Rewrtite implicit joins in `ON` clause automatically to subqueries
* Add support for multiple non-cascading parent objects for updatable entity views

### Bug fixes

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1111,7 +1111,8 @@ private JoinNode renderJoinNode(StringBuilder sb, JoinAliasInfo joinBase, JoinNo

sb.append(node.getAliasInfo().getAlias());
renderedJoins.add(node);
boolean onClause = valuesNode != null || node.getOnPredicate() != null && !node.getOnPredicate().getChildren().isEmpty() || onCondition != null;
boolean realOnClause = node.getOnPredicate() != null && !node.getOnPredicate().getChildren().isEmpty() || onCondition != null;
boolean onClause = valuesNode != null || realOnClause;

if (onClause) {
sb.append(joinRestrictionKeyword);
Expand All @@ -1125,7 +1126,9 @@ private JoinNode renderJoinNode(StringBuilder sb, JoinAliasInfo joinBase, JoinNo
if (valuesNode != null) {
if (!externalRepresentation) {
renderValuesClausePredicate(sb, valuesNode, valuesNode.getAlias(), externalRepresentation);
sb.append(" AND ");
if (realOnClause) {
sb.append(" AND ");
}
}
valuesNode = null;
}
Expand Down Expand Up @@ -2185,26 +2188,34 @@ private boolean isSingleValuedAssociationId(JoinResult joinResult, List<PathElem
return false;
}

String elementCollectionPath = null;
String attributePath = joinResult.joinFields(maybeSingularAssociationName);
String fullAttributePath = attributePath;
if (maybeSingularAssociation instanceof MapKeyAttribute<?, ?>) {
// Skip the foreign join column check for map keys
// They aren't allowed as join sources in the JPA providers yet so we can only render them directly
} else if (baseType instanceof EmbeddableType<?>) {
// Get the base type. This is important if the path is "deeper" i.e. when having embeddables
JoinNode node = parent;
baseType = node.getNodeType();
while (baseType instanceof EmbeddableType<?>) {
if (baseType instanceof EmbeddableType<?>) {
if (node.getParentTreeNode() == null) {
attributePath = node.getValuesLikeAttribute() + "." + attributePath;
fullAttributePath = node.getValuesLikeAttribute() + "." + fullAttributePath;
elementCollectionPath = node.isValueClazzAttributeSingular() ? null : node.getValuesLikeAttribute();
baseType = node.getValueType();
break;
} else {
if (node.getParentTreeNode().getAttribute().isCollection()) {
elementCollectionPath = node.getParentTreeNode().getRelationName();
} else {
attributePath = node.getParentTreeNode().getRelationName() + "." + attributePath;
}
fullAttributePath = node.getParentTreeNode().getRelationName() + "." + fullAttributePath;
node = node.getParent();
baseType = node.getNodeType();
}
attributePath = node.getParentTreeNode().getRelationName() + "." + attributePath;
node = node.getParent();
baseType = node.getNodeType();
}

if (mainQuery.jpaProvider.isForeignJoinColumn((EntityType<?>) baseType, attributePath)) {
if (mainQuery.jpaProvider.isForeignJoinColumn((EntityType<?>) baseType, fullAttributePath)) {
return false;
}
} else if (mainQuery.jpaProvider.isForeignJoinColumn((EntityType<?>) baseType, maybeSingularAssociation.getName())) {
Expand All @@ -2213,10 +2224,18 @@ private boolean isSingleValuedAssociationId(JoinResult joinResult, List<PathElem

PathElementExpression maybeSingularAssociationIdExpression = pathElements.get(maybeSingularAssociationIdIndex);
ExtendedManagedType<?> managedType = metamodel.getManagedType(ExtendedManagedType.class, JpaMetamodelUtils.getTypeName(baseType));
ExtendedAttribute<?, ?> associationAttribute = managedType.getOwnedSingularAttributes().get(attributePath);
return managedType.getOwnedSingularAttributes().containsKey(attributePath + "." + maybeSingularAssociationIdExpression)
&& associationAttribute != null
&& (contains(metamodel.getManagedType(ExtendedManagedType.class, associationAttribute.getElementClass()).getIdAttributes(), maybeSingularAssociationIdExpression) || mainQuery.jpaProvider.supportsSingleValuedAssociationNaturalIdExpressions());
if (elementCollectionPath == null) {
ExtendedAttribute<?, ?> associationAttribute = managedType.getOwnedSingularAttributes().get(fullAttributePath);
return managedType.getOwnedSingularAttributes().containsKey(fullAttributePath + "." + maybeSingularAssociationIdExpression)
&& associationAttribute != null
&& (contains(metamodel.getManagedType(ExtendedManagedType.class, associationAttribute.getElementClass()).getIdAttributes(), maybeSingularAssociationIdExpression) || mainQuery.jpaProvider.supportsSingleValuedAssociationNaturalIdExpressions());
} else {
// We assume that associations within element collections are always owned
ExtendedAttribute<?, ?> associationAttribute = managedType.getAttributes().get(fullAttributePath);
return !mainQuery.jpaProvider.needsElementCollectionIdCutoff() && managedType.getAttributes().containsKey(fullAttributePath + "." + maybeSingularAssociationIdExpression)
&& associationAttribute != null
&& (contains(metamodel.getManagedType(ExtendedManagedType.class, associationAttribute.getElementClass()).getIdAttributes(), maybeSingularAssociationIdExpression) || mainQuery.jpaProvider.supportsSingleValuedAssociationNaturalIdExpressions());
}
}

private static boolean contains(Set<? extends Attribute<?, ?>> attributes, PathElementExpression expression) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.Type;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -841,7 +840,6 @@ public void appendDeReference(StringBuilder sb, String property, boolean renderT
appendAlias(sb, renderTreat, externalRepresentation);
// If we have a valuesTypeName, the property can only be "value" which is already handled in appendAlias
if (property != null && valuesTypeName == null) {
Set<SingularAttribute<?, ?>> idAttributes;
if (requiresElementCollectionIdCutoff && parentTreeNode != null && parentTreeNode.getAttribute().getPersistentAttributeType() == Attribute.PersistentAttributeType.ELEMENT_COLLECTION
&& property.endsWith(".id")) {
// See https://hibernate.atlassian.net/browse/HHH-13045 for details
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ protected StringBuilder applySqlTransformations(String sqlQuery) {
int subselectIndex = sb.indexOf(subselectTableExpr, 0);
if (subselectIndex == -1) {
// this is probably a VALUES clause for an entity type
int syntheticPredicateStart = sb.indexOf(syntheticPredicate, SqlUtils.indexOfWhere(sb));
int syntheticPredicateStart = sb.indexOf(syntheticPredicate, SqlUtils.indexOfFrom(sb));
int end = syntheticPredicateStart + syntheticPredicate.length();
if (sb.indexOf(andSeparator, end) == end) {
sb.replace(syntheticPredicateStart, end + andSeparator.length(), "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ public void setWrappedByteArray(Byte[] wrappedByteArray) {
this.wrappedByteArray = wrappedByteArray;
}

@OneToMany(mappedBy = "partnerDocument")
@OneToMany(mappedBy = "partnerDocument", cascade = CascadeType.PERSIST)
public Set<Person> getPartners() {
return partners;
}
Expand Down Expand Up @@ -260,8 +260,8 @@ public Map<Integer, Person> getContacts() {
return contacts;
}

public void setContacts(Map<Integer, Person> localized) {
this.contacts = localized;
public void setContacts(Map<Integer, Person> contacts) {
this.contacts = contacts;
}

@OneToMany
Expand All @@ -271,8 +271,8 @@ public Map<Integer, Person> getContacts2() {
return contacts2;
}

public void setContacts2(Map<Integer, Person> localized) {
this.contacts2 = localized;
public void setContacts2(Map<Integer, Person> contacts2) {
this.contacts2 = contacts2;
}

@OneToMany
Expand Down
Loading

0 comments on commit 4f1f2da

Please sign in to comment.