Search in sources :

Example 21 with RelationType

use of org.datanucleus.metadata.RelationType in project datanucleus-core by datanucleus.

the class JavaQueryCompiler method getClassForSubqueryClassExpression.

/**
 * Convenience method to find the class that a subquery class expression refers to.
 * Allows for reference to the parent query candidate class, or to a class name.
 * @param classExpr The class expression
 * @return The class that it refers to
 */
private Class getClassForSubqueryClassExpression(String classExpr) {
    if (classExpr == null) {
        return null;
    }
    Class cls = null;
    String[] tokens = StringUtils.split(classExpr, ".");
    if (tokens[0].equalsIgnoreCase(parentCompiler.candidateAlias)) {
        // Starts with candidate of parent query
        cls = parentCompiler.candidateClass;
    } else {
        // Try alias from parent query
        Symbol sym = parentCompiler.symtbl.getSymbolIgnoreCase(tokens[0]);
        if (sym != null) {
            cls = sym.getValueType();
        } else {
            // Must be a class name
            return resolveClass(classExpr);
        }
    }
    AbstractClassMetaData cmd = metaDataManager.getMetaDataForClass(cls, clr);
    for (int i = 1; i < tokens.length; i++) {
        AbstractMemberMetaData mmd = cmd.getMetaDataForMember(tokens[i]);
        RelationType relationType = mmd.getRelationType(clr);
        if (RelationType.isRelationSingleValued(relationType)) {
            cls = mmd.getType();
        } else if (RelationType.isRelationMultiValued(relationType)) {
            if (mmd.hasCollection()) {
                cls = clr.classForName(mmd.getCollection().getElementType());
            } else if (mmd.hasMap()) {
                // Assume we're using the value
                cls = clr.classForName(mmd.getMap().getValueType());
            } else if (mmd.hasArray()) {
                cls = clr.classForName(mmd.getArray().getElementType());
            }
        }
        if (i < tokens.length - 1) {
            cmd = metaDataManager.getMetaDataForClass(cls, clr);
        }
    }
    return cls;
}
Also used : RelationType(org.datanucleus.metadata.RelationType) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData)

Example 22 with RelationType

use of org.datanucleus.metadata.RelationType in project datanucleus-core by datanucleus.

the class JavaQueryCompiler method compileFrom.

/**
 * Method to compile the "from" clause (if present for the query language).
 * @return The compiled from expression(s)
 */
