Skip to content

Commit

Permalink
Fixed #222
Browse files Browse the repository at this point in the history
  • Loading branch information
beikov committed Sep 9, 2016
1 parent 95c39cb commit 5d412e3
Show file tree
Hide file tree
Showing 26 changed files with 472 additions and 62 deletions.
93 changes: 93 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 @@ -161,6 +161,30 @@ public interface FromBuilder<X extends FromBuilder<X>> {
*/
public JoinOnBuilder<X> joinDefaultOn(String path, String alias, JoinType type);

/**
* Like {@link FromBuilder#joinOn(java.lang.String, java.lang.Class, java.lang.String, com.blazebit.persistence.JoinType) } but with
* the query root assumed as base.
*
* @param entityClass The entity class to join
* @param alias The alias for the joined element
* @param type The join type
* @return The restriction builder for the on-clause
* @since 1.2.0
*/
public JoinOnBuilder<X> joinOn(Class<?> entityClass, String alias, JoinType type);

/**
* Adds an entity join with an on-clause to the query and giving the joined element an alias.
*
* @param base The base node on which to join
* @param entityClass The entity class to join
* @param alias The alias for the joined element
* @param type The join type
* @return The restriction builder for the on-clause
* @since 1.2.0
*/
public JoinOnBuilder<X> joinOn(String base, Class<?> entityClass, String alias, JoinType type);

/**
* Like {@link FromBuilder#join(java.lang.String, java.lang.String, com.blazebit.persistence.JoinType) } but with
* {@link JoinType#INNER}.
Expand Down Expand Up @@ -201,6 +225,29 @@ public interface FromBuilder<X extends FromBuilder<X>> {
*/
public JoinOnBuilder<X> innerJoinDefaultOn(String path, String alias);

/**
* Like {@link FromBuilder#joinOn(java.lang.Class, java.lang.String, com.blazebit.persistence.JoinType) } but with
* {@link JoinType#INNER}.
*
* @param entityClass The entity class to join
* @param alias The alias for the joined element
* @return The restriction builder for the on-clause
* @since 1.2.0
*/
public JoinOnBuilder<X> innerJoinOn(Class<?> entityClass, String alias);

/**
* Like {@link FromBuilder#joinOn(java.lang.String, java.lang.Class, java.lang.String, com.blazebit.persistence.JoinType) } but with
* {@link JoinType#INNER}.
*
* @param base The base node on which to join
* @param entityClass The entity class to join
* @param alias The alias for the joined element
* @return The restriction builder for the on-clause
* @since 1.2.0
*/
public JoinOnBuilder<X> innerJoinOn(String base, Class<?> entityClass, String alias);

/**
* Like {@link FromBuilder#join(java.lang.String, java.lang.String, com.blazebit.persistence.JoinType) } but with
* {@link JoinType#LEFT}.
Expand Down Expand Up @@ -241,6 +288,29 @@ public interface FromBuilder<X extends FromBuilder<X>> {
*/
public JoinOnBuilder<X> leftJoinDefaultOn(String path, String alias);

/**
* Like {@link FromBuilder#joinOn(java.lang.Class, java.lang.String, com.blazebit.persistence.JoinType) } but with
* {@link JoinType#LEFT}.
*
* @param entityClass The entity class to join
* @param alias The alias for the joined element
* @return The restriction builder for the on-clause
* @since 1.2.0
*/
public JoinOnBuilder<X> leftJoinOn(Class<?> entityClass, String alias);

/**
* Like {@link FromBuilder#joinOn(java.lang.String, java.lang.Class, java.lang.String, com.blazebit.persistence.JoinType) } but with
* {@link JoinType#LEFT}.
*
* @param base The base node on which to join
* @param entityClass The entity class to join
* @param alias The alias for the joined element
* @return The restriction builder for the on-clause
* @since 1.2.0
*/
public JoinOnBuilder<X> leftJoinOn(String base, Class<?> entityClass, String alias);

/**
* Like {@link FromBuilder#join(java.lang.String, java.lang.String, com.blazebit.persistence.JoinType) } but with
* {@link JoinType#RIGHT}.
Expand Down Expand Up @@ -280,4 +350,27 @@ public interface FromBuilder<X extends FromBuilder<X>> {
* @return The restriction builder for the on-clause
*/
public JoinOnBuilder<X> rightJoinDefaultOn(String path, String alias);

/**
* Like {@link FromBuilder#joinOn(java.lang.Class, java.lang.String, com.blazebit.persistence.JoinType) } but with
* {@link JoinType#RIGHT}.
*
* @param entityClass The entity class to join
* @param alias The alias for the joined element
* @return The restriction builder for the on-clause
* @since 1.2.0
*/
public JoinOnBuilder<X> rightJoinOn(Class<?> entityClass, String alias);

/**
* Like {@link FromBuilder#joinOn(java.lang.String, java.lang.Class, java.lang.String, com.blazebit.persistence.JoinType) } but with
* {@link JoinType#RIGHT}.
*
* @param base The base node on which to join
* @param entityClass The entity class to join
* @param alias The alias for the joined element
* @return The restriction builder for the on-clause
* @since 1.2.0
*/
public JoinOnBuilder<X> rightJoinOn(String base, Class<?> entityClass, String alias);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public interface JpaProvider {

public boolean supportsJpa21();

public boolean supportsEntityJoin();

public boolean supportsInsertStatement();

public boolean needsBracketsForListParamter();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,21 @@ public JoinOnBuilder<BuilderType> joinDefaultOn(String path, String alias, JoinT
return joinManager.joinOn((BuilderType) this, path, alias, type, true);
}

@SuppressWarnings("unchecked")
public JoinOnBuilder<BuilderType> joinOn(Class<?> clazz, String alias, JoinType type) {
return joinOn(joinManager.getRootNodeOrFail("An explicit base join node is required when multiple root nodes are used!").getAlias(), clazz, alias, type);
}

@SuppressWarnings("unchecked")
public JoinOnBuilder<BuilderType> joinOn(String base, Class<?> entityClass, String alias, JoinType type) {
clearCache();
checkJoinPreconditions(base, alias, type);
if (entityClass == null) {
throw new NullPointerException("entityClass");
}
return joinManager.joinOn((BuilderType) this, base, entityClass, alias, type);
}

public JoinOnBuilder<BuilderType> innerJoinOn(String path, String alias) {
return joinOn(path, alias, JoinType.INNER);
}
Expand All @@ -963,6 +978,14 @@ public JoinOnBuilder<BuilderType> innerJoinDefaultOn(String path, String alias)
return joinDefaultOn(path, alias, JoinType.INNER);
}

public JoinOnBuilder<BuilderType> innerJoinOn(Class<?> clazz, String alias) {
return joinOn(clazz, alias, JoinType.INNER);
}

public JoinOnBuilder<BuilderType> innerJoinOn(String base, Class<?> clazz, String alias) {
return joinOn(base, clazz, alias, JoinType.INNER);
}

public JoinOnBuilder<BuilderType> leftJoinOn(String path, String alias) {
return joinOn(path, alias, JoinType.LEFT);
}
Expand All @@ -971,6 +994,14 @@ public JoinOnBuilder<BuilderType> leftJoinDefaultOn(String path, String alias) {
return joinDefaultOn(path, alias, JoinType.LEFT);
}

public JoinOnBuilder<BuilderType> leftJoinOn(Class<?> clazz, String alias) {
return joinOn(clazz, alias, JoinType.LEFT);
}

public JoinOnBuilder<BuilderType> leftJoinOn(String base, Class<?> clazz, String alias) {
return joinOn(base, clazz, alias, JoinType.LEFT);
}

public JoinOnBuilder<BuilderType> rightJoinOn(String path, String alias) {
return joinOn(path, alias, JoinType.RIGHT);
}
Expand All @@ -979,6 +1010,14 @@ public JoinOnBuilder<BuilderType> rightJoinDefaultOn(String path, String alias)
return joinDefaultOn(path, alias, JoinType.RIGHT);
}

public JoinOnBuilder<BuilderType> rightJoinOn(Class<?> clazz, String alias) {
return joinOn(clazz, alias, JoinType.RIGHT);
}

public JoinOnBuilder<BuilderType> rightJoinOn(String base, Class<?> clazz, String alias) {
return joinOn(base, clazz, alias, JoinType.RIGHT);
}

private void checkJoinPreconditions(String path, String alias, JoinType type) {
if (path == null) {
throw new NullPointerException("path");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@ public class JoinAliasInfo implements AliasInfo {
private String alias;
private JoinNode joinNode;
private boolean implicit;
private final boolean rootNode;
// The absolute normalized path with the root as implicit base
private final String absolutePath;
private final AliasManager aliasOwner;

public JoinAliasInfo(String alias, String absolutePath, boolean implicit, AliasManager aliasOwner) {
public JoinAliasInfo(String alias, String absolutePath, boolean implicit, boolean rootNode, AliasManager aliasOwner) {
this.alias = alias;
this.absolutePath = absolutePath;
this.implicit = implicit;
this.rootNode = rootNode;
this.aliasOwner = aliasOwner;
}

Expand Down Expand Up @@ -70,6 +72,10 @@ public void setImplicit(boolean implicit) {
this.implicit = implicit;
}

public boolean isRootNode() {
return rootNode;
}

@Override
public AliasManager getAliasOwner() {
return this.aliasOwner;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ String addRoot(EntityType<?> clazz, String rootAlias) {
rootAlias = aliasManager.generatePostfixedAlias(alias);
}
}
JoinAliasInfo rootAliasInfo = new JoinAliasInfo(rootAlias, rootAlias, true, aliasManager);
JoinAliasInfo rootAliasInfo = new JoinAliasInfo(rootAlias, rootAlias, true, true, aliasManager);
JoinNode rootNode = new JoinNode(null, null, null, rootAliasInfo, null, clazz.getJavaType(), null);
rootAliasInfo.setJoinNode(rootNode);
rootNodes.add(rootNode);
Expand Down Expand Up @@ -136,7 +136,7 @@ String addRoot(String correlationPath, String rootAlias) {
}
// TODO: Implement treat support for correlated subqueries
String treatType = null;
JoinAliasInfo rootAliasInfo = new JoinAliasInfo(rootAlias, rootAlias, true, aliasManager);
JoinAliasInfo rootAliasInfo = new JoinAliasInfo(rootAlias, rootAlias, true, true, aliasManager);
JoinNode rootNode = new JoinNode(correlationParent, correlationProperty, treatType, rootAliasInfo, attributeType, null);
rootAliasInfo.setJoinNode(rootNode);
rootNodes.add(rootNode);
Expand Down Expand Up @@ -199,7 +199,8 @@ boolean hasJoins() {
List<JoinNode> nodes = rootNodes;
int size = nodes.size();
for (int i = 0; i < size; i++) {
if (!nodes.get(i).getNodes().isEmpty()) {
JoinNode n = nodes.get(i);
if (!n.getNodes().isEmpty() || !n.getEntityJoinNodes().isEmpty()) {
return true;
}
}
Expand Down Expand Up @@ -274,6 +275,11 @@ Set<JoinNode> buildClause(StringBuilder sb, Set<ClauseType> clauseExclusions, St
// TODO: not sure if needed since applyImplicitJoins will already invoke that
rootNode.registerDependencies();
applyJoins(sb, rootNode.getAliasInfo(), rootNode.getNodes(), clauseExclusions, aliasPrefix, collectCollectionJoinNodes);
if (!rootNode.getEntityJoinNodes().isEmpty()) {
// TODO: Fix this with #216
boolean isCollection = true;
applyJoins(sb, rootNode.getAliasInfo(), new ArrayList<JoinNode>(rootNode.getEntityJoinNodes()), isCollection, clauseExclusions, aliasPrefix, collectCollectionJoinNodes);
}
}

return collectionJoinNodes;
Expand Down Expand Up @@ -347,6 +353,8 @@ private void renderJoinNode(StringBuilder sb, JoinAliasInfo joinBase, JoinNode n
} else {
throw new IllegalArgumentException("Treat should not be used as the JPA provider does not support subtype property access!");
}
} else if (node.getAliasInfo().isRootNode()) {
sb.append(metamodel.entity(node.getPropertyClass()).getName()).append(' ');
} else {
renderParentAlias(sb, node, joinBase.getAlias());
sb.append(node.getParentTreeNode().getRelationName()).append(' ');
Expand Down Expand Up @@ -412,30 +420,39 @@ private void renderReverseDependency(StringBuilder sb, JoinNode dependency, Stri
private void applyJoins(StringBuilder sb, JoinAliasInfo joinBase, Map<String, JoinTreeNode> nodes, Set<ClauseType> clauseExclusions, String aliasPrefix, boolean collectCollectionJoinNodes) {
for (Map.Entry<String, JoinTreeNode> nodeEntry : nodes.entrySet()) {
JoinTreeNode treeNode = nodeEntry.getValue();
List<JoinNode> stack = new ArrayList<JoinNode>();
stack.addAll(treeNode.getJoinNodes().values());

for (JoinNode node : treeNode.getJoinNodes().values()) {
// If the clauses in which a join node occurs are all excluded or the join node is not mandatory for the cardinality, we skip it
if (!clauseExclusions.isEmpty() && clauseExclusions.containsAll(node.getClauseDependencies()) && !node.isCardinalityMandatory()) {
continue;
}
applyJoins(sb, joinBase, stack, treeNode.isCollection(), clauseExclusions, aliasPrefix, collectCollectionJoinNodes);
}
}

// We have to render any dependencies this join node has before actually rendering itself
if (!node.getDependencies().isEmpty()) {
renderReverseDependency(sb, node, aliasPrefix);
}
private void applyJoins(StringBuilder sb, JoinAliasInfo joinBase, List<JoinNode> stack, boolean isCollection, Set<ClauseType> clauseExclusions, String aliasPrefix, boolean collectCollectionJoinNodes) {
while (!stack.isEmpty()) {
JoinNode node = stack.remove(stack.size() - 1);
// If the clauses in which a join node occurs are all excluded or the join node is not mandatory for the cardinality, we skip it
if (!clauseExclusions.isEmpty() && clauseExclusions.containsAll(node.getClauseDependencies()) && !node.isCardinalityMandatory()) {
continue;
}

// Collect the join nodes referring to collections
if (collectCollectionJoinNodes && treeNode.isCollection()) {
collectionJoinNodes.add(node);
}
stack.addAll(node.getEntityJoinNodes());

// Finally render this join node
renderJoinNode(sb, joinBase, node, aliasPrefix);
// We have to render any dependencies this join node has before actually rendering itself
if (!node.getDependencies().isEmpty()) {
renderReverseDependency(sb, node, aliasPrefix);
}

// Render child nodes recursively
if (!node.getNodes().isEmpty()) {
applyJoins(sb, node.getAliasInfo(), node.getNodes(), clauseExclusions, aliasPrefix, collectCollectionJoinNodes);
}
// Collect the join nodes referring to collections
if (collectCollectionJoinNodes && isCollection) {
collectionJoinNodes.add(node);
}

// Finally render this join node
renderJoinNode(sb, joinBase, node, aliasPrefix);

// Render child nodes recursively
if (!node.getNodes().isEmpty()) {
applyJoins(sb, node.getAliasInfo(), node.getNodes(), clauseExclusions, aliasPrefix, collectCollectionJoinNodes);
}
}
}
Expand Down Expand Up @@ -535,6 +552,34 @@ private boolean isJoinableSelectAlias(PathExpression pathExpr, boolean fromSelec
return false;
}

<X> JoinOnBuilder<X> joinOn(X result, String base, Class<?> clazz, String alias, JoinType type) {
AliasInfo aliasInfo = aliasManager.getAliasInfo(base);

if (aliasInfo == null || !(aliasInfo instanceof JoinAliasInfo)) {
throw new IllegalArgumentException("The base '" + base + "' is not a valid join alias!");
}

EntityType<?> entityType = metamodel.entity(clazz);

if (alias == null || alias.isEmpty()) {
throw new IllegalArgumentException("Invalid empty alias!");
}
if (type != JoinType.INNER && !mainQuery.jpaProvider.supportsEntityJoin()) {
throw new IllegalArgumentException("The JPA provider does not support entity joins and an emulation for non-inner entity joins is not implemented!");
}

JoinNode baseNode = ((JoinAliasInfo) aliasInfo).getJoinNode();

JoinAliasInfo joinAliasInfo = new JoinAliasInfo(alias, null, false, true, aliasManager);
JoinNode entityJoinNode = new JoinNode(baseNode, null, null, joinAliasInfo, type, entityType.getJavaType(), null);
joinAliasInfo.setJoinNode(entityJoinNode);
baseNode.addEntityJoin(entityJoinNode);
aliasManager.registerAliasInfo(joinAliasInfo);

joinOnBuilderListener.joinNode = entityJoinNode;
return joinOnBuilderListener.startBuilder(new JoinOnBuilderImpl<X>(result, joinOnBuilderListener, parameterManager, expressionFactory, subqueryInitFactory));
}

<X> JoinOnBuilder<X> joinOn(X result, String path, String alias, JoinType type, boolean defaultJoin) {
joinOnBuilderListener.joinNode = join(path, alias, type, false, defaultJoin);
return joinOnBuilderListener.startBuilder(new JoinOnBuilderImpl<X>(result, joinOnBuilderListener, parameterManager, expressionFactory, subqueryInitFactory));
Expand Down Expand Up @@ -1385,7 +1430,7 @@ private JoinNode getOrCreate(JoinNode baseNode, String baseNodeTreatType, String
alias = aliasManager.generatePostfixedAlias(alias);
}

JoinAliasInfo newAliasInfo = new JoinAliasInfo(alias, currentJoinPath, implicit, aliasManager);
JoinAliasInfo newAliasInfo = new JoinAliasInfo(alias, currentJoinPath, implicit, false, aliasManager);
aliasManager.registerAliasInfo(newAliasInfo);
node = new JoinNode(baseNode, treeNode, baseNodeTreatType, newAliasInfo, type, joinRelationClass, treatType);
newAliasInfo.setJoinNode(node);
Expand Down
Loading

0 comments on commit 5d412e3

Please sign in to comment.