Created
January 20, 2017 14:23
-
-
Save andrus/29616d6f20fc5094a4eb77114d751db0 to your computer and use it in GitHub Desktop.
A builder of Cayenne relationships that allows to map them on the fly in a running app.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Relationships { | |
private Class<?> sourceType; | |
private Class<?> targetType; | |
private String name; | |
private String reverseName; | |
private int cardinalityBits; | |
private int toDepPKBits; | |
private Collection<String[]> forwardJoins; | |
public static Relationships oneToOne(String name) { | |
return new Relationships().named(name).cardinality(false, false); | |
} | |
public static Relationships oneToMany(String name) { | |
return new Relationships().named(name).cardinality(true, false); | |
} | |
public static Relationships manyToOne(String name) { | |
return new Relationships().named(name).cardinality(false, true); | |
} | |
private Relationships() { | |
this.forwardJoins = new HashSet<>(); | |
} | |
public <S extends Persistent, T extends Persistent> Relationships between(Class<S> sourceType, Class<T> targetType) { | |
this.sourceType = Objects.requireNonNull(sourceType); | |
this.targetType = Objects.requireNonNull(targetType); | |
return this; | |
} | |
protected Relationships named(String name) { | |
this.name = name; | |
return this; | |
} | |
public Relationships createReverse(String name) { | |
this.reverseName = name; | |
return this; | |
} | |
protected Relationships cardinality(boolean forwardToMany, boolean reverseToMany) { | |
if (forwardToMany && reverseToMany) { | |
throw new IllegalArgumentException("Both sides of the relationship can not be to-many"); | |
} | |
int bits = 0; | |
if (forwardToMany) { | |
bits = bits | 1; | |
} | |
if (reverseToMany) { | |
bits = bits | 2; | |
} | |
this.cardinalityBits = bits; | |
return this; | |
} | |
public Relationships toDepPK() { | |
return toDepPK(true, false); | |
} | |
public Relationships fromDepPK() { | |
return toDepPK(false, true); | |
} | |
protected Relationships toDepPK(boolean forwardToDepPK, boolean reverseToDepPK) { | |
if (forwardToDepPK && reverseToDepPK) { | |
throw new IllegalArgumentException("Both sides of the relationship can not be to-dep PK"); | |
} | |
int bits = 0; | |
if (forwardToDepPK) { | |
bits = bits | 1; | |
} | |
if (reverseToDepPK) { | |
bits = bits | 2; | |
} | |
this.toDepPKBits = bits; | |
return this; | |
} | |
public Relationships joined(String srcColumn, String targetColumn) { | |
forwardJoins.add(new String[]{srcColumn, targetColumn}); | |
return this; | |
} | |
public void exec(EntityResolver resolver) { | |
validate(); | |
ObjEntity srcOE = resolver.getObjEntity(sourceType); | |
ObjEntity targetOE = resolver.getObjEntity(targetType); | |
Objects.requireNonNull(srcOE); | |
Objects.requireNonNull(targetOE); | |
DbEntity srcDE = srcOE.getDbEntity(); | |
DbEntity targetDE = targetOE.getDbEntity(); | |
validateJoins(srcDE, targetDE); | |
// forward relationship | |
DbRelationship forwardDBR = new DbRelationship(name); | |
forwardDBR.setToMany((cardinalityBits & 1) > 0); | |
forwardDBR.setToDependentPK((toDepPKBits & 1) > 0); | |
forwardDBR.setTargetEntityName(targetDE.getName()); | |
forwardJoins.forEach(j -> { | |
forwardDBR.addJoin(new DbJoin(forwardDBR, j[0], j[1])); | |
}); | |
srcDE.addRelationship(forwardDBR); | |
ObjRelationship forwardOR = new ObjRelationship(forwardDBR.getName()); | |
forwardOR.setTargetEntityName(targetOE.getName()); | |
srcOE.addRelationship(forwardOR); | |
forwardOR.setDbRelationshipPath(forwardDBR.getName()); | |
// reverse relationship .. create DB flavor reverse even if no object reverse is specified | |
// TODO: use Cayenne unique name generator | |
DbRelationship reverseDBR = new DbRelationship(reverseName != null ? reverseName : "auto_" + srcDE.getName()); | |
reverseDBR.setToMany((cardinalityBits & 2) > 0); | |
reverseDBR.setToDependentPK((toDepPKBits & 2) > 0); | |
reverseDBR.setTargetEntityName(srcDE.getName()); | |
forwardJoins.forEach(j -> { | |
reverseDBR.addJoin(new DbJoin(reverseDBR, j[1], j[0])); | |
}); | |
targetDE.addRelationship(reverseDBR); | |
if (reverseName != null) { | |
ObjRelationship reverseOR = new ObjRelationship(reverseDBR.getName()); | |
reverseOR.setTargetEntityName(srcOE.getName()); | |
targetOE.addRelationship(reverseOR); | |
reverseOR.setDbRelationshipPath(reverseDBR.getName()); | |
} | |
} | |
protected void validate() { | |
Objects.requireNonNull(sourceType); | |
Objects.requireNonNull(targetType); | |
if (forwardJoins.isEmpty()) { | |
throw new IllegalStateException("No joins specified"); | |
} | |
} | |
protected void validateJoins(DbEntity srcDE, DbEntity targetDE) { | |
forwardJoins.forEach(j -> { | |
if (srcDE.getAttribute(j[0]) == null) { | |
throw new IllegalStateException("Invalid source join column: " + j[0]); | |
} | |
if (targetDE.getAttribute(j[1]) == null) { | |
throw new IllegalStateException("Invalid target join column: " + j[1]); | |
} | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment