String tableGroupName = sourceSqlTbl.getGroupName() + "." + mmd.getName();
JavaTypeMapping m = sourceSqlTbl.getTable().getMemberMapping(mmd);
if (m != null && m.includeInFetchStatement())
{
int relationType = mmd.getRelationType(clr);
RDBMSStoreManager storeMgr = stmt.getRDBMSManager();
RDBMSAdapter dba = (RDBMSAdapter) storeMgr.getDatastoreAdapter();
if (!dba.validToSelectMappingInStatement(stmt, m))
{
// Not valid to select this mapping for this statement so return
return;
}
MetaDataManager mmgr = storeMgr.getMetaDataManager();
StatementMappingIndex stmtMapping = new StatementMappingIndex(m);
if (m.getNumberOfDatastoreMappings() > 0)
{
// Select of fields with columns in source table(s)
// Adds inner/outer join to any required superclass/secondary tables
SQLTable sqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(stmt, sourceSqlTbl, m);
boolean selectFK = true;
if (selectSubobjects && (relationType == Relation.ONE_TO_ONE_UNI ||
(relationType == Relation.ONE_TO_ONE_BI && mmd.getMappedBy() == null)) &&
!mmd.isSerialized() && !mmd.isEmbedded())
{
// Related object with FK at this side
if (mmd.fetchFKOnly())
{
// Only want FK fetching, and not the fields of the object (so avoid the join)
}
else
{
// select fetch plan fields of this object
AbstractClassMetaData relatedCmd =
storeMgr.getMetaDataManager().getMetaDataForClass(mmd.getType(), clr);
if (relatedCmd != null)
{
// Find the table of the related class
DatastoreClass relatedTbl = storeMgr.getDatastoreClass(relatedCmd.getFullClassName(), clr);
if (relatedTbl == null)
{
// Class doesn't have its own table (subclass-table) so find where it persists
AbstractClassMetaData[] ownerParentCmds =
storeMgr.getClassesManagingTableForClass(relatedCmd, clr);
if (ownerParentCmds.length > 1)
{
throw new NucleusUserException("Relation (" + mmd.getFullFieldName() +
") with multiple related tables (using subclass-table). Not supported");
}
relatedTbl = storeMgr.getDatastoreClass(ownerParentCmds[0].getFullClassName(), clr);
}
String requiredGroupName = null;
if (sourceSqlTbl.getGroupName() != null)
{
// JPQL will have table groups defined already, named as per "alias.fieldName"
requiredGroupName = sourceSqlTbl.getGroupName() + "." + mmd.getName();
}
SQLTable relatedSqlTbl = stmt.getTable(relatedTbl, requiredGroupName);
if (relatedSqlTbl == null)
{
// Join the 1-1 relation
relatedSqlTbl = addJoinForOneToOneRelation(stmt,
m, sqlTbl, relatedTbl.getIdMapping(), relatedTbl, null, null, tableGroupName, null);
}
StatementClassMapping subMappingDefinition =
new StatementClassMapping(mmd.getClassName(), mmd.getName());
selectFetchPlanOfSourceClassInStatement(stmt, subMappingDefinition, fetchPlan,
relatedSqlTbl, relatedCmd, maxFetchPlanLimit-1);
if (mappingDefinition != null)
{
if (relatedCmd.getIdentityType() == IdentityType.APPLICATION)
{
int[] pkFields = relatedCmd.getPKMemberPositions();
int[] pkCols = new int[m.getNumberOfDatastoreMappings()];
int pkColNo = 0;
for (int i=0;i<pkFields.length;i++)
{
StatementMappingIndex pkIdx = subMappingDefinition.getMappingForMemberPosition(pkFields[i]);
int[] pkColNumbers = pkIdx.getColumnPositions();
for (int j=0;j<pkColNumbers.length;j++)
{
pkCols[pkColNo] = pkColNumbers[j];
pkColNo++;
}
}
selectFK = false;
stmtMapping.setColumnPositions(pkCols);
}
else if (relatedCmd.getIdentityType() == IdentityType.DATASTORE)
{
StatementMappingIndex pkIdx = subMappingDefinition.getMappingForMemberPosition(StatementClassMapping.MEMBER_DATASTORE_ID);
selectFK = false;
stmtMapping.setColumnPositions(pkIdx.getColumnPositions());
}
mappingDefinition.addMappingDefinitionForMember(mmd.getAbsoluteFieldNumber(),
subMappingDefinition);
}
}
else
{
// TODO 1-1 interface relation
}
}
}
if (selectFK)
{
int[] colNumbers = stmt.select(sqlTbl, m, null);
stmtMapping.setColumnPositions(colNumbers);
}
}
else
{
// Select of related objects with FK in other table
if (relationType == Relation.ONE_TO_ONE_BI && mmd.getMappedBy() != null)
{
// 1-1 bidirectional relation with FK in related table
AbstractMemberMetaData[] relatedMmds = mmd.getRelatedMemberMetaData(clr);
AbstractMemberMetaData relatedMmd = relatedMmds[0];
String[] clsNames = null;
if (mmd.getType().isInterface())
{
if (mmd.getFieldTypes() != null && mmd.getFieldTypes().length == 1)
{
// Use field-type since only one class specified
Class fldTypeCls = clr.classForName(mmd.getFieldTypes()[0]);
if (fldTypeCls.isInterface())
{
// User has specified an interface, so find its implementations
clsNames = mmgr.getClassesImplementingInterface(mmd.getFieldTypes()[0], clr);
}
else
{
// Use user-provided field-type
clsNames = new String[] {mmd.getFieldTypes()[0]};
}
}
if (clsNames == null)
{
clsNames = mmgr.getClassesImplementingInterface(mmd.getTypeName(), clr);
}
}
else
{
clsNames = new String[] { mmd.getTypeName() };
}
DatastoreClass relatedTbl = storeMgr.getDatastoreClass(clsNames[0], clr);
JavaTypeMapping relatedMapping = relatedTbl.getMemberMapping(relatedMmd);
JavaTypeMapping relatedDiscrimMapping = relatedTbl.getDiscriminatorMapping(true);
Object[] discrimValues = null;
JavaTypeMapping relatedTypeMapping = null;
AbstractClassMetaData relatedCmd = relatedMmd.getAbstractClassMetaData();
if (relatedDiscrimMapping != null &&
(relatedCmd.getSuperAbstractClassMetaData() != null || !relatedCmd.getFullClassName().equals(mmd.getTypeName())))
{
// Related table has a discriminator and the field can store other types
List discValueList = null;
for (int i=0;i<clsNames.length;i++)
{
List values = getDiscriminatorValuesForMember(clsNames[i],
relatedDiscrimMapping, storeMgr, clr);
if (discValueList == null)
{
discValueList = values;
}
else
{
discValueList.addAll(values);
}
}
discrimValues = discValueList.toArray(new Object[discValueList.size()]);
}
else if (relatedTbl != relatedMapping.getDatastoreContainer())
{
// The relation is to a base class table, and the type stored is a sub-class
relatedTypeMapping = relatedTbl.getIdMapping();
}
SQLTable relatedSqlTbl = null;
if (relatedTypeMapping == null)
{
// Join the 1-1 relation
relatedSqlTbl = addJoinForOneToOneRelation(stmt,
sourceSqlTbl.getTable().getIdMapping(), sourceSqlTbl,
relatedMapping, relatedTbl, null, discrimValues, tableGroupName, null);
// Select the id mapping in the related table
int[] colNumbers = stmt.select(relatedSqlTbl, relatedTbl.getIdMapping(), null);
stmtMapping.setColumnPositions(colNumbers);
}
else
{
DatastoreClass relationTbl = (DatastoreClass)relatedMapping.getDatastoreContainer();
if (relatedTbl != relatedMapping.getDatastoreContainer())
{
if (relatedMapping.isNullable())
{
// Nullable - left outer join from {sourceTable}.ID to {relatedBaseTable}.FK
// and inner join from {relatedBaseTable}.ID to {relatedTable}.ID
// (joins the relation and restricts to the right type)
relatedSqlTbl = stmt.leftOuterJoin(sourceSqlTbl,
sourceSqlTbl.getTable().getIdMapping(),
relatedMapping.getDatastoreContainer(), null, relatedMapping, null, tableGroupName);
relatedSqlTbl = stmt.innerJoin(relatedSqlTbl,
relatedMapping.getDatastoreContainer().getIdMapping(),
relatedTbl, null, relatedTbl.getIdMapping(), null, tableGroupName);
}
else
{
// Not nullable - inner join from {sourceTable}.ID to {relatedBaseTable}.FK
// and inner join from {relatedBaseTable}.ID to {relatedTable}.ID
// (joins the relation and restricts to the right type)
relatedSqlTbl = stmt.innerJoin(sourceSqlTbl,
sourceSqlTbl.getTable().getIdMapping(),
relatedMapping.getDatastoreContainer(), null, relatedMapping, null, tableGroupName);
relatedSqlTbl = stmt.innerJoin(relatedSqlTbl,
relatedMapping.getDatastoreContainer().getIdMapping(),
relatedTbl, null, relatedTbl.getIdMapping(), null, tableGroupName);
}
}
else
{
// Join the 1-1 relation
relatedSqlTbl = addJoinForOneToOneRelation(stmt,
sourceSqlTbl.getTable().getIdMapping(), sourceSqlTbl,
relatedMapping, relationTbl, null, null, tableGroupName, null);
}
// Select the id mapping in the subclass of the related table
// Note this adds an inner join from relatedTable to its subclass
relatedSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(stmt, relatedSqlTbl,
relatedTbl.getIdMapping());
int[] colNumbers = stmt.select(relatedSqlTbl, relatedTbl.getIdMapping(), null);
stmtMapping.setColumnPositions(colNumbers);
}
if (selectSubobjects && !mmd.isSerialized() && !mmd.isEmbedded())
{
// Select the fetch-plan fields of the related object
StatementClassMapping subMappingDefinition = new StatementClassMapping(mmd.getName());
selectFetchPlanOfSourceClassInStatement(stmt, subMappingDefinition, fetchPlan,
relatedSqlTbl, relatedMmd.getAbstractClassMetaData(), maxFetchPlanLimit-1);
if (mappingDefinition != null)
{
mappingDefinition.addMappingDefinitionForMember(mmd.getAbsoluteFieldNumber(),
subMappingDefinition);
}
}
}
else if (relationType == Relation.MANY_TO_ONE_BI)
{
AbstractMemberMetaData[] relatedMmds = mmd.getRelatedMemberMetaData(clr);
if (mmd.getJoinMetaData() != null || relatedMmds[0].getJoinMetaData() != null)
{
// N-1 bidirectional join table relation
// Add left outer join from {sourceTable}.ID to {joinTable}.ELEM_FK
DatastoreContainerObject joinTable =
storeMgr.getDatastoreContainerObject(relatedMmds[0]);
DatastoreElementContainer collTable = (DatastoreElementContainer)joinTable;
JavaTypeMapping selectMapping = collTable.getOwnerMapping();
SQLTable joinSqlTbl = null;
if (stmt.getPrimaryTable().getTable() != joinTable)
{
// Join to the join table
JavaTypeMapping referenceMapping = collTable.getElementMapping();
joinSqlTbl = stmt.leftOuterJoin(sourceSqlTbl,
sourceSqlTbl.getTable().getIdMapping(),
collTable, null, referenceMapping, null, tableGroupName);
}
else
{
// Main table of the statement is the join table so no need to join
joinSqlTbl = stmt.getPrimaryTable();
}
// Select the owner mapping of the join table
int[] colNumbers = stmt.select(joinSqlTbl, selectMapping, null);
stmtMapping.setColumnPositions(colNumbers);
}
// TODO Select fetch plan fields of this related object
}
else if (relationType == Relation.MANY_TO_ONE_UNI)
{
// Add left outer join from {sourceTable}.ID to {joinTable}.OWNER_FK
PersistableJoinTable joinTable = (PersistableJoinTable) storeMgr.getDatastoreContainerObject(mmd);
SQLTable joinSqlTbl = stmt.leftOuterJoin(sourceSqlTbl,
sourceSqlTbl.getTable().getIdMapping(),
joinTable, null, joinTable.getOwnerMapping(), null, tableGroupName);
int[] colNumbers = stmt.select(joinSqlTbl, joinTable.getRelatedMapping(), null);