Skip to content

Commit

Permalink
Add support for correlated inheritance mappings and correlations in i…
Browse files Browse the repository at this point in the history
…nheritance subtypes. Fix group by handling for non-Hibernate
  • Loading branch information
beikov committed Nov 22, 2018
1 parent 4f1f2da commit 252b0f5
Show file tree
Hide file tree
Showing 150 changed files with 2,481 additions and 488 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Not yet released
* 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
* Add support for correlated inheritance mappings and correlations in inheritance subtypes

### Bug fixes

Expand Down
45 changes: 45 additions & 0 deletions core/api/src/main/java/com/blazebit/persistence/FromBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.blazebit.persistence;

import javax.persistence.metamodel.EntityType;
import java.util.Collection;
import java.util.Set;

/**
Expand Down Expand Up @@ -63,6 +64,50 @@ public interface FromBuilder<X extends FromBuilder<X>> extends FromBaseBuilder<X
*/
public Path getPath(String path);

/* Declarations to retain binary compatibility */

@Override
public X from(Class<?> entityClass);

@Override
public X from(Class<?> entityClass, String alias);

@Override
public X from(EntityType<?> entityType);

@Override
public X from(EntityType<?> entityType, String alias);

@Override
public X fromOld(Class<?> entityClass);

@Override
public X fromOld(Class<?> entityClass, String alias);

@Override
public X fromNew(Class<?> entityClass);

@Override
public X fromNew(Class<?> entityClass, String alias);

@Override
public X fromValues(Class<?> valueClass, String alias, int valueCount);

@Override
public X fromValues(Class<?> entityBaseClass, String attributeName, String alias, int valueCount);

@Override
public X fromIdentifiableValues(Class<?> valueClass, String alias, int valueCount);

@Override
public <T> X fromValues(Class<T> valueClass, String alias, Collection<T> values);

@Override
public X fromValues(Class<?> entityBaseClass, String attributeName, String alias, Collection<?> values);

@Override
public <T> X fromIdentifiableValues(Class<T> valueClass, String alias, Collection<T> values);

/*
* Join methods
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,14 @@ public interface JpaProvider {
*/
public boolean supportsSingleValuedAssociationNaturalIdExpressions();

/**
* Indicates whether the provider supports group by entity alias properly.
*
* @return true if supported, else false
* @since 1.3.0
*/
public boolean supportsGroupByEntityAlias();

