Skip to content

Commit

Permalink
[Blazebit#681] Allow multiple non-cascading parents for updatable ent…
Browse files Browse the repository at this point in the history
…ity views
  • Loading branch information
beikov committed Nov 19, 2018
1 parent 792febe commit 74adc64
Show file tree
Hide file tree
Showing 72 changed files with 1,548 additions and 519 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 @@ -841,7 +841,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
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@
public final class DirtyStateViewAttributeAccessor extends ViewAttributeAccessor implements InitialValueAttributeAccessor {

private final int dirtyStateIndex;
private final boolean updatableOnly;
private final BasicUserType<Object> userType;

@SuppressWarnings({ "rawtypes", "unchecked" })
public DirtyStateViewAttributeAccessor(EntityViewManagerImpl evm, MethodAttribute<?, ?> attribute) {
super(evm, attribute, false);
this.dirtyStateIndex = ((AbstractMethodAttribute<?, ?>) attribute).getDirtyStateIndex();
this.updatableOnly = ((AbstractMethodAttribute<?, ?>) attribute).isUpdatableOnly() && !attribute.isCorrelated();
if (attribute instanceof SingularAttribute<?, ?>) {
SingularAttribute<?, ?> singularAttribute = (SingularAttribute<?, ?>) attribute;
if (singularAttribute.getType() instanceof BasicType<?>) {
Expand Down Expand Up @@ -82,7 +84,9 @@ public void setValue(Object view, Object value) {
super.setValue(view, value);
if (view instanceof MutableStateTrackable) {
MutableStateTrackable mutableStateTrackable = (MutableStateTrackable) view;
if (value instanceof DirtyTracker) {
if (updatableOnly && value instanceof MutableStateTrackable) {
((MutableStateTrackable) value).$$_addReadOnlyParent(mutableStateTrackable, dirtyStateIndex);
} else if (value instanceof DirtyTracker) {
((DirtyTracker) value).$$_setParent(mutableStateTrackable, dirtyStateIndex);
}
mutableStateTrackable.$$_getMutableState()[dirtyStateIndex] = value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,17 @@
public class ListCollectionInstantiator extends AbstractCollectionInstantiator {

private final Set<Class<?>> allowedSubtypes;
private final Set<Class<?>> parentRequiringSubtypes;
private final boolean updatable;
private final boolean indexed;
private final boolean optimize;
private final boolean forceUnique;
private final Comparator<Object> comparator;

public ListCollectionInstantiator(PluralObjectFactory<Collection<?>> collectionFactory, Set<Class<?>> allowedSubtypes, boolean updatable, boolean indexed, boolean optimize, boolean forceUnique, Comparator<?> comparator) {
public ListCollectionInstantiator(PluralObjectFactory<Collection<?>> collectionFactory, Set<Class<?>> allowedSubtypes, Set<Class<?>> parentRequiringSubtypes, boolean updatable, boolean indexed, boolean optimize, boolean forceUnique, Comparator<?> comparator) {
super(collectionFactory);
this.allowedSubtypes = allowedSubtypes;
this.parentRequiringSubtypes = parentRequiringSubtypes;
this.updatable = updatable;
this.indexed = indexed;
this.optimize = optimize;
Expand Down Expand Up @@ -90,6 +92,6 @@ public List<?> createCollection(int size) {

@Override
public RecordingList<?> createRecordingCollection(int size) {
return new RecordingList(createCollection(size), indexed, allowedSubtypes, updatable, optimize);
return new RecordingList(createCollection(size), indexed, allowedSubtypes, parentRequiringSubtypes, updatable, optimize);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,16 @@
public class OrderedCollectionInstantiator extends AbstractCollectionInstantiator {

private final Set<Class<?>> allowedSubtypes;
private final Set<Class<?>> parentRequiringSubtypes;
private final boolean updatable;
private final boolean optimize;
private final boolean forceUnique;
private final Comparator<Object> comparator;

public OrderedCollectionInstantiator(PluralObjectFactory<Collection<?>> collectionFactory, Set<Class<?>> allowedSubtypes, boolean updatable, boolean optimize, boolean forceUnique, Comparator<?> comparator) {
public OrderedCollectionInstantiator(PluralObjectFactory<Collection<?>> collectionFactory, Set<Class<?>> allowedSubtypes, Set<Class<?>> parentRequiringSubtypes, boolean updatable, boolean optimize, boolean forceUnique, Comparator<?> comparator) {
super(collectionFactory);
this.allowedSubtypes = allowedSubtypes;
this.parentRequiringSubtypes = parentRequiringSubtypes;
this.updatable = updatable;
this.optimize = optimize;
this.forceUnique = forceUnique;
Expand Down Expand Up @@ -87,6 +89,6 @@ public Collection<?> createCollection(int size) {

@Override
public RecordingCollection<Collection<?>, ?> createRecordingCollection(int size) {
return new RecordingCollection(createCollection(size), false, true, allowedSubtypes, updatable, optimize);
return new RecordingCollection(createCollection(size), false, true, allowedSubtypes, parentRequiringSubtypes, updatable, optimize);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@
public class OrderedMapInstantiator extends AbstractMapInstantiator<Map<?, ?>, RecordingMap<Map<?, ?>, ?, ?>> {

private final Set<Class<?>> allowedSubtypes;
private final Set<Class<?>> parentRequiringSubtypes;
private final boolean updatable;
private final boolean optimize;

public OrderedMapInstantiator(PluralObjectFactory<Map<?, ?>> collectionFactory, Set<Class<?>> allowedSubtypes, boolean updatable, boolean optimize) {
public OrderedMapInstantiator(PluralObjectFactory<Map<?, ?>> collectionFactory, Set<Class<?>> allowedSubtypes, Set<Class<?>> parentRequiringSubtypes, boolean updatable, boolean optimize) {
super(collectionFactory);
this.allowedSubtypes = allowedSubtypes;
this.parentRequiringSubtypes = parentRequiringSubtypes;
this.updatable = updatable;
this.optimize = optimize;
}
Expand All @@ -45,6 +47,6 @@ public OrderedMapInstantiator(PluralObjectFactory<Map<?, ?>> collectionFactory,

@Override
public RecordingMap<Map<?, ?>, ?, ?> createRecordingCollection(int size) {
return new RecordingMap(createCollection(size), true, allowedSubtypes, updatable, optimize);
return new RecordingMap(createCollection(size), true, allowedSubtypes, parentRequiringSubtypes, updatable, optimize);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@
public class OrderedSetCollectionInstantiator extends AbstractCollectionInstantiator {

private final Set<Class<?>> allowedSubtypes;
private final Set<Class<?>> parentRequiringSubtypes;
private final boolean updatable;
private final boolean optimize;

public OrderedSetCollectionInstantiator(PluralObjectFactory<Collection<?>> collectionFactory, Set<Class<?>> allowedSubtypes, boolean updatable, boolean optimize) {
public OrderedSetCollectionInstantiator(PluralObjectFactory<Collection<?>> collectionFactory, Set<Class<?>> allowedSubtypes, Set<Class<?>> parentRequiringSubtypes, boolean updatable, boolean optimize) {
super(collectionFactory);
this.allowedSubtypes = allowedSubtypes;
this.parentRequiringSubtypes = parentRequiringSubtypes;
this.updatable = updatable;
this.optimize = optimize;
}
Expand All @@ -50,6 +52,6 @@ public Set<?> createCollection(int size) {

@Override
public RecordingSet<Set<?>, ?> createRecordingCollection(int size) {
return new RecordingSet(createCollection(size), true, allowedSubtypes, updatable, optimize);
return new RecordingSet(createCollection(size), true, allowedSubtypes, parentRequiringSubtypes, updatable, optimize);
}
}
Loading

0 comments on commit 74adc64

Please sign in to comment.