in openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java [1356:1585]
private void mergeJoinColumn(MetaDataContext context, String prefix,
Column given, Object[][] joins, int idx, Table table, ClassMapping cls,
ClassMapping rel, ForeignKeyDefaults def, boolean inversable,
boolean adapt, boolean fill) {
// default to the primary key column name if this is a pk join
DBIdentifier name = given.getIdentifier();
if (DBIdentifier.isNull(name) && given.getFlag(Column.FLAG_PK_JOIN) && cls != null) {
Column[] pks = cls.getPrimaryKeyColumns();
if (pks.length == 1)
name = pks[0].getIdentifier();
}
// if we can't adapt, then the user must at least give a column name
if (DBIdentifier.isNull(name) && !adapt && !fill)
throw new MetaDataException(_loc.get(prefix + "-no-fkcol-name",
context));
// check to see if the column isn't in the expected table; it might
// be an inverse join or a join to a base class of the target type
Table local = table;
Table foreign = rel.getTable();
boolean fullName = false;
boolean inverse = false;
if (!DBIdentifier.isNull(name)) {
QualifiedDBIdentifier path = QualifiedDBIdentifier.getPath(name);
if (!DBIdentifier.isNull(path.getObjectTableName())) {
if (DBIdentifier.isEmpty(path.getObjectTableName()))
local = foreign;
else
local = findTable(context, path.getObjectTableName(),
local, foreign, null);
fullName = true;
name = path.getIdentifier().getUnqualifiedName();
// if inverse join, then swap local and foreign tables
if (local != table) {
foreign = table;
inverse = true;
}
}
}
boolean forceInverse = !fullName && _join == JOIN_INVERSE;
if (forceInverse) {
local = foreign;
foreign = table;
inverse = true;
}
// determine target
DBIdentifier targetName = given.getTargetIdentifier();
Object target = null;
Table ttable = null;
boolean constant = false;
boolean fullTarget = false;
if (DBIdentifier.isNull(targetName) && given.getTargetField() != null) {
ClassMapping tcls = (inverse) ? cls : rel;
String fieldName = given.getTargetField();
String[] names = Normalizer.splitName(fieldName);
fullTarget = names.length > 1;
if (names.length > 1 && StringUtil.isEmpty(names[0])) {
// allow use of '.' without prefix to mean "use expected local
// cls"; but if we already inversed no need to switch again
if (!inverse)
tcls = cls;
fieldName = names[1];
} else if (names.length > 1) {
// must be class + field name
tcls = findClassMapping(context, names[0], cls, rel);
fieldName = names[1];
}
if (tcls == null)
throw new MetaDataException(_loc.get(prefix
+ "-bad-fktargetcls", context, fieldName, name));
FieldMapping field = tcls.getFieldMapping(fieldName);
if (field == null)
throw new MetaDataException(_loc.get(prefix
+ "-bad-fktargetfield", new Object[]{ context, fieldName,
name, tcls }));
if (field.getColumns().length != 1)
throw new MetaDataException(_loc.get(prefix
+ "-fktargetfield-cols", context, fieldName, name));
ttable = (field.getJoinForeignKey() != null) ? field.getTable()
: field.getDefiningMapping().getTable();
targetName = field.getColumns()[0].getIdentifier();
} else if (!DBIdentifier.isNull(targetName)) {
String targetNameStr = targetName.getName();
if (targetNameStr.charAt(0) == '\'') {
constant = true;
target = targetNameStr.substring(1, targetNameStr.length() - 1);
} else if (targetNameStr.charAt(0) == '-'
|| targetNameStr.charAt(0) == '.'
|| Character.isDigit(targetNameStr.charAt(0))) {
constant = true;
try {
if (targetNameStr.indexOf('.') == -1)
target = new Integer(targetNameStr);
else
target = new Double(targetNameStr);
} catch (RuntimeException re) {
throw new MetaDataException(_loc.get(prefix
+ "-bad-fkconst", context, targetName, name));
}
} else if ("null".equalsIgnoreCase(targetNameStr))
constant = true;
else {
QualifiedDBIdentifier path = QualifiedDBIdentifier.getPath(targetName);
fullTarget = (!DBIdentifier.isNull(path.getObjectTableName()));
if (!DBIdentifier.isNull(path.getObjectTableName()) &&
DBIdentifier.isEmpty(path.getObjectTableName())) {
// allow use of '.' without prefix to mean "use expected
// local table", but ignore if we're already inversed
if (!inverse)
ttable = local;
targetName = path.getIdentifier().getUnqualifiedName();
} else if (!DBIdentifier.isNull(path.getObjectTableName())) {
ttable = findTable(context, path.getObjectTableName(), foreign, local, (inverse) ? cls : rel);
targetName = path.getIdentifier().getUnqualifiedName();
}
}
}
// use explicit target table if available
if (ttable == local && local != foreign) {
// swap, unless user gave incompatible table in column name
if (fullName)
throw new MetaDataException(_loc.get(prefix
+ "-bad-fktarget-inverse", new Object[]{ context, name,
foreign, ttable }));
local = foreign;
foreign = ttable;
} else if (ttable != null) {
// ttable might be a table of a base class of the target
foreign = ttable;
}
// check to see if we inversed; if this is a same-table join, then
// consider it an implicit inverse if the user includes the table name
// in the column name, but not in the column target, or if the user
// gives no column name but a full target name
inverse = inverse || local != table || (local == foreign
&& ((fullName && !fullTarget) || (DBIdentifier.isNull(name) && fullTarget)));
if (!inversable && !constant && inverse) {
if (local == foreign)
throw new MetaDataException(_loc.get(prefix
+ "-bad-fk-self-inverse", context, local));
throw new MetaDataException(_loc.get(prefix + "-bad-fk-inverse",
context, local, table));
}
if (DBIdentifier.isNull(name) && constant)
throw new MetaDataException(_loc.get(prefix
+ "-no-fkcol-name-adapt", context));
if (DBIdentifier.isNull(name) && DBIdentifier.isNull(targetName)) {
// if no name or target is provided and there's more than one likely
// join possibility, too ambiguous
PrimaryKey pk = foreign.getPrimaryKey();
if (joins.length != 1 || pk == null || pk.getColumns().length != 1)
throw new MetaDataException(_loc.get(prefix
+ "-no-fkcol-name-adapt", context));
// assume target is pk column
targetName = pk.getColumns()[0].getIdentifier();
} else if (!DBIdentifier.isNull(name) && DBIdentifier.isNull(targetName)) {
// if one primary key column use it for target; if multiple joins
// look for a foreign column with same name as local column
PrimaryKey pk = foreign.getPrimaryKey();
if (joins.length == 1 && pk != null && pk.getColumns().length == 1) {
targetName = pk.getColumns()[0].getIdentifier();
}
else if (foreign.getColumn(name) != null) {
targetName = name;
}
else {
throw new MetaDataException(_loc.get(prefix
+ "-no-fkcol-target-adapt", context, name));
}
}
// find the target column, and create template for local column based
// on it
Column tmplate = new Column();
tmplate.setIdentifier(name);
if (!constant) {
Column tcol = foreign.getColumn(targetName, false);
if (tcol == null) {
String schemaCase = rel.getMappingRepository().getDBDictionary().schemaCase;
if (DBDictionary.SCHEMA_CASE_LOWER.equals(schemaCase)) {
tcol = foreign.getColumn(DBIdentifier.toLower(targetName, true), false);
} else if (DBDictionary.SCHEMA_CASE_UPPER.equals(schemaCase)) {
tcol = foreign.getColumn(DBIdentifier.toUpper(targetName, true), false);
}
}
if (tcol == null) {
// give up
throw new MetaDataException(_loc.get(prefix + "-bad-fktarget",
new Object[]{ context, targetName, name, foreign }));
}
if (DBIdentifier.isNull(name))
tmplate.setIdentifier(tcol.getIdentifier());
tmplate.setJavaType(tcol.getJavaType());
tmplate.setType(tcol.getType());
tmplate.setTypeName(tcol.getTypeName());
tmplate.setSize(tcol.getSize());
tmplate.setDecimalDigits(tcol.getDecimalDigits());
target = tcol;
} else if (target instanceof String)
tmplate.setJavaType(JavaTypes.STRING);
else if (target instanceof Integer)
tmplate.setJavaType(JavaTypes.INT);
else if (target instanceof Double)
tmplate.setJavaType(JavaTypes.DOUBLE);
// populate template, but let user-given name override default name
if (def != null)
def.populate(local, foreign, tmplate, target, inverse, idx,
joins.length);
if (!DBIdentifier.isNull(name))
tmplate.setIdentifier(name);
// create or merge local column
Column col = mergeColumn(context, prefix, tmplate, true, given, local,
adapt, fill);
joins[idx][0] = col;
joins[idx][1] = target;
if (inverse)
joins[idx][2] = Boolean.TRUE;
}