Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CAY-2529 Custom expression in DbJoin #368

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ CAY-2517 EventManager: optimization of adding listeners
CAY-2518 Add method to append having qualifier expression to ObjectSelect
CAY-2520 Split ObjectId into several specialized variants
CAY-2522 Make ObjectSelect a direct query
CAY-2529 Custom expression in DbJoin
CAY-2540 Modeler: redesign dbRelationship editor dialog
CAY-2543 Move ResultSetMapping generation from metadata to translator

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@
****************************************************************/
package org.apache.cayenne.project.validation;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.map.DbJoin;
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.util.Util;
import org.apache.cayenne.validation.ValidationResult;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

class DbRelationshipValidator extends ConfigurationNodeValidator {

void validate(DbRelationship relationship, ValidationResult validationResult) {
Expand All @@ -48,25 +48,27 @@ void validate(DbRelationship relationship, ValidationResult validationResult) {
toString(relationship));
} else {
// validate joins
for (DbJoin join : relationship.getJoins()) {
if (join.getSource() == null && join.getTarget() == null) {
addFailure(
validationResult,
relationship,
"DbRelationship '%s' has a join with no source and target attributes selected",
toString(relationship));
} else if (join.getSource() == null) {
addFailure(
validationResult,
relationship,
"DbRelationship '%s' has a join with no source attribute selected",
toString(relationship));
} else if (join.getTarget() == null) {
addFailure(
validationResult,
relationship,
"DbRelationship '%s' has a join with no target attribute selected",
toString(relationship));
if(!relationship.isUseJoinExp()) {
for (DbJoin join : relationship.getJoins()) {
if (join.getSource() == null && join.getTarget() == null) {
addFailure(
validationResult,
relationship,
"DbRelationship '%s' has a join with no source and target attributes selected",
toString(relationship));
} else if (join.getSource() == null) {
addFailure(
validationResult,
relationship,
"DbRelationship '%s' has a join with no source attribute selected",
toString(relationship));
} else if (join.getTarget() == null) {
addFailure(
validationResult,
relationship,
"DbRelationship '%s' has a join with no target attribute selected",
toString(relationship));
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@

package org.apache.cayenne.access;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.ObjectId;
import org.apache.cayenne.access.DataDomainSyncBucket.PropagatedValueFactory;
import org.apache.cayenne.exp.parser.ASTDbPath;
Expand All @@ -32,10 +37,6 @@
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.ObjRelationship;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
* Processes object diffs, generating DB diffs. Can be used for both UPDATE and
* INSERT.
Expand Down Expand Up @@ -177,6 +178,9 @@ public void arcCreated(Object nodeId, Object targetNodeId, Object arcId) {
throw new IllegalArgumentException("Bad arcId: " + arcId);
}

} else if(relationship.isReadOnly()) {
throw new CayenneRuntimeException("Attempt to modify relationship marked as read-only: '%s'. ",
relationship.getName());
} else {
if (!relationship.isToMany() && relationship.isToPK()) {
doArcCreated(targetNodeId, arcId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,22 @@

package org.apache.cayenne.access;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.DataRow;
import org.apache.cayenne.Persistent;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.cayenne.exp.property.BaseProperty;
import org.apache.cayenne.exp.property.PropertyFactory;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbJoin;
import org.apache.cayenne.map.DbRelationship;
import org.apache.cayenne.map.ObjRelationship;
Expand All @@ -33,11 +44,8 @@
import org.apache.cayenne.query.QueryMetadata;
import org.apache.cayenne.reflect.ClassDescriptor;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.apache.cayenne.exp.ExpressionFactory.dbPathExp;
import static org.apache.cayenne.exp.ExpressionFactory.fullObjectExp;

/**
* Processes a number of DataRow sets corresponding to a given prefetch tree, resolving
Expand Down Expand Up @@ -155,33 +163,61 @@ public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
if (currentQuery == null
|| (maxIdQualifierSize > 0 && qualifiersCount + joins.size() > maxIdQualifierSize)) {

createDisjointByIdPrefetchQualifier(pathPrefix, currentQuery, joins, values);
if (relationship.hasReverseDdRelationship()) {
createDisjointByIdPrefetchQualifier(pathPrefix,
currentQuery,
getJoinsNames(joins, DbJoin::getTargetName),
values);
currentQuery = new PrefetchSelectQuery<>(node.getPath(), relationship);
} else {
createDisjointByIdPrefetchQualifier(pathPrefix,
currentQuery,
getPksNames(lastDbRelationship),
values);
currentQuery = createInversedQuery(node.getPath(), relationship);
}

currentQuery = new PrefetchSelectQuery<>(node.getPath(), relationship);
currentQuery.setFetchingDataRows(true);
queries.add(currentQuery);
qualifiersCount = 0;
values = new HashSet<>();
}

List<Object> joinValues = new ArrayList<>(joins.size());
for (DbJoin join : joins) {
Object targetValue = dataRow.get(join.getSourceName());
joinValues.add(targetValue);
List<Object> joinValues;
if(relationship.hasReverseDdRelationship()) {
joinValues = getValues(
getJoinsNames(joins, DbJoin::getSourceName),
dataRow);
} else {
joinValues = getValues(getPksNames(lastDbRelationship), dataRow);
}

if(values.add(joinValues)) {
qualifiersCount += joins.size();
}
}

// add final part of values
createDisjointByIdPrefetchQualifier(pathPrefix, currentQuery, joins, values);
if (relationship.hasReverseDdRelationship()) {
createDisjointByIdPrefetchQualifier(pathPrefix,
currentQuery,
getJoinsNames(joins, DbJoin::getTargetName),
values);
} else {
createDisjointByIdPrefetchQualifier(pathPrefix,
currentQuery,
getPksNames(lastDbRelationship),
values);
}

PrefetchTreeNode jointSubtree = node.cloneJointSubtree();

String reversePath = null;
if (relationship.isSourceIndependentFromTargetChange()) {
reversePath = "db:" + relationship.getReverseDbRelationshipPath();
reversePath = "db:";
reversePath += relationship.hasReverseDdRelationship() ?
relationship.getReverseDbRelationshipPath() :
relationship.getDbRelationshipPath();
}

List<DataRow> dataRows = new ArrayList<>();
Expand All @@ -203,17 +239,45 @@ public boolean startDisjointByIdPrefetch(PrefetchTreeNode node) {
return true;
}

private PrefetchSelectQuery<DataRow> createInversedQuery(String path, ObjRelationship relationship) {
PrefetchSelectQuery<DataRow> currentQuery = new PrefetchSelectQuery<>(path, relationship);
currentQuery.setRoot(relationship.getSourceEntity());
Expression fullObjectExp = fullObjectExp(dbPathExp(relationship.getDbRelationshipPath()));
Class<?> targetClassType = context.getEntityResolver().getClassDescriptor(relationship.getTargetEntityName()).getObjectClass();
BaseProperty<?> baseProperty = PropertyFactory.createBase(fullObjectExp, targetClassType);
currentQuery.setColumns(baseProperty);
return currentQuery;
}

private List<Object> getValues(List<String> names, DataRow dataRow) {
List<Object> joinValues = new ArrayList<>(names.size());
for(String name : names) {
Object targetValue = dataRow.get(name);
joinValues.add(targetValue);
}
return joinValues;
}

private List<String> getPksNames(DbRelationship dbRelationship) {
return dbRelationship.getSourceEntity().getPrimaryKeys()
.stream().map(DbAttribute::getName).collect(Collectors.toList());
}

private List<String> getJoinsNames(List<DbJoin> joins, Function<? super DbJoin, ? extends String> func) {
return joins.stream().map(func).collect(Collectors.toList());
}

private void createDisjointByIdPrefetchQualifier(String pathPrefix, PrefetchSelectQuery currentQuery,
List<DbJoin> joins, Set<List<Object>> values) {
List<String> names, Set<List<Object>> values) {
Expression allJoinsQualifier;
if(currentQuery != null) {
Expression[] qualifiers = new Expression[values.size()];
int i = 0;
for(List<Object> joinValues : values) {
allJoinsQualifier = null;
for(int j=0; j<joins.size(); j++) {
for(int j = 0; j < names.size(); j++) {
Expression joinQualifier = ExpressionFactory
.matchDbExp(pathPrefix + joins.get(j).getTargetName(), joinValues.get(j));
.matchDbExp(pathPrefix + names.get(j), joinValues.get(j));
if (allJoinsQualifier == null) {
allJoinsQualifier = joinQualifier;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.apache.cayenne.Persistent;
import org.apache.cayenne.graph.GraphManager;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.map.ObjRelationship;
import org.apache.cayenne.reflect.ClassDescriptor;

/**
Expand All @@ -46,11 +47,8 @@ class JoinedIdParentAttachementStrategy implements ParentAttachmentStrategy {
.getResolver()
.getDescriptor();

relatedIdPrefix = node
.getIncoming()
.getRelationship()
.getReverseDbRelationshipPath()
+ ".";
ObjRelationship relationship = node.getIncoming().getRelationship();
relatedIdPrefix = buildRelatedIdPrefix(relationship);

sourceEntities = parentDescriptor.getEntityInheritanceTree().allSubEntities();

Expand Down Expand Up @@ -80,4 +78,10 @@ public void linkToParent(DataRow row, Persistent object) {

node.linkToParent(object, parentObject);
}

private String buildRelatedIdPrefix(ObjRelationship relationship) {
return relationship.hasReverseDdRelationship() ?
relationship.getReverseDbRelationshipPath() + "."
: relationship.getSourceEntity().getDbEntityName() + ".";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@

package org.apache.cayenne.access;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.DataRow;
import org.apache.cayenne.Persistent;
Expand All @@ -39,15 +48,6 @@
import org.apache.cayenne.reflect.ToManyProperty;
import org.apache.cayenne.reflect.ToOneProperty;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
* A specialized PrefetchTreeNode used for joint prefetch resolving.
*
Expand Down Expand Up @@ -187,9 +187,11 @@ && getIncoming() != null
.getRelationship()
.getDbRelationships()
.get(0);
for (final DbJoin join : r.getJoins()) {
appendColumn(targetSource, join.getTargetName(), prefix
+ join.getTargetName());
if(!r.isUseJoinExp()) {
for (final DbJoin join : r.getJoins()) {
appendColumn(targetSource, join.getTargetName(), prefix
+ join.getTargetName());
}
}
}

Expand All @@ -213,6 +215,9 @@ public boolean visitToOne(ToOneProperty property) {

private boolean visitRelationship(ArcProperty arc) {
DbRelationship dbRel = arc.getRelationship().getDbRelationships().get(0);
if(dbRel.isUseJoinExp()) {
return false;
}
for (DbAttribute attribute : dbRel.getSourceAttributes()) {
String target = attribute.getName();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
****************************************************************/
package org.apache.cayenne.access.translator.ejbql;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.dba.TypesMapping;
import org.apache.cayenne.ejbql.EJBQLBaseVisitor;
Expand All @@ -40,11 +45,6 @@
import org.apache.cayenne.reflect.ToManyProperty;
import org.apache.cayenne.reflect.ToOneProperty;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
* @since 3.0
*/
Expand Down Expand Up @@ -129,6 +129,10 @@ private void visitRelationship(ArcProperty property) {
ObjRelationship rel = property.getRelationship();
DbRelationship dbRel = rel.getDbRelationships().get(0);

if(dbRel.isUseJoinExp()) {
throw new CayenneRuntimeException("Ejbql query doesn't support custom expressions in join.");
}

for (DbJoin join : dbRel.getJoins()) {
DbAttribute src = join.getSource();
appendColumn(idVar, null, src, fields);
Expand Down
Loading