protected Expression[] compileFrom() {
    if (from == null) {
        return null;
    }
    Node[] node = parser.parseFrom(from);
    Expression[] expr = new Expression[node.length];
    for (int i = 0; i < node.length; i++) {
        String className = (String) node[i].getNodeValue();
        String classAlias = null;
        Class cls = null;
        if (parentCompiler != null) {
            cls = getClassForSubqueryClassExpression(className);
        } else {
            cls = resolveClass(className);
        }
        List<Node> children = node[i].getChildNodes();
        for (Node child : children) {
            if (// Alias - maybe should assume it is the first child
            child.getNodeType() == NodeType.NAME) {
                classAlias = (String) child.getNodeValue();
            }
        }
        if (i == 0 && classAlias == null) {
            throw new QueryCompilerSyntaxException("FROM clause of query has class " + cls.getName() + " but no alias");
        }
        if (classAlias != null) {
            if (i == 0) {
                // First expression so set up candidateClass/alias
                candidateClass = cls;
                if (parentCompiler != null && parentCompiler.candidateAlias.equals(classAlias)) {
                    // The defined alias is the same as the parent query, so rename
                    candidateAliasOrig = classAlias;
                    candidateAlias = "sub_" + candidateAlias;
                    classAlias = candidateAlias;
                    swapCandidateAliasNodeName(node[i].getChildNode(0));
                } else {
                    candidateAlias = classAlias;
                }
            }
            if (symtbl.getSymbol(classAlias) == null) {
                // Add symbol for this candidate under its alias
                symtbl.addSymbol(new PropertySymbol(classAlias, cls));
            }
        }
        for (Node childNode : children) {
            // Add entries in symbol table for any joined aliases
            if (childNode.getNodeType() == NodeType.OPERATOR) {
                Node joinedNode = childNode.getFirstChild();
                // Extract alias node
                Node aliasNode = childNode.getNextChild();
                // Extract ON node (if present)
                Node onExprNode = null;
                if (childNode.hasNextChild()) {
                    onExprNode = childNode.getNextChild();
                }
                String joinedAlias = (String) joinedNode.getNodeValue();
                boolean rootNode = false;
                Symbol joinedSym = caseSensitiveAliases ? symtbl.getSymbol(joinedAlias) : symtbl.getSymbolIgnoreCase(joinedAlias);
                if (joinedSym == null) {
                    // DN Extension : Check for FROM clause including join to a new root
                    if (aliasNode != null) {
                        joinedAlias = (String) aliasNode.getNodeValue();
                        cls = resolveClass((String) joinedNode.getNodeValue());
                        if (symtbl.getSymbol(joinedAlias) == null) {
                            // Add symbol for this candidate under its alias
                            symtbl.addSymbol(new PropertySymbol(joinedAlias, cls));
                            rootNode = true;
                            NucleusLogger.QUERY.debug("Found suspected ROOT node joined to in FROM clause : attempting to process as alias=" + joinedAlias);
                        }
                        joinedSym = caseSensitiveAliases ? symtbl.getSymbol(joinedAlias) : symtbl.getSymbolIgnoreCase(joinedAlias);
                    }
                    if (joinedSym == null) {
                        throw new QueryCompilerSyntaxException("FROM clause has identifier " + joinedNode.getNodeValue() + " but this is unknown");
                    }
                }
                if (!rootNode) {
                    AbstractClassMetaData joinedCmd = metaDataManager.getMetaDataForClass(joinedSym.getValueType(), clr);
                    Class joinedCls = joinedSym.getValueType();
                    AbstractMemberMetaData joinedMmd = null;
                    while (joinedNode.getFirstChild() != null) {
                        joinedNode = joinedNode.getFirstChild();
                        String joinedMember = (String) joinedNode.getNodeValue();
                        if (joinedNode.getNodeType() == NodeType.CAST) {
                            // JOIN to "TREAT(identifier AS subcls)"
                            String castTypeName = (String) joinedNode.getNodeValue();
                            if (castTypeName.indexOf('.') < 0) {
                                // Fully-qualify with the current class name?
                                castTypeName = ClassUtils.createFullClassName(joinedCmd.getPackageName(), castTypeName);
                            }
                            joinedCls = clr.classForName(castTypeName);
                            // Update cast type now that we have resolved it
                            joinedNode.setNodeValue(castTypeName);
                        } else {
                            // Allow for multi-field joins
                            String[] joinedMembers = joinedMember.contains(".") ? StringUtils.split(joinedMember, ".") : new String[] { joinedMember };
                            for (int k = 0; k < joinedMembers.length; k++) {
                                String memberName = joinedMembers[k];
                                if (joinedCmd == null) {
                                    throw new NucleusUserException("Query has JOIN to " + memberName + " but previous element (" + joinedCls.getName() + ") has no metadata");
                                }
                                if (memberName.endsWith("#KEY")) {
                                    memberName = memberName.substring(0, memberName.length() - 4);
                                } else if (memberName.endsWith("#VALUE")) {
                                    memberName = memberName.substring(0, memberName.length() - 6);
                                }
                                AbstractMemberMetaData mmd = joinedCmd.getMetaDataForMember(memberName);
                                if (mmd == null) {
                                    if (childNode.getNodeValue().equals(JOIN_OUTER) || childNode.getNodeValue().equals(JOIN_OUTER_FETCH)) {
                                        // Polymorphic join, where the field exists in a subclass (doable since we have outer join)
                                        String[] subclasses = metaDataManager.getSubclassesForClass(joinedCmd.getFullClassName(), true);
                                        if (subclasses != null) {
                                            for (int l = 0; l < subclasses.length; l++) {
                                                AbstractClassMetaData subCmd = metaDataManager.getMetaDataForClass(subclasses[l], clr);
                                                if (subCmd != null) {
                                                    mmd = subCmd.getMetaDataForMember(memberName);
                                                    if (mmd != null) {
                                                        NucleusLogger.QUERY.debug("Polymorphic join found at " + memberName + " of " + subCmd.getFullClassName());
                                                        joinedCmd = subCmd;
                                                        break;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    if (mmd == null) {
                                        throw new QueryCompilerSyntaxException("FROM clause has reference to " + joinedCmd.getFullClassName() + "." + joinedMembers[k] + " but it doesn't exist!");
                                    }
                                }
                                RelationType relationType = mmd.getRelationType(clr);
                                joinedMmd = mmd;
                                if (RelationType.isRelationSingleValued(relationType)) {
                                    joinedCls = mmd.getType();
                                    joinedCmd = metaDataManager.getMetaDataForClass(joinedCls, clr);
                                } else if (RelationType.isRelationMultiValued(relationType)) {
                                    if (mmd.hasCollection()) {
                                        // TODO Don't currently allow interface field navigation
                                        joinedCmd = mmd.getCollection().getElementClassMetaData(clr);
                                        if (joinedCmd != null) {
                                            joinedCls = clr.classForName(joinedCmd.getFullClassName());
                                        } else {
                                            joinedCls = clr.classForName(mmd.getCollection().getElementType());
                                        }
                                    } else if (mmd.hasMap()) {
                                        if (joinedMembers[k].endsWith("#KEY")) {
                                            joinedCmd = mmd.getMap().getKeyClassMetaData(clr);
                                        // TODO Set joinedCls
                                        } else {
                                            joinedCmd = mmd.getMap().getValueClassMetaData(clr);
                                            if (joinedCmd != null) {
                                                // JPA assumption that the value is an entity ... but it may not be!
                                                joinedCls = clr.classForName(joinedCmd.getFullClassName());
                                            } else {
                                                joinedCls = clr.classForName(mmd.getMap().getValueType());
                                            }
                                        }
                                    } else if (mmd.hasArray()) {
                                        // TODO Don't currently allow interface field navigation
                                        joinedCmd = mmd.getArray().getElementClassMetaData(clr);
                                        if (joinedCmd != null) {
                                            joinedCls = clr.classForName(joinedCmd.getFullClassName());
                                        } else {
                                            joinedCls = clr.classForName(mmd.getArray().getElementType());
                                        }
                                    }
                                }
                            }
                        }
                    }
                    if (aliasNode != null && aliasNode.getNodeType() == NodeType.NAME) {
                        // Add JOIN alias to symbol table
                        String alias = (String) aliasNode.getNodeValue();
                        symtbl.addSymbol(new PropertySymbol(alias, joinedCls));
                        if (joinedMmd != null && joinedMmd.hasMap()) {
                            Class keyCls = clr.classForName(joinedMmd.getMap().getKeyType());
                            // Add the KEY so that we can have joins to the key from the value alias
                            symtbl.addSymbol(new PropertySymbol(alias + "#KEY", keyCls));
                            Class valueCls = clr.classForName(joinedMmd.getMap().getValueType());
                            // Add the VALUE so that we can have joins to the value from the key alias
                            symtbl.addSymbol(new PropertySymbol(alias + "#VALUE", valueCls));
                        }
                    }
                }
                if (onExprNode != null) {
                    // ON condition
                    ExpressionCompiler comp = new ExpressionCompiler();
                    comp.setSymbolTable(symtbl);
                    comp.setMethodAliases(queryMgr.getQueryMethodAliasesByPrefix());
                    Expression nextExpr = comp.compileExpression(onExprNode);
                    nextExpr.bind(symtbl);
                }
            }
        }
        boolean classIsExpression = false;
        String[] tokens = StringUtils.split(className, ".");
        if (symtbl.getParentSymbolTable() != null) {
            if (symtbl.getParentSymbolTable().hasSymbol(tokens[0])) {
                classIsExpression = true;
            }
        }
        ExpressionCompiler comp = new ExpressionCompiler();
        comp.setSymbolTable(symtbl);
        comp.setMethodAliases(queryMgr.getQueryMethodAliasesByPrefix());
        expr[i] = comp.compileFromExpression(node[i], classIsExpression);
        if (expr[i] != null) {
            expr[i].bind(symtbl);
        }
    }
    return expr;
}
Also used : NucleusUserException(org.datanucleus.exceptions.NucleusUserException) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) ParameterExpression(org.datanucleus.query.expression.ParameterExpression) Expression(org.datanucleus.query.expression.Expression) VariableExpression(org.datanucleus.query.expression.VariableExpression) PrimaryExpression(org.datanucleus.query.expression.PrimaryExpression) QueryCompilerSyntaxException(org.datanucleus.store.query.QueryCompilerSyntaxException) RelationType(org.datanucleus.metadata.RelationType) ExpressionCompiler(org.datanucleus.query.expression.ExpressionCompiler) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData)

Example 23 with RelationType

use of org.datanucleus.metadata.RelationType in project datanucleus-rdbms by datanucleus.

the class CollectionTable method initialize.

/**
 * Method to initialise the table definition.
 * @param clr The ClassLoaderResolver
 */
public void initialize(ClassLoaderResolver clr) {
    super.initialize(clr);
    // Add column(s) for element
    boolean elementPC = (mmd.hasCollection() && mmd.getCollection().elementIsPersistent());
    Class elementClass = clr.classForName(getElementType());
    if (isSerialisedElement() || isEmbeddedElementPC() || (isEmbeddedElement() && !elementPC) || ClassUtils.isReferenceType(elementClass)) {
        // Element = PC(embedded), PC(serialised), Non-PC(serialised), Non-PC(embedded), Reference
        // Join table has : ownerMapping (PK), elementMapping, orderMapping (PK)
        elementMapping = storeMgr.getMappingManager().getMapping(this, mmd, clr, FieldRole.ROLE_COLLECTION_ELEMENT);
        if (NucleusLogger.DATASTORE.isDebugEnabled()) {
            logMapping(mmd.getFullFieldName() + ".[ELEMENT]", elementMapping);
        }
    } else {
        // Element = PC
        // Join table has : ownerMapping (PK), elementMapping, orderMapping (optional)
        ColumnMetaData[] elemColmd = null;
        AbstractMemberMetaData[] relatedMmds = mmd.getRelatedMemberMetaData(clr);
        ElementMetaData elemmd = mmd.getElementMetaData();
        if (elemmd != null && elemmd.getColumnMetaData() != null && elemmd.getColumnMetaData().length > 0) {
            // Column mappings defined at this side (1-N, M-N)
            elemColmd = elemmd.getColumnMetaData();
        } else if (relatedMmds != null && relatedMmds[0].getJoinMetaData() != null && relatedMmds[0].getJoinMetaData().getColumnMetaData() != null && relatedMmds[0].getJoinMetaData().getColumnMetaData().length > 0) {
            // Column mappings defined at other side (M-N) on <join>
            elemColmd = relatedMmds[0].getJoinMetaData().getColumnMetaData();
        }
        elementMapping = ColumnCreator.createColumnsForJoinTables(elementClass, mmd, elemColmd, storeMgr, this, false, false, FieldRole.ROLE_COLLECTION_ELEMENT, clr, null);
        RelationType relationType = mmd.getRelationType(clr);
        if (Boolean.TRUE.equals(mmd.getContainer().allowNulls()) && relationType != RelationType.MANY_TO_MANY_BI) {
            // 1-N : Make all element col(s) nullable so we can store null elements
            for (int i = 0; i < elementMapping.getNumberOfDatastoreMappings(); i++) {
                Column elementCol = elementMapping.getDatastoreMapping(i).getColumn();
                elementCol.setNullable(true);
            }
        }
        if (NucleusLogger.DATASTORE.isDebugEnabled()) {
            logMapping(mmd.getFullFieldName() + ".[ELEMENT]", elementMapping);
        }
    }
    PrimaryKeyMetaData pkmd = (mmd.getJoinMetaData() != null ? mmd.getJoinMetaData().getPrimaryKeyMetaData() : null);
    boolean pkColsSpecified = (pkmd != null ? pkmd.getColumnMetaData() != null : false);
    boolean pkRequired = requiresPrimaryKey();
    // Add order mapping if required
    boolean orderRequired = false;
    if (mmd.getOrderMetaData() != null) {
        if (mmd.getOrderMetaData().isIndexedList()) {
            // Indexed Collection with <order>, so add index mapping
            orderRequired = true;
            RelationType relType = mmd.getRelationType(clr);
            if (relType == RelationType.MANY_TO_MANY_BI) {
                // Don't support M-N using indexed List
                throw new NucleusUserException(Localiser.msg("020002", mmd.getFullFieldName())).setFatal();
            }
        }
    } else if (List.class.isAssignableFrom(mmd.getType())) {
        // Indexed List with no <order>, so has index mapping
        orderRequired = true;
    } else if (pkRequired && !pkColsSpecified) {
        // PK is required so maybe need to add an index to form the PK
        if (isEmbeddedElementPC()) {
            if (mmd.getCollection().getElementClassMetaData(clr).getIdentityType() != IdentityType.APPLICATION) {
                // Embedded PC with datastore id so we need an index to form the PK
                orderRequired = true;
            }
        } else if (isSerialisedElement()) {
            // Serialised element, so need an index to form the PK
            orderRequired = true;
        } else if (elementMapping instanceof ReferenceMapping) {
            // ReferenceMapping, so have order if more than 1 implementation
            ReferenceMapping refMapping = (ReferenceMapping) elementMapping;
            if (refMapping.getJavaTypeMapping().length > 1) {
                orderRequired = true;
            }
        } else if (!(elementMapping instanceof PersistableMapping)) {
            // Non-PC, so depends if the element column can be used as part of a PK
            // TODO This assumes the elementMapping has a single column but what if it is Color with 4 cols?
            Column elementCol = elementMapping.getDatastoreMapping(0).getColumn();
            if (!storeMgr.getDatastoreAdapter().isValidPrimaryKeyType(elementCol.getJdbcType())) {
                // Not possible to use this Non-PC type as part of the PK
                orderRequired = true;
            }
        }
    }
    if (orderRequired) {
        // Order (index) column is required (integer based)
        ColumnMetaData orderColmd = null;
        if (mmd.getOrderMetaData() != null && mmd.getOrderMetaData().getColumnMetaData() != null && mmd.getOrderMetaData().getColumnMetaData().length > 0) {
            // Specified "order" column info
            orderColmd = mmd.getOrderMetaData().getColumnMetaData()[0];
            if (orderColmd.getName() == null) {
                // No column name so use default
                orderColmd = new ColumnMetaData(orderColmd);
                DatastoreIdentifier id = storeMgr.getIdentifierFactory().newIndexFieldIdentifier(mmd);
                orderColmd.setName(id.getName());
            }
        } else {
            // No column name so use default
            DatastoreIdentifier id = storeMgr.getIdentifierFactory().newIndexFieldIdentifier(mmd);
            orderColmd = new ColumnMetaData();
            orderColmd.setName(id.getName());
        }
        // JDO2 spec [18.5] order column is assumed to be "int"
        orderMapping = storeMgr.getMappingManager().getMapping(int.class);
        ColumnCreator.createIndexColumn(orderMapping, storeMgr, clr, this, orderColmd, pkRequired && !pkColsSpecified);
        if (NucleusLogger.DATASTORE.isDebugEnabled()) {
            logMapping(mmd.getFullFieldName() + ".[ORDER]", orderMapping);
        }
    }
    // Define primary key of the join table (if any)
    if (pkRequired) {
        if (pkColsSpecified) {
            // Apply the users PK specification
            applyUserPrimaryKeySpecification(pkmd);
        } else {
            // Define PK
            for (int i = 0; i < ownerMapping.getNumberOfDatastoreMappings(); i++) {
                ownerMapping.getDatastoreMapping(i).getColumn().setPrimaryKey();
            }
            if (orderRequired) {
                // Order column specified so owner+order are the PK
                orderMapping.getDatastoreMapping(0).getColumn().setPrimaryKey();
            } else {
                // No order column specified so owner+element are the PK
                for (int i = 0; i < elementMapping.getNumberOfDatastoreMappings(); i++) {
                    elementMapping.getDatastoreMapping(i).getColumn().setPrimaryKey();
                }
            }
        }
    }
    if (NucleusLogger.DATASTORE_SCHEMA.isDebugEnabled()) {
        NucleusLogger.DATASTORE_SCHEMA.debug(Localiser.msg("057023", this));
    }
    storeMgr.registerTableInitialized(this);
    state = TABLE_STATE_INITIALIZED;
}
Also used : ElementMetaData(org.datanucleus.metadata.ElementMetaData) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) PrimaryKeyMetaData(org.datanucleus.metadata.PrimaryKeyMetaData) PersistableMapping(org.datanucleus.store.rdbms.mapping.java.PersistableMapping) ReferenceMapping(org.datanucleus.store.rdbms.mapping.java.ReferenceMapping) DatastoreIdentifier(org.datanucleus.store.rdbms.identifier.DatastoreIdentifier) RelationType(org.datanucleus.metadata.RelationType) List(java.util.List) ColumnMetaData(org.datanucleus.metadata.ColumnMetaData) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData)

Example 24 with RelationType

use of org.datanucleus.metadata.RelationType in project datanucleus-rdbms by datanucleus.

the class ClassTable method manageMembers.

/**
 * Goes through all specified members for the specified class and adds a mapping for each.
 * Ignores primary-key fields which are added elsewhere.
 * @param theCmd ClassMetaData for the class to be managed
 * @param clr The ClassLoaderResolver
 * @param mmds the fields/properties to manage
 */
private void manageMembers(AbstractClassMetaData theCmd, ClassLoaderResolver clr, AbstractMemberMetaData[] mmds) {
    // Go through the fields for this class and add columns for them
    for (int fieldNumber = 0; fieldNumber < mmds.length; fieldNumber++) {
        // Primary key fields are added by the initialisePK method
        AbstractMemberMetaData mmd = mmds[fieldNumber];
        if (!mmd.isPrimaryKey()) {
            if (managesMember(mmd.getFullFieldName())) {
                if (!mmd.getClassName(true).equals(theCmd.getFullClassName())) {
                    // Field already managed by this table so maybe we are overriding a superclass
                    JavaTypeMapping fieldMapping = getMappingForMemberName(mmd.getFullFieldName());
                    ColumnMetaData[] colmds = mmd.getColumnMetaData();
                    if (colmds != null && colmds.length > 0) {
                        // Apply this set of ColumnMetaData to the existing mapping
                        int colnum = 0;
                        IdentifierFactory idFactory = getStoreManager().getIdentifierFactory();
                        for (int i = 0; i < fieldMapping.getNumberOfDatastoreMappings(); i++) {
                            Column col = fieldMapping.getDatastoreMapping(i).getColumn();
                            col.setIdentifier(idFactory.newColumnIdentifier(colmds[colnum].getName()));
                            col.setColumnMetaData(colmds[colnum]);
                            colnum++;
                            if (colnum == colmds.length) {
                                // Reached end of specified metadata
                                break;
                            }
                        }
                        // TODO Change this to reflect that we have updated the previous mapping
                        logMapping(mmd.getFullFieldName(), fieldMapping);
                    }
                }
            } else {
                // Manage the field if not already managed (may already exist if overriding a superclass field)
                if (mmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) {
                    boolean isPrimary = true;
                    if (mmd.getTable() != null && mmd.getJoinMetaData() == null) {
                        // Field has a table specified and is not a 1-N with join table
                        // so is mapped to a secondary table
                        isPrimary = false;
                    }
                    if (isPrimary) {
                        // Add the field to this table
                        JavaTypeMapping fieldMapping = storeMgr.getMappingManager().getMapping(this, mmd, clr, FieldRole.ROLE_FIELD);
                        if (theCmd != cmd && theCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUPERCLASS_TABLE && fieldMapping.getNumberOfDatastoreMappings() > 0) {
                            // Field is for a subclass and so column(s) has to either allow nulls, or have default
                            int numCols = fieldMapping.getNumberOfDatastoreMappings();
                            for (int colNum = 0; colNum < numCols; colNum++) {
                                Column col = fieldMapping.getDatastoreMapping(colNum).getColumn();
                                if (col.getDefaultValue() == null && !col.isNullable()) {
                                    // Column needs to be nullable
                                    NucleusLogger.DATASTORE_SCHEMA.debug("Member " + mmd.getFullFieldName() + " uses superclass-table yet the field is not marked as nullable " + " nor does it have a default value, so setting the column as nullable");
                                    col.setNullable(true);
                                }
                            }
                        }
                        addMemberMapping(fieldMapping);
                    } else {
                        // Add the field to the appropriate secondary table
                        if (secondaryTables == null) {
                            secondaryTables = new HashMap();
                        }
                        SecondaryTable secTable = secondaryTables.get(mmd.getTable());
                        if (secTable == null) {
                            // Secondary table doesnt exist yet so create it to users specifications.
                            List<JoinMetaData> joinmds = theCmd.getJoinMetaData();
                            JoinMetaData theJoinMD = null;
                            if (joinmds != null) {
                                for (JoinMetaData joinmd : joinmds) {
                                    if (joinmd.getTable().equalsIgnoreCase(mmd.getTable()) && (joinmd.getCatalog() == null || (joinmd.getCatalog() != null && joinmd.getCatalog().equalsIgnoreCase(mmd.getCatalog()))) && (joinmd.getSchema() == null || (joinmd.getSchema() != null && joinmd.getSchema().equalsIgnoreCase(mmd.getSchema())))) {
                                        theJoinMD = joinmd;
                                        break;
                                    }
                                }
                            }
                            DatastoreIdentifier secTableIdentifier = storeMgr.getIdentifierFactory().newTableIdentifier(mmd.getTable());
                            // Use specified catalog, else take catalog of the owning table
                            String catalogName = mmd.getCatalog();
                            if (catalogName == null) {
                                catalogName = getCatalogName();
                            }
                            // Use specified schema, else take schema of the owning table
                            String schemaName = mmd.getSchema();
                            if (schemaName == null) {
                                schemaName = getSchemaName();
                            }
                            secTableIdentifier.setCatalogName(catalogName);
                            secTableIdentifier.setSchemaName(schemaName);
                            secTable = new SecondaryTable(secTableIdentifier, storeMgr, this, theJoinMD, clr);
                            secTable.preInitialize(clr);
                            secTable.initialize(clr);
                            secTable.postInitialize(clr);
                            secondaryTables.put(mmd.getTable(), secTable);
                        }
                        secTable.addMemberMapping(storeMgr.getMappingManager().getMapping(secTable, mmd, clr, FieldRole.ROLE_FIELD));
                    }
                } else if (mmd.getPersistenceModifier() != FieldPersistenceModifier.TRANSACTIONAL) {
                    throw new NucleusException(Localiser.msg("057006", mmd.getName())).setFatal();
                }
                // Calculate if we need a FK adding due to a 1-N (FK) relationship
                boolean needsFKToContainerOwner = false;
                RelationType relationType = mmd.getRelationType(clr);
                if (relationType == RelationType.ONE_TO_MANY_BI) {
                    AbstractMemberMetaData[] relatedMmds = mmd.getRelatedMemberMetaData(clr);
                    if (mmd.getJoinMetaData() == null && relatedMmds[0].getJoinMetaData() == null) {
                        needsFKToContainerOwner = true;
                    }
                } else if (relationType == RelationType.ONE_TO_MANY_UNI && !mmd.isSingleCollection()) {
                    if (mmd.getJoinMetaData() == null) {
                        needsFKToContainerOwner = true;
                    }
                }
                if (needsFKToContainerOwner) {
                    // 1-N uni/bidirectional using FK, so update the element side with a FK
                    if ((mmd.getCollection() != null && !SCOUtils.collectionHasSerialisedElements(mmd)) || (mmd.getArray() != null && !SCOUtils.arrayIsStoredInSingleColumn(mmd, storeMgr.getMetaDataManager()))) {
                        // 1-N ForeignKey collection/array, so add FK to element table
                        AbstractClassMetaData elementCmd = null;
                        if (mmd.hasCollection()) {
                            // Collection
                            elementCmd = storeMgr.getMetaDataManager().getMetaDataForClass(mmd.getCollection().getElementType(), clr);
                        } else {
                            // Array
                            elementCmd = storeMgr.getMetaDataManager().getMetaDataForClass(mmd.getType().getComponentType(), clr);
                        }
                        if (elementCmd == null) {
                            String[] implClassNames = storeMgr.getMetaDataManager().getClassesImplementingInterface(mmd.getCollection().getElementType(), clr);
                            if (implClassNames != null && implClassNames.length > 0) {
                                // Collection/array of interface type so apply callback to all implementation types
                                AbstractClassMetaData[] elementCmds = new AbstractClassMetaData[implClassNames.length];
                                for (int i = 0; i < implClassNames.length; i++) {
                                    elementCmds[i] = storeMgr.getMetaDataManager().getMetaDataForClass(implClassNames[i], clr);
                                }
                                // Run callbacks for each of the element classes.
                                for (int i = 0; i < elementCmds.length; i++) {
                                    storeMgr.addSchemaCallback(elementCmds[i].getFullClassName(), mmd);
                                    DatastoreClass dc = storeMgr.getDatastoreClass(elementCmds[i].getFullClassName(), clr);
                                    if (dc == null) {
                                        throw new NucleusException("Unable to add foreign-key to " + elementCmds[i].getFullClassName() + " to " + this + " since element has no table!");
                                    }
                                    if (dc instanceof ClassTable) {
                                        ClassTable ct = (ClassTable) dc;
                                        if (ct.isInitialized()) {
                                            // if the target table is already initialized, run the callbacks
                                            ct.runCallBacks(clr);
                                        }
                                    } else {
                                        NucleusLogger.DATASTORE_SCHEMA.info("Table " + toString() + " has to manage member " + mmd.getFullFieldName() + " yet the related element uses a VIEW so not remotely adding element FK owner column; assumed to be part of the VIEW definition");
                                    }
                                }
                            } else {
                                // Elements that are reference types or non-PC will come through here
                                if (mmd.hasCollection()) {
                                    NucleusLogger.METADATA.warn(Localiser.msg("057016", theCmd.getFullClassName(), mmd.getCollection().getElementType()));
                                } else {
                                    NucleusLogger.METADATA.warn(Localiser.msg("057014", theCmd.getFullClassName(), mmd.getType().getComponentType().getName()));
                                }
                            }
                        } else {
                            AbstractClassMetaData[] elementCmds = null;
                            // TODO : Cater for interface elements, and get the metadata for the implementation classes here
                            if (elementCmd.getBaseAbstractClassMetaData().getInheritanceMetaData().getStrategy() == InheritanceStrategy.COMPLETE_TABLE) {
                                // COMPLETE-TABLE inheritance in element, so really need FK in each!
                                Collection<String> elementSubclassNames = storeMgr.getSubClassesForClass(elementCmd.getFullClassName(), true, clr);
                                elementCmds = new ClassMetaData[elementSubclassNames != null ? 1 + elementSubclassNames.size() : 1];
                                int elemNo = 0;
                                elementCmds[elemNo++] = elementCmd;
                                if (elementSubclassNames != null) {
                                    for (String elementSubclassName : elementSubclassNames) {
                                        AbstractClassMetaData elemSubCmd = storeMgr.getMetaDataManager().getMetaDataForClass(elementSubclassName, clr);
                                        elementCmds[elemNo++] = elemSubCmd;
                                    }
                                }
                            } else if (elementCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE) {
                                elementCmds = storeMgr.getClassesManagingTableForClass(elementCmd, clr);
                            } else {
                                elementCmds = new ClassMetaData[1];
                                elementCmds[0] = elementCmd;
                            }
                            ElementMetaData elemmd = mmd.getElementMetaData();
                            if (elemmd != null && !StringUtils.isWhitespace(elemmd.getTable())) {
                                DatastoreIdentifier requiredTableId = storeMgr.getIdentifierFactory().newTableIdentifier(elemmd.getTable());
                                DatastoreClass requiredTable = storeMgr.getDatastoreClass(requiredTableId);
                                if (requiredTable != null) {
                                    // TODO Respect specification of table in ElementMetaData rather than just defaulting to table of element type
                                    // Note that this will need updates to FKListStore, FKSetStore etc to look for the table
                                    NucleusLogger.GENERAL.warn("Member=" + mmd.getFullFieldName() + " has 1-N FK with required table=" + requiredTable + " : we don't currently support specification of the element table, and always take the default table for the element type");
                                /*for (int i=0;i<elementCmds.length;i++)
                                        {
                                            AbstractClassMetaData theElementCmd = elementCmds[i];
                                            while (theElementCmd != null)
                                            {
                                                if (requiredTable.managesClass(theElementCmd.getFullClassName()))
                                                {
                                                    if (theElementCmd != elementCmds[i])
                                                    {
                                                        elementCmds = new ClassMetaData[1];
                                                        elementCmds[0] = theElementCmd;
                                                        break;
                                                    }
                                                }
                                                theElementCmd = theElementCmd.getSuperAbstractClassMetaData();
                                            }
                                        }*/
                                } else {
                                    NucleusLogger.DATASTORE_SCHEMA.warn("Member " + mmd.getFullFieldName() + " specified element FK in table=" + elemmd.getTable() + " but table not known. Ignoring.");
                                }
                            }
                            // Run callbacks for each of the element classes
                            for (int i = 0; i < elementCmds.length; i++) {
                                storeMgr.addSchemaCallback(elementCmds[i].getFullClassName(), mmd);
                                DatastoreClass dc = storeMgr.getDatastoreClass(elementCmds[i].getFullClassName(), clr);
                                if (// If dc is null then we assume the (possible) element is abstract so no FK needed
                                dc != null) {
                                    if (dc instanceof ClassTable) {
                                        ClassTable ct = (ClassTable) dc;
                                        if (ct.isInitialized()) {
                                            // if the target table is already initialized, run the callbacks
                                            ct.runCallBacks(clr);
                                        }
                                    } else {
                                        NucleusLogger.DATASTORE_SCHEMA.info("Table " + toString() + " has to manage member " + mmd.getFullFieldName() + " yet the related element uses a VIEW so not remotely adding element FK owner column; assumed to be part of the VIEW definition");
                                    }
                                }
                            }
                        }
                    } else if (mmd.getMap() != null && !SCOUtils.mapHasSerialisedKeysAndValues(mmd)) {
                        // 1-N ForeignKey map, so add FK to value table
                        if (mmd.getKeyMetaData() != null && mmd.getKeyMetaData().getMappedBy() != null) {
                            // Key is stored in the value table so add the FK to the value table
                            AbstractClassMetaData valueCmd = storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForClass(mmd.getMap().getValueType(), clr);
                            if (valueCmd == null) {
                                // Interface elements will come through here and java.lang.String and others as well
                                NucleusLogger.METADATA.warn(Localiser.msg("057018", theCmd.getFullClassName(), mmd.getMap().getValueType()));
                            } else {
                                AbstractClassMetaData[] valueCmds = null;
                                // TODO : Cater for interface values, and get the metadata for the implementation classes here
                                if (valueCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE) {
                                    valueCmds = storeMgr.getClassesManagingTableForClass(valueCmd, clr);
                                } else {
                                    valueCmds = new ClassMetaData[1];
                                    valueCmds[0] = valueCmd;
                                }
                                // Run callbacks for each of the value classes.
                                for (int i = 0; i < valueCmds.length; i++) {
                                    storeMgr.addSchemaCallback(valueCmds[i].getFullClassName(), mmd);
                                    DatastoreClass dc = storeMgr.getDatastoreClass(valueCmds[i].getFullClassName(), clr);
                                    if (dc instanceof ClassTable) {
                                        ClassTable ct = (ClassTable) dc;
                                        if (ct.isInitialized()) {
                                            // if the target table is already initialized, run the callbacks
                                            ct.runCallBacks(clr);
                                        }
                                    } else {
                                        NucleusLogger.DATASTORE_SCHEMA.info("Table " + toString() + " has to manage member " + mmd.getFullFieldName() + " yet the related value uses a VIEW so not remotely adding value owner FK column; assumed to be part of the VIEW definition");
                                    }
                                }
                            }
                        } else if (mmd.getValueMetaData() != null && mmd.getValueMetaData().getMappedBy() != null) {
                            // Value is stored in the key table so add the FK to the key table
                            AbstractClassMetaData keyCmd = storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForClass(mmd.getMap().getKeyType(), clr);
                            if (keyCmd == null) {
                                // Interface elements will come through here and java.lang.String and others as well
                                NucleusLogger.METADATA.warn(Localiser.msg("057019", theCmd.getFullClassName(), mmd.getMap().getKeyType()));
                            } else {
                                AbstractClassMetaData[] keyCmds = null;
                                // TODO : Cater for interface keys, and get the metadata for the implementation classes here
                                if (keyCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE) {
                                    keyCmds = storeMgr.getClassesManagingTableForClass(keyCmd, clr);
                                } else {
                                    keyCmds = new ClassMetaData[1];
                                    keyCmds[0] = keyCmd;
                                }
                                // Run callbacks for each of the key classes.
                                for (int i = 0; i < keyCmds.length; i++) {
                                    storeMgr.addSchemaCallback(keyCmds[i].getFullClassName(), mmd);
                                    DatastoreClass dc = storeMgr.getDatastoreClass(keyCmds[i].getFullClassName(), clr);
                                    if (dc instanceof ClassTable) {
                                        ClassTable ct = (ClassTable) dc;
                                        if (ct.isInitialized()) {
                                            // if the target table is already initialized, run the callbacks
                                            ct.runCallBacks(clr);
                                        }
                                    } else {
                                        NucleusLogger.DATASTORE_SCHEMA.info("Table " + toString() + " has to manage member " + mmd.getFullFieldName() + " yet the related key uses a VIEW so not remotely adding key FK owner column; assumed to be part of the VIEW definition");
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
Also used : JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) HashMap(java.util.HashMap) ElementMetaData(org.datanucleus.metadata.ElementMetaData) MacroString(org.datanucleus.util.MacroString) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) RelationType(org.datanucleus.metadata.RelationType) ColumnMetaData(org.datanucleus.metadata.ColumnMetaData) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) ClassMetaData(org.datanucleus.metadata.ClassMetaData) IdentifierFactory(org.datanucleus.store.rdbms.identifier.IdentifierFactory) DatastoreIdentifier(org.datanucleus.store.rdbms.identifier.DatastoreIdentifier) JoinMetaData(org.datanucleus.metadata.JoinMetaData) NucleusException(org.datanucleus.exceptions.NucleusException) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData)

Example 25 with RelationType

use of org.datanucleus.metadata.RelationType in project datanucleus-rdbms by datanucleus.

the class RDBMSPersistenceHandler method fetchObject.

// ------------------------------ Fetch ----------------------------------
/**
 * Fetches (fields of) a persistent object from the database.
 * This does a single SELECT on the candidate of the class in question. Will join to inherited
 * tables as appropriate to get values persisted into other tables. Can also join to the tables of
 * related objects (1-1, N-1) as neccessary to retrieve those objects.
 * @param op Object Provider of the object to be fetched.
 * @param memberNumbers The numbers of the members to be fetched.
 * @throws NucleusObjectNotFoundException if the object doesn't exist
 * @throws NucleusDataStoreException when an error occurs in the datastore communication
 */
public void fetchObject(ObjectProvider op, int[] memberNumbers) {
    ExecutionContext ec = op.getExecutionContext();
    ClassLoaderResolver clr = ec.getClassLoaderResolver();
    // Extract metadata of members to process
    AbstractMemberMetaData[] mmds = null;
    if (memberNumbers != null && memberNumbers.length > 0) {
        int[] memberNumbersToProcess = memberNumbers;
        AbstractClassMetaData cmd = op.getClassMetaData();
        if (storeMgr.getBooleanProperty(RDBMSPropertyNames.PROPERTY_RDBMS_FETCH_UNLOADED_AUTO)) {
            // Here we simply load up any unloaded non-relation or 1-1/N-1 members
            if (!op.getLifecycleState().isDeleted()) {
                // Check if this will actually do a SELECT (because we don't want to impose that if not otherwise)
                boolean fetchPerformsSelect = false;
                for (int i = 0; i < memberNumbers.length; i++) {
                    AbstractMemberMetaData mmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(memberNumbers[i]);
                    RelationType relationType = mmd.getRelationType(clr);
                    if (relationType != RelationType.ONE_TO_MANY_UNI && relationType != RelationType.ONE_TO_MANY_BI && relationType != RelationType.MANY_TO_MANY_BI) {
                        fetchPerformsSelect = true;
                        break;
                    }
                }
                if (fetchPerformsSelect) {
                    // Definitely does a SELECT, so try to identify any additional non-relation or 1-1, N-1
                    // members that aren't loaded that could be fetched right now in this call.
                    List<Integer> memberNumberList = new ArrayList<>();
                    for (int i = 0; i < memberNumbers.length; i++) {
                        memberNumberList.add(memberNumbers[i]);
                    }
                    // Check if we could retrieve any other unloaded fields in this call
                    boolean[] loadedFlags = op.getLoadedFields();
                    for (int i = 0; i < loadedFlags.length; i++) {
                        boolean requested = false;
                        for (int j = 0; j < memberNumbers.length; j++) {
                            if (memberNumbers[j] == i) {
                                requested = true;
                                break;
                            }
                        }
                        if (!requested && !loadedFlags[i]) {
                            AbstractMemberMetaData mmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(i);
                            RelationType relType = mmd.getRelationType(clr);
                            if (relType == RelationType.NONE || relType == RelationType.ONE_TO_ONE_BI || relType == RelationType.ONE_TO_ONE_UNI) {
                                memberNumberList.add(i);
                            }
                        }
                    }
                    memberNumbersToProcess = new int[memberNumberList.size()];
                    int i = 0;
                    Iterator<Integer> fieldNumberIter = memberNumberList.iterator();
                    while (fieldNumberIter.hasNext()) {
                        memberNumbersToProcess[i++] = fieldNumberIter.next();
                    }
                }
            }
        }
        // Convert the field numbers for this class into their metadata for the class
        mmds = new AbstractMemberMetaData[memberNumbersToProcess.length];
        for (int i = 0; i < mmds.length; i++) {
            mmds[i] = cmd.getMetaDataForManagedMemberAtAbsolutePosition(memberNumbersToProcess[i]);
        }
    }
    if (op.isEmbedded()) {
        StringBuilder str = new StringBuilder();
        if (mmds != null) {
            for (int i = 0; i < mmds.length; i++) {
                if (i > 0) {
                    str.append(',');
                }
                str.append(mmds[i].getName());
            }
        }
        NucleusLogger.PERSISTENCE.info("Request to load fields \"" + str.toString() + "\" of class " + op.getClassMetaData().getFullClassName() + " but object is embedded, so ignored");
    } else {
        if (ec.getStatistics() != null) {
            ec.getStatistics().incrementFetchCount();
        }
        DatastoreClass table = getDatastoreClass(op.getClassMetaData().getFullClassName(), clr);
        Request req = getFetchRequest(table, mmds, op.getClassMetaData(), clr);
        req.execute(op);
    }
}
Also used : ClassLoaderResolver(org.datanucleus.ClassLoaderResolver) ArrayList(java.util.ArrayList) LocateRequest(org.datanucleus.store.rdbms.request.LocateRequest) Request(org.datanucleus.store.rdbms.request.Request) FetchRequest(org.datanucleus.store.rdbms.request.FetchRequest) DeleteRequest(org.datanucleus.store.rdbms.request.DeleteRequest) InsertRequest(org.datanucleus.store.rdbms.request.InsertRequest) LocateBulkRequest(org.datanucleus.store.rdbms.request.LocateBulkRequest) UpdateRequest(org.datanucleus.store.rdbms.request.UpdateRequest) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) ExecutionContext(org.datanucleus.ExecutionContext) RelationType(org.datanucleus.metadata.RelationType) SecondaryDatastoreClass(org.datanucleus.store.rdbms.table.SecondaryDatastoreClass) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData)

Aggregations

RelationType (org.datanucleus.metadata.RelationType)41 AbstractMemberMetaData (org.datanucleus.metadata.AbstractMemberMetaData)33 AbstractClassMetaData (org.datanucleus.metadata.AbstractClassMetaData)14 ClassLoaderResolver (org.datanucleus.ClassLoaderResolver)10 NucleusUserException (org.datanucleus.exceptions.NucleusUserException)10 JavaTypeMapping (org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping)10 ArrayList (java.util.ArrayList)9 ExecutionContext (org.datanucleus.ExecutionContext)7 ApiAdapter (org.datanucleus.api.ApiAdapter)7 DatastoreClass (org.datanucleus.store.rdbms.table.DatastoreClass)7 ObjectProvider (org.datanucleus.state.ObjectProvider)6 Collection (java.util.Collection)5 Iterator (java.util.Iterator)5 List (java.util.List)5 NucleusException (org.datanucleus.exceptions.NucleusException)5 HashMap (java.util.HashMap)4 HashSet (java.util.HashSet)4 Map (java.util.Map)4 ColumnMetaData (org.datanucleus.metadata.ColumnMetaData)4 EmbeddedPCMapping (org.datanucleus.store.rdbms.mapping.java.EmbeddedPCMapping)4