use of org.apache.cayenne.map.ObjRelationship in project cayenne by apache.
the class DropRelationshipToModelIT method testForeignKey.
@Test
public void testForeignKey() 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);
// create db relationships
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());
assertTokensAndExecute(3, 0);
assertTokensAndExecute(0, 0);
// 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());
// remove relationship and fk from model, merge to db and read to model
dbEntity2.removeRelationship(rel2To1.getName());
dbEntity1.removeRelationship(rel1To2.getName());
dbEntity2.removeAttribute(e2col2.getName());
List<MergerToken> tokens = createMergeTokens();
/**
* Drop Relationship NEW_TABLE2->NEW_TABLE To DB
* Drop Column NEW_TABLE2.FK To DB
*/
assertTokens(tokens, 2, 0);
for (MergerToken token : tokens) {
if (token.getDirection().isToDb()) {
execute(token);
}
}
assertTokensAndExecute(0, 0);
dbEntity2.addRelationship(rel2To1);
dbEntity1.addRelationship(rel1To2);
dbEntity2.addAttribute(e2col2);
// try do use the merger to remove the relationship in the model
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();
}
execute(token0);
execute(token1);
// 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.ObjRelationship in project cayenne by apache.
the class NameBuilderTest method testName_ObjEntityContext.
@Test
public void testName_ObjEntityContext() {
ObjEntity entity = new ObjEntity();
entity.getCallbackMap().getPostAdd().addCallbackMethod("getMe");
ObjAttribute a0 = new ObjAttribute();
String na0 = NameBuilder.builder(a0).in(entity).name();
assertEquals("untitledAttr", na0);
a0.setName(na0);
entity.addAttribute(a0);
ObjAttribute a1 = new ObjAttribute();
String na1 = NameBuilder.builder(a1).in(entity).name();
assertEquals("untitledAttr1", na1);
a1.setName(na1);
entity.addAttribute(a1);
ObjAttribute a2 = new ObjAttribute();
String na2 = NameBuilder.builder(a2).in(entity).baseName("me").name();
assertEquals("Conflict with callback method was not detected", "me1", na2);
a2.setName(na2);
entity.addAttribute(a2);
ObjRelationship r0 = new ObjRelationship();
String nr0 = NameBuilder.builder(r0).in(entity).name();
assertEquals("untitledRel", nr0);
r0.setName(nr0);
entity.addRelationship(r0);
ObjRelationship r1 = new ObjRelationship();
String nr1 = NameBuilder.builder(r1).in(entity).name();
assertEquals("untitledRel1", nr1);
r1.setName(nr1);
entity.addRelationship(r1);
}
use of org.apache.cayenne.map.ObjRelationship in project cayenne by apache.
the class ObjRelationshipValidator method validate.
void validate(ObjRelationship relationship, ValidationResult validationResult) {
if (Util.isEmptyString(relationship.getName())) {
addFailure(validationResult, relationship, "Unnamed ObjRelationship");
} else if (relationship.getSourceEntity().getAttribute(relationship.getName()) != null) {
// check if there are attributes having the same name
addFailure(validationResult, relationship, "ObjRelationship '%s' has the same name as one of ObjAttributes", toString(relationship));
} else {
NameValidationHelper helper = NameValidationHelper.getInstance();
String invalidChars = helper.invalidCharsInObjPathComponent(relationship.getName());
if (invalidChars != null) {
addFailure(validationResult, relationship, "ObjRelationship name '%s' contains invalid characters: %s", toString(relationship), invalidChars);
} else if (helper.invalidDataObjectProperty(relationship.getName())) {
addFailure(validationResult, relationship, "ObjRelationship name '%s' is a reserved word", toString(relationship));
}
}
if (relationship.getTargetEntity() == null) {
addFailure(validationResult, relationship, "ObjRelationship '%s' has no target entity", toString(relationship));
} else {
// check for missing DbRelationship mappings
List<DbRelationship> dbRels = relationship.getDbRelationships();
if (dbRels.isEmpty()) {
addFailure(validationResult, relationship, "ObjRelationship '%s' has no DbRelationship mapping", toString(relationship));
} else {
DbEntity expectedSrc = relationship.getSourceEntity().getDbEntity();
DbEntity expectedTarget = relationship.getTargetEntity().getDbEntity();
if ((dbRels.get(0)).getSourceEntity() != expectedSrc || (dbRels.get(dbRels.size() - 1)).getTargetEntity() != expectedTarget) {
addFailure(validationResult, relationship, "ObjRelationship '%s' has incomplete DbRelationship mapping", toString(relationship));
}
}
}
// foreign key attributes are mandatory.
if (relationship.isToMany() && !relationship.isFlattened() && (relationship.getDeleteRule() == DeleteRule.NULLIFY)) {
ObjRelationship inverse = relationship.getReverseRelationship();
if (inverse != null) {
DbRelationship firstRel = inverse.getDbRelationships().get(0);
Iterator<DbJoin> attributePairIterator = firstRel.getJoins().iterator();
// by default, the relation will be check for mandatory.
boolean check = true;
while (attributePairIterator.hasNext()) {
DbJoin pair = attributePairIterator.next();
if (!pair.getSource().isMandatory()) {
// a field of the fk can be nullable, cancel the check.
check = false;
break;
}
}
if (check) {
addFailure(validationResult, relationship, "ObjRelationship '%s' has a Nullify delete rule and a mandatory reverse relationship", toString(relationship));
}
}
}
// check for relationships with same source and target entities
ObjEntity entity = relationship.getSourceEntity();
for (ObjRelationship rel : entity.getRelationships()) {
if (relationship.getDbRelationshipPath() != null && relationship.getDbRelationshipPath().equals(rel.getDbRelationshipPath())) {
if (relationship != rel && relationship.getTargetEntity() == rel.getTargetEntity() && relationship.getSourceEntity() == rel.getSourceEntity()) {
addFailure(validationResult, relationship, "ObjectRelationship '%s' duplicates relationship '%s'", toString(relationship), toString(rel));
}
}
}
// check for invalid relationships in inherited entities
if (relationship.getReverseRelationship() != null) {
ObjRelationship revRel = relationship.getReverseRelationship();
if (relationship.getSourceEntity() != revRel.getTargetEntity() || relationship.getTargetEntity() != revRel.getSourceEntity()) {
addFailure(validationResult, revRel, "Usage of super entity's relationships '%s' as reversed relationships for sub entity is discouraged", toString(revRel));
}
}
checkForDuplicates(relationship, validationResult);
}
use of org.apache.cayenne.map.ObjRelationship in project cayenne by apache.
the class BaseDataObject method setReverseRelationship.
/**
* Initializes reverse relationship from object <code>val</code> to this
* object.
*
* @param relName
* name of relationship from this object to <code>val</code>.
*/
protected void setReverseRelationship(String relName, DataObject val) {
ObjRelationship rel = objectContext.getEntityResolver().getObjEntity(objectId.getEntityName()).getRelationship(relName);
ObjRelationship revRel = rel.getReverseRelationship();
if (revRel != null) {
if (revRel.isToMany()) {
val.addToManyTarget(revRel.getName(), this, false);
} else {
val.setToOneTarget(revRel.getName(), this, false);
}
}
}
use of org.apache.cayenne.map.ObjRelationship in project cayenne by apache.
the class AshwoodEntitySorter method sortObjectsForEntity.
@Override
public void sortObjectsForEntity(ObjEntity objEntity, List<?> objects, boolean deleteOrder) {
indexSorter();
List<Persistent> persistent = (List<Persistent>) objects;
DbEntity dbEntity = objEntity.getDbEntity();
// if no sorting is required
if (!isReflexive(dbEntity)) {
return;
}
int size = persistent.size();
if (size == 0) {
return;
}
EntityResolver resolver = persistent.get(0).getObjectContext().getEntityResolver();
ClassDescriptor descriptor = resolver.getClassDescriptor(objEntity.getName());
List<DbRelationship> reflexiveRels = reflexiveDbEntities.get(dbEntity);
String[] reflexiveRelNames = new String[reflexiveRels.size()];
for (int i = 0; i < reflexiveRelNames.length; i++) {
DbRelationship dbRel = reflexiveRels.get(i);
ObjRelationship objRel = (dbRel != null ? objEntity.getRelationshipForDbRelationship(dbRel) : null);
reflexiveRelNames[i] = (objRel != null ? objRel.getName() : null);
}
List<Persistent> sorted = new ArrayList<>(size);
Digraph<Persistent, Boolean> objectDependencyGraph = new MapDigraph<>();
Object[] masters = new Object[reflexiveRelNames.length];
for (int i = 0; i < size; i++) {
Persistent current = (Persistent) objects.get(i);
objectDependencyGraph.addVertex(current);
int actualMasterCount = 0;
for (int k = 0; k < reflexiveRelNames.length; k++) {
String reflexiveRelName = reflexiveRelNames[k];
if (reflexiveRelName == null) {
continue;
}
masters[k] = descriptor.getProperty(reflexiveRelName).readProperty(current);
if (masters[k] == null) {
masters[k] = findReflexiveMaster(current, objEntity.getRelationship(reflexiveRelName), current.getObjectId().getEntityName());
}
if (masters[k] != null) {
actualMasterCount++;
}
}
int mastersFound = 0;
for (int j = 0; j < size && mastersFound < actualMasterCount; j++) {
if (i == j) {
continue;
}
Persistent masterCandidate = persistent.get(j);
for (Object master : masters) {
if (masterCandidate == master) {
objectDependencyGraph.putArc(masterCandidate, current, Boolean.TRUE);
mastersFound++;
}
}
}
}
IndegreeTopologicalSort<Persistent> sorter = new IndegreeTopologicalSort<>(objectDependencyGraph);
while (sorter.hasNext()) {
Persistent o = sorter.next();
if (o == null) {
throw new CayenneRuntimeException("Sorting objects for %s failed. Cycles found.", objEntity.getClassName());
}
sorted.add(o);
}
// since API requires sorting within the same array,
// simply replace all objects with objects in the right order...
// may come up with something cleaner later
persistent.clear();
persistent.addAll(sorted);
if (deleteOrder) {
Collections.reverse(persistent);
}
}
Aggregations