/**
* Indicates whether the provider needs to cutoff id properties when used as subpath of element collections.
* This is to be able to workaround https://hibernate.atlassian.net/browse/HHH-13045 .
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.blazebit.persistence.impl;

import com.blazebit.persistence.BaseSubqueryBuilder;
import com.blazebit.persistence.CTEBuilder;
import com.blazebit.persistence.CaseWhenStarterBuilder;
import com.blazebit.persistence.CriteriaBuilderFactory;
Expand All @@ -39,23 +40,13 @@
import com.blazebit.persistence.SubqueryBuilder;
import com.blazebit.persistence.SubqueryInitiator;
import com.blazebit.persistence.WhereOrBuilder;
import com.blazebit.persistence.impl.util.SqlUtils;
import com.blazebit.persistence.parser.EntityMetamodel;
import com.blazebit.persistence.parser.expression.Expression;
import com.blazebit.persistence.parser.expression.ExpressionFactory;
import com.blazebit.persistence.parser.expression.PathExpression;
import com.blazebit.persistence.parser.expression.Subquery;
import com.blazebit.persistence.parser.expression.SubqueryExpressionFactory;
import com.blazebit.persistence.parser.expression.VisitorAdapter;
import com.blazebit.persistence.impl.function.entity.ValuesEntity;
import com.blazebit.persistence.impl.keyset.KeysetBuilderImpl;
import com.blazebit.persistence.impl.keyset.KeysetImpl;
import com.blazebit.persistence.impl.keyset.KeysetLink;
import com.blazebit.persistence.impl.keyset.KeysetManager;
import com.blazebit.persistence.impl.keyset.KeysetMode;
import com.blazebit.persistence.impl.keyset.SimpleKeysetLink;
import com.blazebit.persistence.parser.expression.modifier.ExpressionModifier;
import com.blazebit.persistence.parser.predicate.Predicate;
import com.blazebit.persistence.impl.query.AbstractCustomQuery;
import com.blazebit.persistence.impl.query.CTENode;
import com.blazebit.persistence.impl.query.CustomQuerySpecification;
Expand All @@ -71,6 +62,16 @@
import com.blazebit.persistence.impl.transform.SizeTransformationVisitor;
import com.blazebit.persistence.impl.transform.SizeTransformerGroup;
import com.blazebit.persistence.impl.transform.SubqueryRecursiveExpressionVisitor;
import com.blazebit.persistence.impl.util.SqlUtils;
import com.blazebit.persistence.parser.EntityMetamodel;
import com.blazebit.persistence.parser.expression.Expression;
import com.blazebit.persistence.parser.expression.ExpressionFactory;
import com.blazebit.persistence.parser.expression.PathExpression;
import com.blazebit.persistence.parser.expression.Subquery;
import com.blazebit.persistence.parser.expression.SubqueryExpressionFactory;
import com.blazebit.persistence.parser.expression.VisitorAdapter;
import com.blazebit.persistence.parser.expression.modifier.ExpressionModifier;
import com.blazebit.persistence.parser.predicate.Predicate;
import com.blazebit.persistence.parser.util.JpaMetamodelUtils;
import com.blazebit.persistence.spi.ConfigurationSource;
import com.blazebit.persistence.spi.DbmsDialect;
Expand Down Expand Up @@ -552,6 +553,10 @@ public SubquerySetReturn startSet() {
}

private SetReturn addSetOperation(SetOperationType type) {
// We support set operations in subqueries since we use custom functions
if (!(this instanceof BaseSubqueryBuilder<?>)) {
mainQuery.assertSupportsAdvancedSql("Illegal use of SET operation!");
}
prepareForModification(ClauseType.SELECT);
this.setOperationEnded = true;
// We only check non-empty queries since empty ones will be replaced
Expand Down Expand Up @@ -669,7 +674,7 @@ public BuilderType from(EntityType<?> entityType, String alias) {
}

public BuilderType fromCte(Class<?> clazz, String cteName) {
return fromCte(clazz, null);
return fromCte(clazz, cteName, null);
}

public BuilderType fromCte(Class<?> clazz, String cteName, String alias) {
Expand Down Expand Up @@ -856,6 +861,7 @@ private BuilderType from(EntityType<?> type, String alias, DbmsModificationState

// Handle old and new references
if (state != null) {
mainQuery.assertSupportsAdvancedSql("Illegal use of modification state clause OLD/NEW!");
Class<?> clazz = type.getJavaType();
Map<String, DbmsModificationState> versionEntities = explicitVersionEntities.get(clazz);
if (versionEntities == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ private void checkEntityIdComponent(Object component, Expression expression, Str
public X fetch(String path) {
prepareForModification(ClauseType.JOIN);
verifyBuilderEnded();
joinManager.implicitJoin(expressionFactory.createPathExpression(path), true, true, null, null, new HashSet<String>(), false, false, true, false, true);
joinManager.implicitJoin(expressionFactory.createPathExpression(path), true, true, null, null, new HashSet<String>(), false, false, true, false, true, false);
return (X) this;
}

Expand All @@ -630,7 +630,7 @@ public X fetch(String... paths) {

HashSet<String> currentlyResolvingAliases = new HashSet<>();
for (String path : paths) {
joinManager.implicitJoin(expressionFactory.createPathExpression(path), true, true, null, null, currentlyResolvingAliases, false, false, true, false, true);
joinManager.implicitJoin(expressionFactory.createPathExpression(path), true, true, null, null, currentlyResolvingAliases, false, false, true, false, true, false);
}

return (X) this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ void buildClause(StringBuilder sb) {

@SuppressWarnings("unchecked")
<Y> StartOngoingSetOperationCTECriteriaBuilder<Y, LeafOngoingFinalSetOperationCTECriteriaBuilder<Y>> withStartSet(Class<?> cteClass, Y result) {
mainQuery.assertSupportsAdvancedSql("Illegal use of WITH clause!");
String cteName = cteClass.getSimpleName();
FinalSetOperationCTECriteriaBuilderImpl<Y> parentFinalSetOperationBuilder = new FinalSetOperationCTECriteriaBuilderImpl<Y>(mainQuery, queryContext, (Class<Y>) cteClass, result, null, false, this, null);
OngoingFinalSetOperationCTECriteriaBuilderImpl<Y> subFinalSetOperationBuilder = new OngoingFinalSetOperationCTECriteriaBuilderImpl<Y>(mainQuery, queryContext, (Class<Y>) cteClass, null, null, true, parentFinalSetOperationBuilder.getSubListener(), null);
Expand All @@ -156,6 +157,7 @@ <Y> StartOngoingSetOperationCTECriteriaBuilder<Y, LeafOngoingFinalSetOperationCT

@SuppressWarnings("unchecked")
<Y> FullSelectCTECriteriaBuilder<Y> with(Class<?> cteClass, Y result) {
mainQuery.assertSupportsAdvancedSql("Illegal use of WITH clause!");
String cteName = cteClass.getSimpleName();
FullSelectCTECriteriaBuilderImpl<Y> cteBuilder = new FullSelectCTECriteriaBuilderImpl<Y>(mainQuery, queryContext, cteName, (Class<Object>) cteClass, result, this);
this.onBuilderStarted(cteBuilder);
Expand All @@ -164,6 +166,7 @@ <Y> FullSelectCTECriteriaBuilder<Y> with(Class<?> cteClass, Y result) {

@SuppressWarnings("unchecked")
<Y> SelectRecursiveCTECriteriaBuilder<Y> withRecursive(Class<?> cteClass, Y result) {
mainQuery.assertSupportsAdvancedSql("Illegal use of WITH clause!");
String cteName = cteClass.getSimpleName();
recursive = true;
RecursiveCTECriteriaBuilderImpl<Y> cteBuilder = new RecursiveCTECriteriaBuilderImpl<Y>(mainQuery, queryContext, cteName, (Class<Object>) cteClass, result, this);
Expand All @@ -172,6 +175,7 @@ <Y> SelectRecursiveCTECriteriaBuilder<Y> withRecursive(Class<?> cteClass, Y resu
}

<Y> ReturningModificationCriteriaBuilderFactory<Y> withReturning(Class<?> cteClass, Y result) {
mainQuery.assertSupportsAdvancedSql("Illegal use of WITH clause!");
String cteName = cteClass.getSimpleName();
ReturningModificationCriteraBuilderFactoryImpl<Y> factory = new ReturningModificationCriteraBuilderFactoryImpl<Y>(mainQuery, cteName, cteClass, result, this);
return factory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,11 @@ public boolean supportsSingleValuedAssociationNaturalIdExpressions() {
return jpaProvider.supportsSingleValuedAssociationNaturalIdExpressions();
}

@Override
public boolean supportsGroupByEntityAlias() {
return jpaProvider.supportsGroupByEntityAlias();
}

@Override
public boolean needsElementCollectionIdCutoff() {
return jpaProvider.needsElementCollectionIdCutoff();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

Expand Down Expand Up @@ -108,7 +109,7 @@ public EntityMetamodelImpl(EntityManagerFactory emf, JpaProviderFactory jpaProvi
}

Set<Attribute<?, ?>> attributes = (Set<Attribute<?, ?>>) (Set) e.getAttributes();
Map<String, AttributeEntry<?, ?>> attributeMap = new HashMap<>(attributes.size());
Map<String, AttributeEntry<?, ?>> attributeMap = new TreeMap<>();
TemporaryExtendedManagedType extendedManagedType = new TemporaryExtendedManagedType(e, attributeMap);
temporaryExtendedManagedTypes.put(JpaMetamodelUtils.getTypeName(e), extendedManagedType);
if (e.getJavaType() != null) {
Expand Down Expand Up @@ -257,7 +258,7 @@ private TemporaryExtendedManagedType collectColumnNames(EntityType<?> e, Map<Str
Set<Attribute<?, ?>> attributes = (Set<Attribute<?, ?>>) (Set) type.getAttributes();
TemporaryExtendedManagedType extendedManagedType = temporaryExtendedManagedTypes.get(JpaMetamodelUtils.getTypeName(type));
if (extendedManagedType == null) {
extendedManagedType = new TemporaryExtendedManagedType(type, new HashMap<String, AttributeEntry<?, ?>>());
extendedManagedType = new TemporaryExtendedManagedType(type, new TreeMap<String, AttributeEntry<?, ?>>());
temporaryExtendedManagedTypes.put(JpaMetamodelUtils.getTypeName(type), extendedManagedType);
if (type.getJavaType() != null) {
temporaryExtendedManagedTypes.put(type.getJavaType().getName(), extendedManagedType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,6 @@

package com.blazebit.persistence.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.persistence.FetchType;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;

import com.blazebit.persistence.parser.EntityMetamodel;
import com.blazebit.persistence.parser.expression.FunctionExpression;
import com.blazebit.persistence.parser.expression.ListIndexExpression;
Expand All @@ -36,7 +26,19 @@
import com.blazebit.persistence.parser.expression.PathExpression;
import com.blazebit.persistence.parser.expression.PropertyExpression;
import com.blazebit.persistence.parser.expression.VisitorAdapter;
import com.blazebit.persistence.parser.util.JpaMetamodelUtils;
import com.blazebit.persistence.spi.ExtendedAttribute;
import com.blazebit.persistence.spi.ExtendedManagedType;
import com.blazebit.persistence.spi.JpaProvider;

import javax.persistence.FetchType;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* This visitor resolves entity references to their attributes. This is needed for entity references
Expand All @@ -50,13 +52,21 @@
public class EntitySelectResolveVisitor extends VisitorAdapter {

private final EntityMetamodel m;
private final JpaProvider jpaProvider;
private final Set<PathExpression> pathExpressions;

public EntitySelectResolveVisitor(EntityMetamodel m, Set<PathExpression> pathExpressions) {
private JoinNode rootNode;

public EntitySelectResolveVisitor(EntityMetamodel m, JpaProvider jpaProvider, Set<PathExpression> pathExpressions) {
this.m = m;
this.jpaProvider = jpaProvider;
this.pathExpressions = pathExpressions;
}

public JoinNode getRootNode() {
return rootNode;
}

@Override
public void visit(FunctionExpression expression) {
// Skip all functions in here
Expand All @@ -71,23 +81,34 @@ public void visit(PathExpression expression) {
* the group by (because of DB2) we need to resolve such entity
* selects here
*/
JoinNode baseNode = ((JoinNode) expression.getBaseNode());
if (baseNode == null) {
rootNode = ((JoinNode) expression.getBaseNode());
if (rootNode == null) {
// This is an alias expression to a complex expression
return;
}
EntityType<?> entityType = m.getEntity(baseNode.getJavaType());
EntityType<?> entityType = m.getEntity(rootNode.getJavaType());
if (entityType == null) {
// ignore if the expression is not an entity
return;
}

Class<?> entityClass = entityType.getJavaType();
// we need to ensure a deterministic order for testing
SortedSet<Attribute<?, ?>> sortedAttributes = new TreeSet<>(JpaMetamodelUtils.ATTRIBUTE_NAME_COMPARATOR);
// TODO: a polymorphic query will fail because we don't collect subtype properties
sortedAttributes.addAll(entityType.getAttributes());
for (Attribute<?, ?> attr : sortedAttributes) {
ExtendedManagedType<?> extendedManagedType = m.getManagedType(ExtendedManagedType.class, entityType);
Set<String> attributePaths;
if (rootNode.getValuesIdNames() != null && !rootNode.getValuesIdNames().isEmpty()) {
attributePaths = rootNode.getValuesIdNames();
} else {
attributePaths = Collections.emptySet();
}
Map<String, ExtendedAttribute<?, ?>> ownedSingularAttributes = (Map<String, ExtendedAttribute<?, ?>>) (Map) extendedManagedType.getOwnedSingularAttributes();
Collection<String> propertyPaths = JpaUtils.getEmbeddedPropertyPaths(ownedSingularAttributes, null, jpaProvider.needsElementCollectionIdCutoff(), true);
for (String propertyPath : propertyPaths) {
// Skip if the attribute is not in the desired attribute paths
if (!attributePaths.isEmpty() && !attributePaths.contains(propertyPath)) {
continue;
}
ExtendedAttribute<?, ?> extendedAttribute = ownedSingularAttributes.get(propertyPath);
Attribute<?, ?> attr = extendedAttribute.getAttribute();
boolean resolve = false;
if (ExpressionUtils.isAssociation(attr) && !attr.isCollection()) {
resolve = true;
Expand All @@ -101,9 +122,11 @@ public void visit(PathExpression expression) {
if (resolve) {
List<PathElementExpression> paths = new ArrayList<>(expression.getExpressions().size() + 1);
paths.addAll(expression.getExpressions());
paths.add(new PropertyExpression(attr.getName()));
for (Attribute<?, ?> attribute : extendedAttribute.getAttributePath()) {
paths.add(new PropertyExpression(attribute.getName()));
}
PathExpression attrPath = new PathExpression(paths);
attrPath.setPathReference(new SimplePathReference(baseNode, attr.getName(), m.type(JpaMetamodelUtils.resolveFieldClass(entityClass, attr))));
attrPath.setPathReference(new SimplePathReference(rootNode, propertyPath, m.type(extendedAttribute.getElementClass())));
pathExpressions.add(attrPath);
}
}
Expand All @@ -127,5 +150,4 @@ public void visit(MapKeyExpression expression) {
@Override
public void visit(MapValueExpression expression) {
}

}
Loading

0 comments on commit 252b0f5

Please sign in to comment.