use of org.apache.cayenne.map.DbJoin in project cayenne by apache.
the class EJBQLDbPathTranslator method processTerminatingRelationship.
protected void processTerminatingRelationship(DbRelationship relationship) {
if (relationship.isToMany()) {
// use an outer join for to-many matches
resolveJoin(false);
DbEntity table = (DbEntity) relationship.getTargetEntity();
String alias = this.lastAlias != null ? lastAlias : context.getTableAlias(idPath, context.getQuotingStrategy().quotedFullyQualifiedName(table));
Collection<DbAttribute> pks = table.getPrimaryKeys();
if (pks.size() == 1) {
DbAttribute pk = pks.iterator().next();
context.append(' ');
if (isUsingAliases()) {
context.append(alias).append('.');
}
context.append(context.getQuotingStrategy().quotedName(pk));
} else {
throw new EJBQLException("Multi-column PK to-many matches are not yet supported.");
}
} else {
// match FK against the target object
DbEntity table = (DbEntity) relationship.getSourceEntity();
String alias = this.lastAlias != null ? lastAlias : context.getTableAlias(idPath, context.getQuotingStrategy().quotedFullyQualifiedName(table));
List<DbJoin> joins = relationship.getJoins();
if (joins.size() == 1) {
DbJoin join = joins.get(0);
context.append(' ');
if (isUsingAliases()) {
context.append(alias).append('.');
}
context.append(context.getQuotingStrategy().quotedName(join.getSource()));
} else {
Map<String, String> multiColumnMatch = new HashMap<>(joins.size() + 2);
for (DbJoin join : joins) {
String column = isUsingAliases() ? alias + "." + join.getSourceName() : join.getSourceName();
multiColumnMatch.put(join.getTargetName(), column);
}
appendMultiColumnPath(EJBQLMultiColumnOperand.getPathOperand(context, multiColumnMatch));
}
}
}
use of org.apache.cayenne.map.DbJoin in project cayenne by apache.
the class EJBQLIdentifierColumnsTranslator method visitIdentifier.
@Override
public boolean visitIdentifier(EJBQLExpression expression) {
Map<String, String> xfields = null;
if (context.isAppendingResultColumns()) {
xfields = context.nextEntityResult().getFields();
}
// assign whatever we have to a final ivar so that it can be accessed
// within
// the inner class
final Map<String, String> fields = xfields;
final String idVar = expression.getText();
// append all table columns ... the trick is to follow the algorithm for
// describing the fields in the expression compiler, so that we could
// assign
// columns labels from FieldResults in the order we encounter them
// here...
// TODO: andrus 2008/02/17 - this is a bit of a hack, think of a better
// solution
ClassDescriptor descriptor = context.getEntityDescriptor(idVar);
PropertyVisitor visitor = new PropertyVisitor() {
public boolean visitAttribute(AttributeProperty property) {
ObjAttribute oa = property.getAttribute();
Iterator<?> dbPathIterator = oa.getDbPathIterator();
EJBQLJoinAppender joinAppender = null;
String marker = null;
EJBQLTableId lhsId = new EJBQLTableId(idVar);
while (dbPathIterator.hasNext()) {
Object pathPart = dbPathIterator.next();
if (pathPart == null) {
throw new CayenneRuntimeException("ObjAttribute has no component: %s", oa.getName());
} else if (pathPart instanceof DbRelationship) {
if (marker == null) {
marker = EJBQLJoinAppender.makeJoinTailMarker(idVar);
joinAppender = context.getTranslatorFactory().getJoinAppender(context);
}
DbRelationship dr = (DbRelationship) pathPart;
EJBQLTableId rhsId = new EJBQLTableId(lhsId, dr.getName());
joinAppender.appendOuterJoin(marker, lhsId, rhsId);
lhsId = rhsId;
} else if (pathPart instanceof DbAttribute) {
appendColumn(idVar, oa, (DbAttribute) pathPart, fields, oa.getType());
}
}
return true;
}
public boolean visitToMany(ToManyProperty property) {
visitRelationship(property);
return true;
}
public boolean visitToOne(ToOneProperty property) {
visitRelationship(property);
return true;
}
private void visitRelationship(ArcProperty property) {
ObjRelationship rel = property.getRelationship();
DbRelationship dbRel = rel.getDbRelationships().get(0);
for (DbJoin join : dbRel.getJoins()) {
DbAttribute src = join.getSource();
appendColumn(idVar, null, src, fields);
}
}
};
// EJBQL queries are polymorphic by definition - there is no distinction
// between
// inheritance/no-inheritance fetch
descriptor.visitAllProperties(visitor);
// append id columns ... (some may have been appended already via
// relationships)
DbEntity table = descriptor.getEntity().getDbEntity();
for (DbAttribute pk : table.getPrimaryKeys()) {
appendColumn(idVar, null, pk, fields);
}
// append inheritance discriminator columns...
for (ObjAttribute column : descriptor.getDiscriminatorColumns()) {
appendColumn(idVar, column, column.getDbAttribute(), fields);
}
addPrefetchedColumnsIfAny(idVar);
return false;
}
use of org.apache.cayenne.map.DbJoin in project cayenne by apache.
the class EJBQLPathTranslator method processTerminatingRelationship.
protected void processTerminatingRelationship(ObjRelationship relationship) {
if (relationship.isSourceIndependentFromTargetChange()) {
// (andrus) use an outer join for to-many matches.. This is somewhat
// different
// from traditional Cayenne SelectQuery, as EJBQL spec does not
// allow regular
// path matches done against to-many relationships, and instead
// provides
// MEMBER OF and IS EMPTY operators. Outer join is needed for IS
// EMPTY... I
// guess MEMBER OF could've been done with an inner join though..
this.innerJoin = false;
resolveJoin();
DbRelationship dbRelationship = chooseDbRelationship(relationship);
DbEntity table = (DbEntity) dbRelationship.getTargetEntity();
String alias = this.lastAlias != null ? lastAlias : context.getTableAlias(idPath, context.getQuotingStrategy().quotedFullyQualifiedName(table));
Collection<DbAttribute> pks = table.getPrimaryKeys();
if (pks.size() == 1) {
DbAttribute pk = pks.iterator().next();
context.append(' ');
if (isUsingAliases()) {
context.append(alias).append('.');
}
context.append(context.getQuotingStrategy().quotedName(pk));
} else {
throw new EJBQLException("Multi-column PK to-many matches are not yet supported.");
}
} else {
// match FK against the target object
DbRelationship dbRelationship = chooseDbRelationship(relationship);
DbEntity table = (DbEntity) dbRelationship.getSourceEntity();
String alias = this.lastAlias != null ? lastAlias : context.getTableAlias(idPath, context.getQuotingStrategy().quotedFullyQualifiedName(table));
List<DbJoin> joins = dbRelationship.getJoins();
if (joins.size() == 1) {
DbJoin join = joins.get(0);
context.append(' ');
if (isUsingAliases()) {
context.append(alias).append('.');
}
context.append(context.getQuotingStrategy().quotedName(join.getSource()));
} else {
Map<String, String> multiColumnMatch = new HashMap<>(joins.size() + 2);
for (DbJoin join : joins) {
String column = isUsingAliases() ? alias + "." + join.getSourceName() : join.getSourceName();
multiColumnMatch.put(join.getTargetName(), column);
}
appendMultiColumnPath(EJBQLMultiColumnOperand.getPathOperand(context, multiColumnMatch));
}
}
}
use of org.apache.cayenne.map.DbJoin in project cayenne by apache.
the class DropColumnToModelIT method testRemoveFKColumnWithoutRelationshipInDb.
@Test
public void testRemoveFKColumnWithoutRelationshipInDb() throws Exception {
dropTableIfPresent("NEW_TABLE");
dropTableIfPresent("NEW_TABLE2");
assertTokensAndExecute(0, 0);
DbEntity dbEntity1 = new DbEntity("NEW_TABLE");
DbAttribute e1col1 = new DbAttribute("ID", Types.INTEGER, dbEntity1);
e1col1.setMandatory(true);
e1col1.setPrimaryKey(true);
dbEntity1.addAttribute(e1col1);
DbAttribute e1col2 = new DbAttribute("NAME", Types.VARCHAR, dbEntity1);
e1col2.setMaxLength(10);
e1col2.setMandatory(false);
dbEntity1.addAttribute(e1col2);
map.addDbEntity(dbEntity1);
DbEntity dbEntity2 = new DbEntity("NEW_TABLE2");
DbAttribute e2col1 = new DbAttribute("ID", Types.INTEGER, dbEntity2);
e2col1.setMandatory(true);
e2col1.setPrimaryKey(true);
dbEntity2.addAttribute(e2col1);
DbAttribute e2col2 = new DbAttribute("FK", Types.INTEGER, dbEntity2);
dbEntity2.addAttribute(e2col2);
DbAttribute e2col3 = new DbAttribute("NAME", Types.VARCHAR, dbEntity2);
e2col3.setMaxLength(10);
dbEntity2.addAttribute(e2col3);
map.addDbEntity(dbEntity2);
assertTokensAndExecute(2, 0);
assertTokensAndExecute(0, 0);
// force drop fk column in db
execute(mergerFactory().createDropColumnToDb(dbEntity2, e2col2));
// create db relationships, but do not sync them to db
DbRelationship rel1To2 = new DbRelationship("rel1To2");
rel1To2.setSourceEntity(dbEntity1);
rel1To2.setTargetEntityName(dbEntity2);
rel1To2.setToMany(true);
rel1To2.addJoin(new DbJoin(rel1To2, e1col1.getName(), e2col2.getName()));
dbEntity1.addRelationship(rel1To2);
DbRelationship rel2To1 = new DbRelationship("rel2To1");
rel2To1.setSourceEntity(dbEntity2);
rel2To1.setTargetEntityName(dbEntity1);
rel2To1.setToMany(false);
rel2To1.addJoin(new DbJoin(rel2To1, e2col2.getName(), e1col1.getName()));
dbEntity2.addRelationship(rel2To1);
assertSame(rel1To2, rel2To1.getReverseRelationship());
assertSame(rel2To1, rel1To2.getReverseRelationship());
// create ObjEntities
ObjEntity objEntity1 = new ObjEntity("NewTable");
objEntity1.setDbEntity(dbEntity1);
ObjAttribute oatr1 = new ObjAttribute("name");
oatr1.setDbAttributePath(e1col2.getName());
oatr1.setType("java.lang.String");
objEntity1.addAttribute(oatr1);
map.addObjEntity(objEntity1);
ObjEntity objEntity2 = new ObjEntity("NewTable2");
objEntity2.setDbEntity(dbEntity2);
ObjAttribute o2a1 = new ObjAttribute("name");
o2a1.setDbAttributePath(e2col3.getName());
o2a1.setType("java.lang.String");
objEntity2.addAttribute(o2a1);
map.addObjEntity(objEntity2);
// create ObjRelationships
assertEquals(0, objEntity1.getRelationships().size());
assertEquals(0, objEntity2.getRelationships().size());
ObjRelationship objRel1To2 = new ObjRelationship("objRel1To2");
objRel1To2.addDbRelationship(rel1To2);
objRel1To2.setSourceEntity(objEntity1);
objRel1To2.setTargetEntityName(objEntity2);
objEntity1.addRelationship(objRel1To2);
ObjRelationship objRel2To1 = new ObjRelationship("objRel2To1");
objRel2To1.addDbRelationship(rel2To1);
objRel2To1.setSourceEntity(objEntity2);
objRel2To1.setTargetEntityName(objEntity1);
objEntity2.addRelationship(objRel2To1);
assertEquals(1, objEntity1.getRelationships().size());
assertEquals(1, objEntity2.getRelationships().size());
assertSame(objRel1To2, objRel2To1.getReverseRelationship());
assertSame(objRel2To1, objRel1To2.getReverseRelationship());
// try do use the merger to remove the column and relationship in the
// model
List<MergerToken> tokens = createMergeTokens();
assertTokens(tokens, 2, 0);
// TODO: reversing the following two tokens should also reverse the
// order
MergerToken token0 = tokens.get(0).createReverse(mergerFactory());
MergerToken token1 = tokens.get(1).createReverse(mergerFactory());
if (!(token0 instanceof DropRelationshipToModel && token1 instanceof DropColumnToModel || token1 instanceof DropRelationshipToModel && token0 instanceof DropColumnToModel)) {
fail();
}
// do not execute DropRelationshipToModel, only DropColumnToModel.
if (token1 instanceof DropColumnToModel) {
execute(token1);
} else {
execute(token0);
}
// check after merging
assertNull(dbEntity2.getAttribute(e2col2.getName()));
assertEquals(0, dbEntity1.getRelationships().size());
assertEquals(0, dbEntity2.getRelationships().size());
assertEquals(0, objEntity1.getRelationships().size());
assertEquals(0, objEntity2.getRelationships().size());
// clear up
dbEntity1.removeRelationship(rel1To2.getName());
dbEntity2.removeRelationship(rel2To1.getName());
map.removeObjEntity(objEntity1.getName(), true);
map.removeDbEntity(dbEntity1.getName(), true);
map.removeObjEntity(objEntity2.getName(), true);
map.removeDbEntity(dbEntity2.getName(), true);
resolver.refreshMappingCache();
assertNull(map.getObjEntity(objEntity1.getName()));
assertNull(map.getDbEntity(dbEntity1.getName()));
assertNull(map.getObjEntity(objEntity2.getName()));
assertNull(map.getDbEntity(dbEntity2.getName()));
assertFalse(map.getDbEntities().contains(dbEntity1));
assertFalse(map.getDbEntities().contains(dbEntity2));
assertTokensAndExecute(2, 0);
assertTokensAndExecute(0, 0);
}
use of org.apache.cayenne.map.DbJoin in project cayenne by apache.
the class DbRelationshipMerger method createTokensForMissingOriginal.
/**
* @param imported DbRelationship that is in db but not in model
* @return generated tokens
*/
@Override
Collection<MergerToken> createTokensForMissingOriginal(DbRelationship imported) {
DbEntity originalDbEntity = getOriginalSourceDbEntity(imported);
DbEntity targetEntity = getOriginalTargetDbEntity(imported);
if (targetEntity != null) {
imported.setTargetEntityName(targetEntity);
}
imported.setSourceEntity(originalDbEntity);
// manipulate the joins to match the DbAttributes in the model
for (DbJoin join : imported.getJoins()) {
DbAttribute sourceAttr = findDbAttribute(originalDbEntity, join.getSourceName());
if (sourceAttr != null) {
join.setSourceName(sourceAttr.getName());
}
DbAttribute targetAttr = findDbAttribute(targetEntity, join.getTargetName());
if (targetAttr != null) {
join.setTargetName(targetAttr.getName());
}
}
// Add all relationships. Tokens will decide whether or not to execute
MergerToken token = getTokenFactory().createDropRelationshipToDb(originalDbEntity, imported);
return Collections.singleton(token);
}
Aggregations