Search in sources :

Example 31 with ReferenceMapping

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);
}
Also used : SQLExpressionFactory(org.datanucleus.store.rdbms.sql.expression.SQLExpressionFactory) SQLExpression(org.datanucleus.store.rdbms.sql.expression.SQLExpression) StatementClassMapping(org.datanucleus.store.rdbms.query.StatementClassMapping) UnionStatementGenerator(org.datanucleus.store.rdbms.sql.UnionStatementGenerator) SelectStatementGenerator(org.datanucleus.store.rdbms.sql.SelectStatementGenerator) SelectStatement(org.datanucleus.store.rdbms.sql.SelectStatement) ReferenceMapping(org.datanucleus.store.rdbms.mapping.java.ReferenceMapping) DiscriminatorStatementGenerator(org.datanucleus.store.rdbms.sql.DiscriminatorStatementGenerator) SQLTable(org.datanucleus.store.rdbms.sql.SQLTable) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) NucleusException(org.datanucleus.exceptions.NucleusException)

Example 32 with ReferenceMapping

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);
        }
    }
}
Also used : FetchPlanForClass(org.datanucleus.FetchPlanForClass) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) StatementMappingIndex(org.datanucleus.store.rdbms.query.StatementMappingIndex) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) ReferenceMapping(org.datanucleus.store.rdbms.mapping.java.ReferenceMapping) RelationType(org.datanucleus.metadata.RelationType) List(java.util.List) ArrayList(java.util.ArrayList) ElementContainerTable(org.datanucleus.store.rdbms.table.ElementContainerTable) ElementContainerTable(org.datanucleus.store.rdbms.table.ElementContainerTable) PersistableJoinTable(org.datanucleus.store.rdbms.table.PersistableJoinTable) Table(org.datanucleus.store.rdbms.table.Table) JoinTable(org.datanucleus.store.rdbms.table.JoinTable) MetaDataManager(org.datanucleus.metadata.MetaDataManager) JoinType(org.datanucleus.store.rdbms.sql.SQLJoin.JoinType) RDBMSStoreManager(org.datanucleus.store.rdbms.RDBMSStoreManager) StatementClassMapping(org.datanucleus.store.rdbms.query.StatementClassMapping) DatastoreAdapter(org.datanucleus.store.rdbms.adapter.DatastoreAdapter) SecondaryDatastoreClass(org.datanucleus.store.rdbms.table.SecondaryDatastoreClass) FetchPlanForClass(org.datanucleus.FetchPlanForClass) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) PersistableJoinTable(org.datanucleus.store.rdbms.table.PersistableJoinTable) SecondaryDatastoreClass(org.datanucleus.store.rdbms.table.SecondaryDatastoreClass) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData)

Example 33 with ReferenceMapping

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;
}
Also used : FetchPlanForClass(org.datanucleus.FetchPlanForClass) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) JoinType(org.datanucleus.store.rdbms.sql.SQLJoin.JoinType) StatementMappingIndex(org.datanucleus.store.rdbms.query.StatementMappingIndex) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) RDBMSStoreManager(org.datanucleus.store.rdbms.RDBMSStoreManager) StatementClassMapping(org.datanucleus.store.rdbms.query.StatementClassMapping) ReferenceMapping(org.datanucleus.store.rdbms.mapping.java.ReferenceMapping) SecondaryDatastoreClass(org.datanucleus.store.rdbms.table.SecondaryDatastoreClass) FetchPlanForClass(org.datanucleus.FetchPlanForClass) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) SecondaryDatastoreClass(org.datanucleus.store.rdbms.table.SecondaryDatastoreClass) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass)

Example 34 with ReferenceMapping

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);
                }
            }
        }
    }
}
Also used : PersistableMapping(org.datanucleus.store.rdbms.mapping.java.PersistableMapping) ReferenceMapping(org.datanucleus.store.rdbms.mapping.java.ReferenceMapping) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) EmbeddedPCMapping(org.datanucleus.store.rdbms.mapping.java.EmbeddedPCMapping) Collection(java.util.Collection) ForeignKey(org.datanucleus.store.rdbms.key.ForeignKey) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData)

Example 35 with ReferenceMapping

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;
}
Also used : Set(java.util.Set) HashSet(java.util.HashSet) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) EmbeddedPCMapping(org.datanucleus.store.rdbms.mapping.java.EmbeddedPCMapping) ArrayList(java.util.ArrayList) ForeignKey(org.datanucleus.store.rdbms.key.ForeignKey) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) PersistableMapping(org.datanucleus.store.rdbms.mapping.java.PersistableMapping) ReferenceMapping(org.datanucleus.store.rdbms.mapping.java.ReferenceMapping) Iterator(java.util.Iterator) Collection(java.util.Collection) ForeignKeyMetaData(org.datanucleus.metadata.ForeignKeyMetaData) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData) Map(java.util.Map) HashMap(java.util.HashMap)

Aggregations

ReferenceMapping (org.datanucleus.store.rdbms.mapping.java.ReferenceMapping)38 JavaTypeMapping (org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping)23 PersistableMapping (org.datanucleus.store.rdbms.mapping.java.PersistableMapping)16 AbstractMemberMetaData (org.datanucleus.metadata.AbstractMemberMetaData)14 StatementMappingIndex (org.datanucleus.store.rdbms.query.StatementMappingIndex)13 DatastoreClass (org.datanucleus.store.rdbms.table.DatastoreClass)13 ExecutionContext (org.datanucleus.ExecutionContext)12 AbstractClassMetaData (org.datanucleus.metadata.AbstractClassMetaData)12 SQLException (java.sql.SQLException)11 PersistentClassROF (org.datanucleus.store.rdbms.query.PersistentClassROF)11 StatementClassMapping (org.datanucleus.store.rdbms.query.StatementClassMapping)11 PreparedStatement (java.sql.PreparedStatement)10 ResultSet (java.sql.ResultSet)10 NucleusDataStoreException (org.datanucleus.exceptions.NucleusDataStoreException)10 ManagedConnection (org.datanucleus.store.connection.ManagedConnection)10 SQLController (org.datanucleus.store.rdbms.SQLController)10 ResultObjectFactory (org.datanucleus.store.rdbms.query.ResultObjectFactory)10 SelectStatement (org.datanucleus.store.rdbms.sql.SelectStatement)10 SQLTable (org.datanucleus.store.rdbms.sql.SQLTable)8 NucleusUserException (org.datanucleus.exceptions.NucleusUserException)7