use of org.datanucleus.store.rdbms.mapping.java.ReferenceMapping in project datanucleus-rdbms by datanucleus.
the class JoinSetStore method getIteratorStatement.
/**
* Method to return the SQLStatement and mapping for an iterator for this backing store.
* Create a statement of the form
* <pre>
* SELECT ELEM_COLS
* FROM JOIN_TBL
* [JOIN ELEM_TBL ON ELEM_TBL.ID = JOIN_TBL.ELEM_ID]
* [WHERE]
* [JOIN_TBL.OWNER_ID = {value}] [AND]
* [JOIN_TBL.DISCRIM = {discrimValue}]
* [ORDER BY {orderClause}]
* </pre>
* This is public to provide access for BulkFetchXXXHandler class(es).
* @param ec ExecutionContext
* @param fp FetchPlan to use in determining which fields of element to select
* @param addRestrictionOnOwner Whether to restrict to a particular owner (otherwise functions as bulk fetch for many owners).
* @return The SQLStatement and its associated StatementClassMapping
*/
public ElementIteratorStatement getIteratorStatement(ExecutionContext ec, FetchPlan fp, boolean addRestrictionOnOwner) {
SelectStatement sqlStmt = null;
SQLExpressionFactory exprFactory = storeMgr.getSQLExpressionFactory();
StatementClassMapping elementClsMapping = null;
if (elementsAreEmbedded || elementsAreSerialised) {
// Element = embedded, serialised (maybe Non-PC)
// Just select the join table since we're going to return the embedded/serialised columns from it
sqlStmt = new SelectStatement(storeMgr, containerTable, null, null);
sqlStmt.setClassLoaderResolver(clr);
// Select the element column - first select is assumed by SetStoreIterator
sqlStmt.select(sqlStmt.getPrimaryTable(), elementMapping, null);
// TODO If embedded element and it includes 1-1/N-1 in FetchPlan then select its fields also
} else if (elementMapping instanceof ReferenceMapping) {
// Element = Reference type (interface/Object)
// Just select the join table since we're going to return the implementation id columns only
sqlStmt = new SelectStatement(storeMgr, containerTable, null, null);
sqlStmt.setClassLoaderResolver(clr);
// Select the reference column(s) - first select is assumed by SetStoreIterator
sqlStmt.select(sqlStmt.getPrimaryTable(), elementMapping, null);
} else {
// Element = PC
// Join to the element table(s)
elementClsMapping = new StatementClassMapping();
for (int i = 0; i < elementInfo.length; i++) {
final int elementNo = i;
final Class elementCls = clr.classForName(elementInfo[elementNo].getClassName());
SelectStatement elementStmt = null;
if (elementInfo[elementNo].getDiscriminatorStrategy() != null && elementInfo[elementNo].getDiscriminatorStrategy() != DiscriminatorStrategy.NONE) {
// The element uses a discriminator so just use that in the SELECT
String elementType = ownerMemberMetaData.getCollection().getElementType();
if (ClassUtils.isReferenceType(clr.classForName(elementType))) {
String[] clsNames = storeMgr.getNucleusContext().getMetaDataManager().getClassesImplementingInterface(elementType, clr);
Class[] cls = new Class[clsNames.length];
for (int j = 0; j < clsNames.length; j++) {
cls[j] = clr.classForName(clsNames[j]);
}
SelectStatementGenerator stmtGen = new DiscriminatorStatementGenerator(storeMgr, clr, cls, true, null, null, containerTable, null, elementMapping);
if (allowNulls) {
stmtGen.setOption(SelectStatementGenerator.OPTION_ALLOW_NULLS);
}
elementStmt = stmtGen.getStatement(ec);
} else {
SelectStatementGenerator stmtGen = new DiscriminatorStatementGenerator(storeMgr, clr, elementCls, true, null, null, containerTable, null, elementMapping);
if (allowNulls) {
stmtGen.setOption(SelectStatementGenerator.OPTION_ALLOW_NULLS);
}
elementStmt = stmtGen.getStatement(ec);
}
iterateUsingDiscriminator = true;
} else {
// No discriminator, but subclasses so use UNIONs
SelectStatementGenerator stmtGen = new UnionStatementGenerator(storeMgr, clr, elementCls, true, null, null, containerTable, null, elementMapping);
stmtGen.setOption(SelectStatementGenerator.OPTION_SELECT_DN_TYPE);
elementClsMapping.setNucleusTypeColumnName(UnionStatementGenerator.DN_TYPE_COLUMN);
elementStmt = stmtGen.getStatement(ec);
}
// TODO What if the first elementInfo has fields selected in one order and then the second in a different order?
if (sqlStmt == null) {
sqlStmt = elementStmt;
// Select the required fields
SQLTable elementSqlTbl = sqlStmt.getTable(elementInfo[i].getDatastoreClass(), sqlStmt.getPrimaryTable().getGroupName());
SQLStatementHelper.selectFetchPlanOfSourceClassInStatement(sqlStmt, elementClsMapping, fp, elementSqlTbl, elementCmd, fp.getMaxFetchDepth());
} else {
// Select the required fields
SQLTable elementSqlTbl = elementStmt.getTable(elementInfo[i].getDatastoreClass(), elementStmt.getPrimaryTable().getGroupName());
SQLStatementHelper.selectFetchPlanOfSourceClassInStatement(elementStmt, elementClsMapping, fp, elementSqlTbl, elementCmd, fp.getMaxFetchDepth());
sqlStmt.union(elementStmt);
}
}
}
if (sqlStmt == null) {
throw new NucleusException("Error in generation of SQL statement for iterator over (Join) set. Statement is null");
}
if (addRestrictionOnOwner) {
// Apply condition on join-table owner field to filter by owner
SQLTable ownerSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), ownerMapping);
SQLExpression ownerExpr = exprFactory.newExpression(sqlStmt, ownerSqlTbl, ownerMapping);
SQLExpression ownerVal = exprFactory.newLiteralParameter(sqlStmt, ownerMapping, null, "OWNER");
sqlStmt.whereAnd(ownerExpr.eq(ownerVal), true);
}
if (relationDiscriminatorMapping != null) {
// Apply condition on distinguisher field to filter by distinguisher (when present)
SQLTable distSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), relationDiscriminatorMapping);
SQLExpression distExpr = exprFactory.newExpression(sqlStmt, distSqlTbl, relationDiscriminatorMapping);
SQLExpression distVal = exprFactory.newLiteral(sqlStmt, relationDiscriminatorMapping, relationDiscriminatorValue);
sqlStmt.whereAnd(distExpr.eq(distVal), true);
}
if (orderMapping != null) {
// TODO If we have multiple roots then cannot allow this
// Order by the ordering column, when present
SQLTable orderSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), orderMapping);
SQLExpression[] orderExprs = new SQLExpression[orderMapping.getNumberOfColumnMappings()];
boolean[] descendingOrder = new boolean[orderMapping.getNumberOfColumnMappings()];
orderExprs[0] = exprFactory.newExpression(sqlStmt, orderSqlTbl, orderMapping);
sqlStmt.setOrdering(orderExprs, descendingOrder);
}
return new ElementIteratorStatement(this, sqlStmt, elementClsMapping);
}
use of org.datanucleus.store.rdbms.mapping.java.ReferenceMapping in project datanucleus-rdbms by datanucleus.
the class SQLStatementHelper method selectMemberOfSourceInStatement.
/**
* Method to select the specified member (field/property) of the source table in the passed SQL
* statement. This populates the mappingDefinition with the column details for this member.
* @param stmt The SQL statement
* @param mappingDefinition Mapping definition for the results (will be populated by any
* selected mappings if provided as input)
* @param fetchPlan FetchPlan
* @param sourceSqlTbl Table that has the member (or a super-table/secondary-table of this table)
* @param mmd Meta-data for the field/property in the source that we are selecting
* @param clr ClassLoader resolver
* @param maxFetchPlanLimit Max fetch depth from this point to select (0 implies no other objects)
* @param inputJoinType Optional join type to use for subobjects (otherwise decide join type internally)
*/
public static void selectMemberOfSourceInStatement(SelectStatement stmt, StatementClassMapping mappingDefinition, FetchPlan fetchPlan, SQLTable sourceSqlTbl, AbstractMemberMetaData mmd, ClassLoaderResolver clr, int maxFetchPlanLimit, JoinType inputJoinType) {
boolean selectSubobjects = false;
if (maxFetchPlanLimit > 0) {
selectSubobjects = true;
}
if (mmd.fetchFKOnly()) {
// Equivalent to recursion-depth=0
selectSubobjects = false;
}
if (fetchPlan != null) {
FetchPlanForClass fpClass = fetchPlan.getFetchPlanForClass(mmd.getAbstractClassMetaData());
if (RelationType.isRelationSingleValued(mmd.getRelationType(clr)) && fpClass.getRecursionDepthForMember(mmd.getAbsoluteFieldNumber()) == 0) {
// User has marked this member as recursion-depth=0 meaning pull in just the FK and dont join to the sub-object
selectSubobjects = false;
}
}
// Set table-group name for any related object we join to (naming based on member name)
String tableGroupName = sourceSqlTbl.getGroupName() + "." + mmd.getName();
JavaTypeMapping m = sourceSqlTbl.getTable().getMemberMapping(mmd);
if (m != null && m.includeInFetchStatement()) {
RelationType relationType = mmd.getRelationType(clr);
RDBMSStoreManager storeMgr = stmt.getRDBMSManager();
DatastoreAdapter dba = 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.getNumberOfColumnMappings() > 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 == RelationType.ONE_TO_ONE_UNI || (relationType == RelationType.ONE_TO_ONE_BI && mmd.getMappedBy() == null)) && !mmd.isSerialized() && !mmd.isEmbedded()) {
// Related object with FK at this side
selectFK = selectFetchPlanFieldsOfFKRelatedObject(stmt, mappingDefinition, fetchPlan, sourceSqlTbl, mmd, clr, maxFetchPlanLimit, m, tableGroupName, stmtMapping, sqlTbl, inputJoinType);
} else if (selectSubobjects && (!mmd.isEmbedded() && !mmd.isSerialized()) && relationType == RelationType.MANY_TO_ONE_BI) {
AbstractMemberMetaData[] relatedMmds = mmd.getRelatedMemberMetaData(clr);
if (mmd.getJoinMetaData() != null || relatedMmds[0].getJoinMetaData() != null) {
// N-1 bidirectional join table relation
// TODO Add left outer join from {sourceTable}.ID to {joinTable}.ELEM_FK
Table joinTable = storeMgr.getTable(relatedMmds[0]);
ElementContainerTable collTable = (ElementContainerTable) joinTable;
JavaTypeMapping selectMapping = collTable.getOwnerMapping();
SQLTable joinSqlTbl = null;
if (stmt.getPrimaryTable().getTable() != joinTable) {
// Join to the join table
JavaTypeMapping referenceMapping = collTable.getElementMapping();
joinSqlTbl = stmt.join(JoinType.LEFT_OUTER_JOIN, sourceSqlTbl, sourceSqlTbl.getTable().getIdMapping(), collTable, null, referenceMapping, null, tableGroupName, true);
} 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 Join to 1 side from join table?
} else {
// N-1 bidirectional FK relation
// Related object with FK at this side, so join/select related object as required
selectFK = selectFetchPlanFieldsOfFKRelatedObject(stmt, mappingDefinition, fetchPlan, sourceSqlTbl, mmd, clr, maxFetchPlanLimit, m, tableGroupName, stmtMapping, sqlTbl, inputJoinType);
}
}
if (selectFK) {
int[] colNumbers = stmt.select(sqlTbl, m, null);
stmtMapping.setColumnPositions(colNumbers);
}
} else {
// Select of related objects with FK in other table
if (relationType == RelationType.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 {
String typeName = mmd.isSingleCollection() ? mmd.getCollection().getElementType() : mmd.getTypeName();
clsNames = new String[] { typeName };
}
DatastoreClass relatedTbl = storeMgr.getDatastoreClass(clsNames[0], clr);
JavaTypeMapping relatedMapping = relatedTbl.getMemberMapping(relatedMmd);
JavaTypeMapping relatedDiscrimMapping = relatedTbl.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, 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 (String clsName : clsNames) {
List values = getDiscriminatorValuesForMember(clsName, relatedDiscrimMapping, storeMgr, clr);
if (discValueList == null) {
discValueList = values;
} else {
discValueList.addAll(values);
}
}
if (discValueList != null) {
discrimValues = discValueList.toArray(new Object[discValueList.size()]);
}
} else if (relatedTbl != relatedMapping.getTable()) {
// 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, if not already joined
relatedSqlTbl = stmt.getTable(relatedTbl, tableGroupName);
if (relatedSqlTbl == null) {
JoinType joinType = getJoinTypeForOneToOneRelationJoin(sourceSqlTbl.getTable().getIdMapping(), sourceSqlTbl, inputJoinType);
if (joinType == JoinType.LEFT_OUTER_JOIN || joinType == JoinType.RIGHT_OUTER_JOIN) {
inputJoinType = joinType;
}
relatedSqlTbl = addJoinForOneToOneRelation(stmt, sourceSqlTbl.getTable().getIdMapping(), sourceSqlTbl, relatedMapping, relatedTbl, null, discrimValues, tableGroupName, joinType);
}
// Select the id mapping in the related table
int[] colNumbers = stmt.select(relatedSqlTbl, relatedTbl.getIdMapping(), null);
stmtMapping.setColumnPositions(colNumbers);
} else {
DatastoreClass relationTbl = (DatastoreClass) relatedMapping.getTable();
if (relatedTbl != relatedMapping.getTable()) {
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.join(JoinType.LEFT_OUTER_JOIN, sourceSqlTbl, sourceSqlTbl.getTable().getIdMapping(), relatedMapping.getTable(), null, relatedMapping, null, tableGroupName, true);
relatedSqlTbl = stmt.join(JoinType.INNER_JOIN, relatedSqlTbl, relatedMapping.getTable().getIdMapping(), relatedTbl, null, relatedTbl.getIdMapping(), null, tableGroupName, true);
} 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.join(JoinType.INNER_JOIN, sourceSqlTbl, sourceSqlTbl.getTable().getIdMapping(), relatedMapping.getTable(), null, relatedMapping, null, tableGroupName, true);
relatedSqlTbl = stmt.join(JoinType.INNER_JOIN, relatedSqlTbl, relatedMapping.getTable().getIdMapping(), relatedTbl, null, relatedTbl.getIdMapping(), null, tableGroupName, true);
}
} else {
// Join the 1-1 relation, if not already joined
relatedSqlTbl = stmt.getTable(relatedTbl, tableGroupName);
if (relatedSqlTbl == null) {
JoinType joinType = getJoinTypeForOneToOneRelationJoin(sourceSqlTbl.getTable().getIdMapping(), sourceSqlTbl, inputJoinType);
if (joinType == JoinType.LEFT_OUTER_JOIN || joinType == JoinType.RIGHT_OUTER_JOIN) {
inputJoinType = joinType;
}
relatedSqlTbl = addJoinForOneToOneRelation(stmt, sourceSqlTbl.getTable().getIdMapping(), sourceSqlTbl, relatedMapping, relationTbl, null, null, tableGroupName, joinType);
}
}
// 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(null, mmd.getName());
selectFetchPlanOfSourceClassInStatement(stmt, subMappingDefinition, fetchPlan, relatedSqlTbl, relatedMmd.getAbstractClassMetaData(), maxFetchPlanLimit - 1, inputJoinType);
if (mappingDefinition != null) {
mappingDefinition.addMappingDefinitionForMember(mmd.getAbsoluteFieldNumber(), subMappingDefinition);
}
}
} else if (relationType == RelationType.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
Table joinTable = storeMgr.getTable(relatedMmds[0]);
ElementContainerTable collTable = (ElementContainerTable) joinTable;
JavaTypeMapping selectMapping = collTable.getOwnerMapping();
SQLTable joinSqlTbl = null;
if (stmt.getPrimaryTable().getTable() != joinTable) {
// Join to the join table
JavaTypeMapping referenceMapping = collTable.getElementMapping();
if (referenceMapping instanceof ReferenceMapping) {
// Join table has a reference mapping pointing to our table, so get the submapping for the implementation
ReferenceMapping refMap = (ReferenceMapping) referenceMapping;
Class implType = clr.classForName(mmd.getClassName(true));
referenceMapping = refMap.getJavaTypeMappingForType(implType);
}
joinSqlTbl = stmt.join(JoinType.LEFT_OUTER_JOIN, sourceSqlTbl, sourceSqlTbl.getTable().getIdMapping(), collTable, null, referenceMapping, null, tableGroupName + "_JOIN", true);
} 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 == RelationType.MANY_TO_ONE_UNI) {
// Add left outer join from {sourceTable}.ID to {joinTable}.OWNER_FK
PersistableJoinTable joinTable = (PersistableJoinTable) storeMgr.getTable(mmd);
SQLTable joinSqlTbl = stmt.join(JoinType.LEFT_OUTER_JOIN, sourceSqlTbl, sourceSqlTbl.getTable().getIdMapping(), joinTable, null, joinTable.getOwnerMapping(), null, tableGroupName + "_JOIN", true);
int[] colNumbers = stmt.select(joinSqlTbl, joinTable.getRelatedMapping(), null);
stmtMapping.setColumnPositions(colNumbers);
// TODO Select fetch plan fields of this related object
}
}
if (mappingDefinition != null) {
mappingDefinition.addMappingForMember(mmd.getAbsoluteFieldNumber(), stmtMapping);
}
}
}
use of org.datanucleus.store.rdbms.mapping.java.ReferenceMapping in project datanucleus-rdbms by datanucleus.
the class SQLStatementHelper method selectFetchPlanFieldsOfFKRelatedObject.
/**
* Convenience method to join to and select all required FP fields of a related object where linked via an FK at this side.
* Note that the FetchPlan can have null passed in here (27/04/2021) so cater for it.
* @return Whether the caller should select the FK themselves (i.e we haven't selected anything)
*/
private static boolean selectFetchPlanFieldsOfFKRelatedObject(SelectStatement stmt, StatementClassMapping mappingDefinition, FetchPlan fetchPlan, SQLTable sourceSqlTbl, AbstractMemberMetaData mmd, ClassLoaderResolver clr, int maxFetchPlanLimit, JavaTypeMapping m, String tableGroupName, StatementMappingIndex stmtMapping, SQLTable sqlTbl, JoinType inputJoinType) {
boolean selectFK = true;
if (mmd.fetchFKOnly()) {
// Only want FK fetching, and not the fields of the object (so avoid the join)
} else {
RDBMSStoreManager storeMgr = stmt.getRDBMSManager();
Class type = mmd.isSingleCollection() ? clr.classForName(mmd.getCollection().getElementType()) : mmd.getType();
if (m instanceof ReferenceMapping) {
ReferenceMapping refMapping = (ReferenceMapping) m;
if (refMapping.getMappingStrategy() == ReferenceMapping.PER_IMPLEMENTATION_MAPPING && refMapping.getJavaTypeMapping().length == 1) {
JavaTypeMapping[] subMappings = refMapping.getJavaTypeMapping();
if (subMappings != null && subMappings.length == 1) {
// Special case of reference mapping with single FK implementation
type = clr.classForName(refMapping.getJavaTypeMapping()[0].getType());
}
}
}
// select fetch plan fields of this object
AbstractClassMetaData relatedCmd = storeMgr.getMetaDataManager().getMetaDataForClass(type, clr);
if (relatedCmd != null) {
if (relatedCmd.isEmbeddedOnly()) {
return true;
}
if (relatedCmd.getBaseAbstractClassMetaData().getInheritanceMetaData().getStrategy() == InheritanceStrategy.COMPLETE_TABLE) {
// Related object uses complete-table
Collection<String> relSubclassNames = storeMgr.getSubClassesForClass(relatedCmd.getFullClassName(), true, clr);
if (relatedCmd.isMappedSuperclass() && relSubclassNames.size() > 1) {
// Multiple possible related types and we don't have the FK so omit
return true;
} else if (!relatedCmd.isMappedSuperclass() && relSubclassNames.size() > 0) {
// Multiple possible related types and we don't have the FK so omit
return true;
}
// TODO Maybe do a LEFT OUTER JOIN to each possible?
}
// Check if we are only fetching the PK field(s), in which case we can avoid any join
if (fetchPlan != null) {
FetchPlanForClass relatedFP = fetchPlan.getFetchPlanForClass(relatedCmd);
int[] fpFieldNums = relatedFP.getMemberNumbers();
if (relatedCmd.getIdentityType() == IdentityType.APPLICATION) {
int[] pkFieldNums = relatedCmd.getPKMemberPositions();
if (fpFieldNums != null && pkFieldNums != null && fpFieldNums.length == pkFieldNums.length) {
boolean equal = true;
for (int i = 0; i < fpFieldNums.length; i++) {
if (fpFieldNums[i] != pkFieldNums[i]) {
equal = false;
break;
}
}
if (equal) {
// Solely fetching the PK fields, so no need to join (just like fetchFkOnly case above)
return true;
}
}
} else if (relatedCmd.getIdentityType() == IdentityType.DATASTORE) {
if (fpFieldNums == null || fpFieldNums.length == 0) {
return true;
}
}
}
// 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) {
// TODO Fix this
NucleusLogger.QUERY.warn("Relation (" + mmd.getFullFieldName() + ") with multiple related tables (using subclass-table). Not supported so selecting FK of related object only");
return true;
}
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
JoinType joinType = getJoinTypeForOneToOneRelationJoin(m, sqlTbl, inputJoinType);
if (joinType == JoinType.LEFT_OUTER_JOIN || joinType == JoinType.RIGHT_OUTER_JOIN) {
inputJoinType = joinType;
}
relatedSqlTbl = addJoinForOneToOneRelation(stmt, m, sqlTbl, relatedTbl.getIdMapping(), relatedTbl, null, null, tableGroupName, joinType);
}
StatementClassMapping subMappingDefinition = new StatementClassMapping(mmd.getClassName(), mmd.getName());
selectFetchPlanOfSourceClassInStatement(stmt, subMappingDefinition, fetchPlan, relatedSqlTbl, relatedCmd, maxFetchPlanLimit - 1, inputJoinType);
if (mappingDefinition != null) {
if (relatedCmd.getIdentityType() == IdentityType.APPLICATION) {
int[] pkFields = relatedCmd.getPKMemberPositions();
int[] pkCols = new int[m.getNumberOfColumnMappings()];
int pkColNo = 0;
for (int pkField : pkFields) {
StatementMappingIndex pkIdx = subMappingDefinition.getMappingForMemberPosition(pkField);
int[] pkColNumbers = pkIdx.getColumnPositions();
for (int pkColNumber : pkColNumbers) {
pkCols[pkColNo] = pkColNumber;
pkColNo++;
}
}
selectFK = false;
stmtMapping.setColumnPositions(pkCols);
} else if (relatedCmd.getIdentityType() == IdentityType.DATASTORE) {
StatementMappingIndex pkIdx = subMappingDefinition.getMappingForMemberPosition(SurrogateColumnType.DATASTORE_ID.getFieldNumber());
selectFK = false;
stmtMapping.setColumnPositions(pkIdx.getColumnPositions());
}
mappingDefinition.addMappingDefinitionForMember(mmd.getAbsoluteFieldNumber(), subMappingDefinition);
}
} else {
// TODO 1-1 interface relation
}
}
return selectFK;
}
use of org.datanucleus.store.rdbms.mapping.java.ReferenceMapping in project datanucleus-rdbms by datanucleus.
the class ClassTable method addExpectedForeignKeysForEmbeddedPCField.
/**
* Convenience method to add the expected FKs for an embedded PC field.
* @param foreignKeys The list of FKs to add the FKs to
* @param autoMode Whether operating in "auto-mode" where DataNucleus can create its own FKs
* @param clr ClassLoader resolver
* @param embeddedMapping The embedded PC mapping
*/
private void addExpectedForeignKeysForEmbeddedPCField(List foreignKeys, boolean autoMode, ClassLoaderResolver clr, EmbeddedPCMapping embeddedMapping) {
for (int i = 0; i < embeddedMapping.getNumberOfJavaTypeMappings(); i++) {
JavaTypeMapping embFieldMapping = embeddedMapping.getJavaTypeMapping(i);
if (embFieldMapping instanceof EmbeddedPCMapping) {
// Nested embedded PC so add the FKs for that
addExpectedForeignKeysForEmbeddedPCField(foreignKeys, autoMode, clr, (EmbeddedPCMapping) embFieldMapping);
} else {
AbstractMemberMetaData embFmd = embFieldMapping.getMemberMetaData();
if (ClassUtils.isReferenceType(embFmd.getType()) && embFieldMapping instanceof ReferenceMapping) {
// Field is a reference type, so add a FK to the table of the PC for each PC implementation
Collection fks = TableUtils.getForeignKeysForReferenceField(embFieldMapping, embFmd, autoMode, storeMgr, clr);
foreignKeys.addAll(fks);
} else if (storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForClass(embFmd.getType(), clr) != null && embFieldMapping.getNumberOfColumnMappings() > 0 && embFieldMapping instanceof PersistableMapping) {
// Field is for a PC class with the FK at this side, so add a FK to the table of this PC
ForeignKey fk = TableUtils.getForeignKeyForPCField(embFieldMapping, embFmd, autoMode, storeMgr, clr);
if (fk != null) {
foreignKeys.add(fk);
}
}
}
}
}
use of org.datanucleus.store.rdbms.mapping.java.ReferenceMapping in project datanucleus-rdbms by datanucleus.
the class ClassTable method getExpectedForeignKeys.
/**
* Accessor for the expected foreign keys for this table.
* @param clr The ClassLoaderResolver
* @return The expected foreign keys.
*/
@Override
public List<ForeignKey> getExpectedForeignKeys(ClassLoaderResolver clr) {
assertIsInitialized();
// Auto mode allows us to decide which FKs are needed as well as using what is in the users MetaData.
boolean autoMode = false;
if (storeMgr.getStringProperty(RDBMSPropertyNames.PROPERTY_RDBMS_CONSTRAINT_CREATE_MODE).equals("DataNucleus")) {
autoMode = true;
}
ArrayList<ForeignKey> foreignKeys = new ArrayList<>();
// Check each field for FK requirements (user-defined, or required)
// <field><foreign-key>...</foreign-key></field>
Set memberNumbersSet = memberMappingsMap.keySet();
Iterator iter = memberNumbersSet.iterator();
while (iter.hasNext()) {
AbstractMemberMetaData mmd = (AbstractMemberMetaData) iter.next();
JavaTypeMapping memberMapping = memberMappingsMap.get(mmd);
if (memberMapping instanceof EmbeddedPCMapping) {
EmbeddedPCMapping embMapping = (EmbeddedPCMapping) memberMapping;
addExpectedForeignKeysForEmbeddedPCField(foreignKeys, autoMode, clr, embMapping);
} else {
if (ClassUtils.isReferenceType(mmd.getType()) && memberMapping instanceof ReferenceMapping) {
// Field is a reference type, so add a FK to the table of the PC for each PC implementation
Collection fks = TableUtils.getForeignKeysForReferenceField(memberMapping, mmd, autoMode, storeMgr, clr);
foreignKeys.addAll(fks);
} else if (storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForClass(mmd.getType(), clr) != null && memberMapping.getNumberOfColumnMappings() > 0 && memberMapping instanceof PersistableMapping) {
// Field is for a PC class with the FK at this side, so add a FK to the table of this PC
ForeignKey fk = TableUtils.getForeignKeyForPCField(memberMapping, mmd, autoMode, storeMgr, clr);
if (fk != null) {
// Check for dups (can happen if we override a persistent property for 1-1/N-1 in a subclass)
boolean exists = false;
for (ForeignKey theFK : foreignKeys) {
if (theFK.isEqual(fk)) {
exists = true;
break;
}
}
if (!exists) {
foreignKeys.add(fk);
}
}
}
}
}
// FK from id column(s) to id column(s) of superclass, as specified by
// <inheritance><join><foreign-key ...></join></inheritance>
ForeignKeyMetaData idFkmd = (cmd.getInheritanceMetaData().getJoinMetaData() != null) ? cmd.getInheritanceMetaData().getJoinMetaData().getForeignKeyMetaData() : null;
if (supertable != null && (autoMode || (idFkmd != null && idFkmd.getDeleteAction() != ForeignKeyAction.NONE))) {
ForeignKey fk = new ForeignKey(getIdMapping(), dba, supertable, false);
if (idFkmd != null && idFkmd.getName() != null) {
fk.setName(idFkmd.getName());
}
foreignKeys.add(0, fk);
}
// Add any user-required FKs for the class as a whole
// <class><foreign-key>...</foreign-key></field>
Iterator<AbstractClassMetaData> cmdIter = managedClassMetaData.iterator();
while (cmdIter.hasNext()) {
AbstractClassMetaData thisCmd = cmdIter.next();
List<ForeignKeyMetaData> fkmds = thisCmd.getForeignKeyMetaData();
if (fkmds != null) {
for (ForeignKeyMetaData fkmd : fkmds) {
ForeignKey fk = getForeignKeyForForeignKeyMetaData(fkmd);
if (fk != null) {
foreignKeys.add(fk);
}
}
}
}
Map externalFks = getExternalFkMappings();
if (!externalFks.isEmpty()) {
// 1-N FK relationships - FK to id column(s) of owner table where this is the element table and we have a FK
Collection externalFkKeys = externalFks.entrySet();
Iterator<Map.Entry<AbstractMemberMetaData, JavaTypeMapping>> externalFkKeysIter = externalFkKeys.iterator();
while (externalFkKeysIter.hasNext()) {
Map.Entry<AbstractMemberMetaData, JavaTypeMapping> entry = externalFkKeysIter.next();
AbstractMemberMetaData fmd = entry.getKey();
DatastoreClass referencedTable = storeMgr.getDatastoreClass(fmd.getAbstractClassMetaData().getFullClassName(), clr);
if (referencedTable != null) {
// Take <foreign-key> from either <field> or <element>
ForeignKeyMetaData fkmd = fmd.getForeignKeyMetaData();
if (fkmd == null && fmd.getElementMetaData() != null) {
fkmd = fmd.getElementMetaData().getForeignKeyMetaData();
}
if ((fkmd != null && fkmd.getDeleteAction() != ForeignKeyAction.NONE) || autoMode) {
// Either has been specified by user, or using autoMode, so add FK
JavaTypeMapping fkMapping = entry.getValue();
ForeignKey fk = new ForeignKey(fkMapping, dba, referencedTable, true);
// Does nothing when no FK MetaData
fk.setForMetaData(fkmd);
if (!foreignKeys.contains(fk)) {
// Only add when not already present (in the case of shared FKs there can be dups here)
foreignKeys.add(fk);
}
}
}
}
}
return foreignKeys;
}
Aggregations