Search in sources :

Example 46 with ClassLoaderResolver

use of org.datanucleus.ClassLoaderResolver in project datanucleus-rdbms by datanucleus.

the class DeleteRequest method updateOneToOneBidirectionalOwnerObjectForField.

/**
 * Method to update any 1-1 bidir non-owner fields where the foreign-key is stored in the other object.
 * @param sm StateManager of this object
 * @param mmd MetaData for field that has related (owner) objects
 */
private void updateOneToOneBidirectionalOwnerObjectForField(DNStateManager sm, AbstractMemberMetaData mmd) {
    if (softDeleteStmt != null) {
        // If we are soft deleting the owner then no need to null the linkage BUT we need to check the soft delete status on retrieval of the owner from related
        return;
    }
    if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
        NucleusLogger.PERSISTENCE.debug(Localiser.msg("052217", IdentityUtils.getPersistableIdentityForId(sm.getInternalObjectId()), mmd.getFullFieldName()));
    }
    RDBMSStoreManager storeMgr = table.getStoreManager();
    ExecutionContext ec = sm.getExecutionContext();
    ClassLoaderResolver clr = ec.getClassLoaderResolver();
    AbstractMemberMetaData[] relatedMmds = mmd.getRelatedMemberMetaData(clr);
    // Check if we should null here, or leave to the datastore FK handler
    boolean checkFK = true;
    if (ec.getStringProperty(PropertyNames.PROPERTY_DELETION_POLICY).equals("JDO2")) {
        // JDO2 doesn't currently (2.0 spec) take note of foreign-key
        checkFK = false;
    }
    if (checkFK) {
        for (int i = 0; i < relatedMmds.length; i++) {
            ForeignKeyMetaData relFkmd = relatedMmds[i].getForeignKeyMetaData();
            if (relFkmd != null && relFkmd.getDeleteAction() != null) {
                // Field has a FK with a delete-action so leave to the datastore to process the delete
                return;
            }
        }
    }
    // TODO Cater for more than 1 related field
    String fullClassName = ((AbstractClassMetaData) relatedMmds[0].getParent()).getFullClassName();
    // TODO I'm not sure that we need to loop all implementations. will we have the fk set to all tables, if many?
    String[] classes;
    if (((AbstractClassMetaData) relatedMmds[0].getParent()) instanceof InterfaceMetaData) {
        classes = storeMgr.getNucleusContext().getMetaDataManager().getClassesImplementingInterface(fullClassName, clr);
    } else {
        classes = new String[] { fullClassName };
    }
    Set<DatastoreClass> datastoreClasses = new HashSet();
    for (int i = 0; i < classes.length; i++) {
        // just remove duplicates
        datastoreClasses.add(storeMgr.getDatastoreClass(classes[i], clr));
    }
    Iterator<DatastoreClass> it = datastoreClasses.iterator();
    while (it.hasNext()) {
        DatastoreClass refTable = it.next();
        JavaTypeMapping refMapping = refTable.getMemberMapping(mmd.getMappedBy());
        if (// Only clear the references that can be cleared
        refMapping.isNullable()) {
            // Create a statement to clear the link from the previous related object
            StringBuilder clearLinkStmt = new StringBuilder("UPDATE " + refTable.toString() + " SET ");
            for (int j = 0; j < refMapping.getNumberOfColumnMappings(); j++) {
                if (j > 0) {
                    clearLinkStmt.append(",");
                }
                clearLinkStmt.append(refMapping.getColumnMapping(j).getColumn().getIdentifier());
                clearLinkStmt.append("=NULL");
            }
            clearLinkStmt.append(" WHERE ");
            for (int j = 0; j < refMapping.getNumberOfColumnMappings(); j++) {
                if (j > 0) {
                    clearLinkStmt.append(" AND ");
                }
                clearLinkStmt.append(refMapping.getColumnMapping(j).getColumn().getIdentifier());
                clearLinkStmt.append("=?");
            }
            try {
                ManagedConnection mconn = storeMgr.getConnectionManager().getConnection(ec);
                SQLController sqlControl = storeMgr.getSQLController();
                try {
                    // Null out the relationship to the object being deleted.
                    PreparedStatement ps = null;
                    try {
                        ps = sqlControl.getStatementForUpdate(mconn, clearLinkStmt.toString(), false);
                        refMapping.setObject(ec, ps, MappingHelper.getMappingIndices(1, refMapping), sm.getObject());
                        sqlControl.executeStatementUpdate(ec, mconn, clearLinkStmt.toString(), ps, true);
                    } finally {
                        if (ps != null) {
                            sqlControl.closeStatement(mconn, ps);
                        }
                    }
                } finally {
                    mconn.release();
                }
            } catch (Exception e) {
                throw new NucleusDataStoreException("Update request failed", e);
            }
        }
    }
}
Also used : JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) ClassLoaderResolver(org.datanucleus.ClassLoaderResolver) PreparedStatement(java.sql.PreparedStatement) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) SQLException(java.sql.SQLException) NucleusException(org.datanucleus.exceptions.NucleusException) NucleusDataStoreException(org.datanucleus.exceptions.NucleusDataStoreException) NucleusOptimisticException(org.datanucleus.exceptions.NucleusOptimisticException) RDBMSStoreManager(org.datanucleus.store.rdbms.RDBMSStoreManager) SQLController(org.datanucleus.store.rdbms.SQLController) NucleusDataStoreException(org.datanucleus.exceptions.NucleusDataStoreException) ExecutionContext(org.datanucleus.ExecutionContext) ForeignKeyMetaData(org.datanucleus.metadata.ForeignKeyMetaData) InterfaceMetaData(org.datanucleus.metadata.InterfaceMetaData) ManagedConnection(org.datanucleus.store.connection.ManagedConnection) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData) HashSet(java.util.HashSet)

Example 47 with ClassLoaderResolver

use of org.datanucleus.ClassLoaderResolver in project datanucleus-rdbms by datanucleus.

the class SQLQuery method compileInternal.

/**
 * Verify the elements of the query and provide a hint to the query to prepare and optimize an execution plan.
 */
public void compileInternal(Map parameterValues) {
    if (isCompiled) {
        return;
    }
    // Default to using the users SQL direct with no substitution of params etc
    compiledSQL = inputSQL;
    if (candidateClass != null && getType() == QueryType.SELECT) {
        // Perform any sanity checking of input for SELECT queries
        RDBMSStoreManager storeMgr = (RDBMSStoreManager) getStoreManager();
        ClassLoaderResolver clr = ec.getClassLoaderResolver();
        AbstractClassMetaData cmd = ec.getMetaDataManager().getMetaDataForClass(candidateClass, clr);
        if (cmd == null) {
            throw new ClassNotPersistableException(candidateClass.getName());
        }
        if (cmd.getPersistableSuperclass() != null) {
        // throw new PersistentSuperclassNotAllowedException(candidateClass.getName());
        }
        if (getResultClass() == null) {
            if (getBooleanExtensionProperty(EXTENSION_SQL_SYNTAX_CHECKS, true)) {
                // Check syntax for the presence of the required columns (id, version, discriminator) in the candidate class
                // Skip "SELECT "
                String selections = stripComments(compiledSQL.trim()).substring(7);
                int fromStart = selections.indexOf("FROM");
                if (fromStart == -1) {
                    fromStart = selections.indexOf("from");
                }
                selections = selections.substring(0, fromStart).trim();
                String[] selectedColumns = StringUtils.split(selections, ",");
                if (selectedColumns == null || selectedColumns.length == 0) {
                    throw new NucleusUserException(Localiser.msg("059003", compiledSQL));
                }
                if (selectedColumns.length == 1 && selectedColumns[0].trim().equals("*")) {
                // SQL Query using * so just end the checking since all possible columns will be selected
                } else {
                    // Generate id column field information for later checking the id is present
                    DatastoreClass table = storeMgr.getDatastoreClass(candidateClass.getName(), clr);
                    PersistableMapping idMapping = (PersistableMapping) table.getIdMapping();
                    String[] idColNames = new String[idMapping.getNumberOfColumnMappings()];
                    boolean[] idColMissing = new boolean[idMapping.getNumberOfColumnMappings()];
                    for (int i = 0; i < idMapping.getNumberOfColumnMappings(); i++) {
                        idColNames[i] = idMapping.getColumnMapping(i).getColumn().getIdentifier().toString();
                        idColMissing[i] = true;
                    }
                    // Generate discriminator/version information for later checking they are present
                    JavaTypeMapping discrimMapping = table.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, false);
                    String discriminatorColName = (discrimMapping != null) ? discrimMapping.getColumnMapping(0).getColumn().getIdentifier().toString() : null;
                    JavaTypeMapping versionMapping = table.getSurrogateMapping(SurrogateColumnType.VERSION, false);
                    String versionColName = (versionMapping != null) ? versionMapping.getColumnMapping(0).getColumn().getIdentifier().toString() : null;
                    boolean discrimMissing = (discriminatorColName != null);
                    boolean versionMissing = (versionColName != null);
                    // Go through the selected fields and check the existence of id, version, discriminator cols
                    DatastoreAdapter dba = storeMgr.getDatastoreAdapter();
                    final AbstractClassMetaData candidateCmd = ec.getMetaDataManager().getMetaDataForClass(candidateClass, clr);
                    for (int i = 0; i < selectedColumns.length; i++) {
                        String colName = selectedColumns[i].trim();
                        if (colName.indexOf(" AS ") > 0) {
                            // Allow for user specification of "XX.YY AS ZZ"
                            colName = colName.substring(colName.indexOf(" AS ") + 4).trim();
                        } else if (colName.indexOf(" as ") > 0) {
                            // Allow for user specification of "XX.YY as ZZ"
                            colName = colName.substring(colName.indexOf(" as ") + 4).trim();
                        } else if (colName.indexOf(".") > 0) {
                            // Note that this assumes the alias is the candidate table
                            colName = colName.substring(colName.indexOf(".") + 1);
                        }
                        if (candidateCmd.getIdentityType() == IdentityType.DATASTORE) {
                            // Check for existence of id column, allowing for any RDBMS using quoted identifiers
                            if (SQLQuery.columnNamesAreTheSame(dba, idColNames[0], colName)) {
                                idColMissing[0] = false;
                            }
                        } else if (candidateCmd.getIdentityType() == IdentityType.APPLICATION) {
                            for (int j = 0; j < idColNames.length; j++) {
                                // Check for existence of id column, allowing for any RDBMS using quoted identifiers
                                if (SQLQuery.columnNamesAreTheSame(dba, idColNames[j], colName)) {
                                    idColMissing[j] = false;
                                }
                            }
                        }
                        if (discrimMissing && SQLQuery.columnNamesAreTheSame(dba, discriminatorColName, colName)) {
                            discrimMissing = false;
                        } else if (versionMissing && SQLQuery.columnNamesAreTheSame(dba, versionColName, colName)) {
                            versionMissing = false;
                        }
                    }
                    if (discrimMissing) {
                        throw new NucleusUserException(Localiser.msg("059014", compiledSQL, candidateClass.getName(), discriminatorColName));
                    }
                    if (versionMissing) {
                        throw new NucleusUserException(Localiser.msg("059015", compiledSQL, candidateClass.getName(), versionColName));
                    }
                    for (int i = 0; i < idColMissing.length; i++) {
                        if (idColMissing[i]) {
                            throw new NucleusUserException(Localiser.msg("059013", compiledSQL, candidateClass.getName(), idColNames[i]));
                        }
                    }
                }
            }
        }
    }
    if (NucleusLogger.QUERY.isDebugEnabled()) {
        NucleusLogger.QUERY.debug(Localiser.msg("059012", compiledSQL));
    }
    isCompiled = true;
}
Also used : JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) ClassLoaderResolver(org.datanucleus.ClassLoaderResolver) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) RDBMSStoreManager(org.datanucleus.store.rdbms.RDBMSStoreManager) PersistableMapping(org.datanucleus.store.rdbms.mapping.java.PersistableMapping) DatastoreAdapter(org.datanucleus.store.rdbms.adapter.DatastoreAdapter) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) ClassNotPersistableException(org.datanucleus.exceptions.ClassNotPersistableException)

Example 48 with ClassLoaderResolver

use of org.datanucleus.ClassLoaderResolver in project datanucleus-rdbms by datanucleus.

the class SQLQuery method getResultObjectFactoryForCandidateClass.

/**
 * Method to generate a ResultObjectFactory for converting rows of the provided ResultSet into instances of the candidate class.
 * Populates "stmtMappings".
 * @param rs The ResultSet
 * @return The ResultObjectFactory
 * @throws SQLException Thrown if an error occurs processing the ResultSet
 */
protected ResultObjectFactory getResultObjectFactoryForCandidateClass(ResultSet rs) throws SQLException {
    ClassLoaderResolver clr = ec.getClassLoaderResolver();
    RDBMSStoreManager storeMgr = (RDBMSStoreManager) getStoreManager();
    DatastoreAdapter dba = storeMgr.getDatastoreAdapter();
    // Create an index listing for ALL (fetchable) fields in the result class.
    final AbstractClassMetaData candidateCmd = ec.getMetaDataManager().getMetaDataForClass(candidateClass, clr);
    int fieldCount = candidateCmd.getNoOfManagedMembers() + candidateCmd.getNoOfInheritedManagedMembers();
    // Map of field numbers keyed by the column name
    Map columnFieldNumberMap = new HashMap();
    stmtMappings = new StatementMappingIndex[fieldCount];
    DatastoreClass tbl = storeMgr.getDatastoreClass(candidateClass.getName(), clr);
    for (int fieldNumber = 0; fieldNumber < fieldCount; ++fieldNumber) {
        AbstractMemberMetaData mmd = candidateCmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
        String fieldName = mmd.getName();
        Class fieldType = mmd.getType();
        JavaTypeMapping m = null;
        if (mmd.getPersistenceModifier() != FieldPersistenceModifier.NONE) {
            if (tbl != null) {
                // Get the field mapping from the candidate table
                m = tbl.getMemberMapping(mmd);
            } else {
                // Fall back to generating a mapping for this type - does this ever happen?
                m = storeMgr.getMappingManager().getMappingWithColumnMapping(fieldType, false, false, clr);
            }
            if (m.includeInFetchStatement()) {
                // Set mapping for this field since it can potentially be returned from a fetch
                String columnName = null;
                if (mmd.getColumnMetaData() != null && mmd.getColumnMetaData().length > 0) {
                    for (int colNum = 0; colNum < mmd.getColumnMetaData().length; colNum++) {
                        columnName = mmd.getColumnMetaData()[colNum].getName();
                        columnFieldNumberMap.put(columnName, Integer.valueOf(fieldNumber));
                    }
                } else {
                    columnName = storeMgr.getIdentifierFactory().newColumnIdentifier(fieldName, ec.getNucleusContext().getTypeManager().isDefaultEmbeddedType(fieldType), FieldRole.ROLE_NONE, false).getName();
                    columnFieldNumberMap.put(columnName, Integer.valueOf(fieldNumber));
                }
            } else {
            // Don't put anything in this position (field has no column in the result set)
            }
        } else {
        // Don't put anything in this position (field has no column in the result set)
        }
        stmtMappings[fieldNumber] = new StatementMappingIndex(m);
    }
    if (columnFieldNumberMap.size() == 0) {
        // None of the fields in the class have columns in the datastore table!
        throw new NucleusUserException(Localiser.msg("059030", candidateClass.getName())).setFatal();
    }
    // Generate id column field information for later checking the id is present
    DatastoreClass table = storeMgr.getDatastoreClass(candidateClass.getName(), clr);
    if (table == null) {
        AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(candidateCmd, clr);
        if (cmds != null && cmds.length == 1) {
            table = storeMgr.getDatastoreClass(cmds[0].getFullClassName(), clr);
        } else {
            throw new NucleusUserException("SQL query specified with class " + candidateClass.getName() + " but this doesn't have its own table, or is mapped to multiple tables. Unsupported");
        }
    }
    PersistableMapping idMapping = (PersistableMapping) table.getIdMapping();
    String[] idColNames = new String[idMapping.getNumberOfColumnMappings()];
    for (int i = 0; i < idMapping.getNumberOfColumnMappings(); i++) {
        idColNames[i] = idMapping.getColumnMapping(i).getColumn().getIdentifier().toString();
    }
    // Generate discriminator information for later checking it is present
    JavaTypeMapping discrimMapping = table.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, false);
    String discrimColName = discrimMapping != null ? discrimMapping.getColumnMapping(0).getColumn().getIdentifier().toString() : null;
    // Generate version information for later checking it is present
    JavaTypeMapping versionMapping = table.getSurrogateMapping(SurrogateColumnType.VERSION, false);
    String versionColName = versionMapping != null ? versionMapping.getColumnMapping(0).getColumn().getIdentifier().toString() : null;
    // Go through the fields of the ResultSet and map to the required fields in the candidate
    ResultSetMetaData rsmd = rs.getMetaData();
    int colCount = rsmd.getColumnCount();
    int[] datastoreIndex = null;
    int[] versionIndex = null;
    int[] discrimIndex = null;
    int[] matchedFieldNumbers = new int[colCount];
    int fieldNumberPosition = 0;
    for (int colNum = 1; colNum <= colCount; ++colNum) {
        String colName = rsmd.getColumnName(colNum);
        // Find the field for this column
        int fieldNumber = -1;
        Integer fieldNum = (Integer) columnFieldNumberMap.get(colName);
        if (fieldNum == null) {
            // Try column name in lowercase
            fieldNum = (Integer) columnFieldNumberMap.get(colName.toLowerCase());
            if (fieldNum == null) {
                // Try column name in UPPERCASE
                fieldNum = (Integer) columnFieldNumberMap.get(colName.toUpperCase());
            }
        }
        if (fieldNum != null) {
            fieldNumber = fieldNum.intValue();
        }
        if (fieldNumber >= 0) {
            int[] exprIndices = null;
            if (stmtMappings[fieldNumber].getColumnPositions() != null) {
                exprIndices = new int[stmtMappings[fieldNumber].getColumnPositions().length + 1];
                for (int i = 0; i < stmtMappings[fieldNumber].getColumnPositions().length; i++) {
                    exprIndices[i] = stmtMappings[fieldNumber].getColumnPositions()[i];
                }
                exprIndices[exprIndices.length - 1] = colNum;
            } else {
                exprIndices = new int[] { colNum };
            }
            stmtMappings[fieldNumber].setColumnPositions(exprIndices);
            matchedFieldNumbers[fieldNumberPosition++] = fieldNumber;
        }
        if (discrimColName != null && colName.equals(discrimColName)) {
            // Identify the location of the discriminator column
            discrimIndex = new int[1];
            discrimIndex[0] = colNum;
        }
        if (versionColName != null && colName.equals(versionColName)) {
            // Identify the location of the version column
            versionIndex = new int[1];
            versionIndex[0] = colNum;
        }
        if (candidateCmd.getIdentityType() == IdentityType.DATASTORE) {
            // Check for existence of id column, allowing for any RDBMS using quoted identifiers
            if (columnNamesAreTheSame(dba, idColNames[0], colName)) {
                datastoreIndex = new int[1];
                datastoreIndex[0] = colNum;
            }
        }
    }
    // Set the field numbers found to match what we really have
    int[] fieldNumbers = new int[fieldNumberPosition];
    for (int i = 0; i < fieldNumberPosition; i++) {
        fieldNumbers[i] = matchedFieldNumbers[i];
    }
    StatementClassMapping mappingDefinition = new StatementClassMapping();
    for (int i = 0; i < fieldNumbers.length; i++) {
        mappingDefinition.addMappingForMember(fieldNumbers[i], stmtMappings[fieldNumbers[i]]);
    }
    if (datastoreIndex != null) {
        StatementMappingIndex datastoreMappingIdx = new StatementMappingIndex(table.getSurrogateMapping(SurrogateColumnType.DATASTORE_ID, false));
        datastoreMappingIdx.setColumnPositions(datastoreIndex);
        mappingDefinition.addMappingForMember(SurrogateColumnType.DATASTORE_ID.getFieldNumber(), datastoreMappingIdx);
    }
    if (discrimIndex != null) {
        StatementMappingIndex discrimMappingIdx = new StatementMappingIndex(table.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, true));
        discrimMappingIdx.setColumnPositions(discrimIndex);
        mappingDefinition.addMappingForMember(SurrogateColumnType.DISCRIMINATOR.getFieldNumber(), discrimMappingIdx);
    }
    if (versionIndex != null) {
        StatementMappingIndex versionMappingIdx = new StatementMappingIndex(table.getSurrogateMapping(SurrogateColumnType.VERSION, true));
        versionMappingIdx.setColumnPositions(versionIndex);
        mappingDefinition.addMappingForMember(SurrogateColumnType.VERSION.getFieldNumber(), versionMappingIdx);
    }
    return new PersistentClassROF(ec, rs, ignoreCache, getFetchPlan(), mappingDefinition, candidateCmd, getCandidateClass());
}
Also used : HashMap(java.util.HashMap) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) ClassLoaderResolver(org.datanucleus.ClassLoaderResolver) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) RDBMSStoreManager(org.datanucleus.store.rdbms.RDBMSStoreManager) ResultSetMetaData(java.sql.ResultSetMetaData) PersistableMapping(org.datanucleus.store.rdbms.mapping.java.PersistableMapping) DatastoreAdapter(org.datanucleus.store.rdbms.adapter.DatastoreAdapter) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) HashMap(java.util.HashMap) Map(java.util.Map) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData)

Example 49 with ClassLoaderResolver

use of org.datanucleus.ClassLoaderResolver in project datanucleus-rdbms by datanucleus.

the class DynamicSchemaFieldManager method storeObjectField.

/**
 * Method to store an object field into the attached instance.
 * @param fieldNumber Number of the field to store
 * @param value the value in the detached instance
 */
public void storeObjectField(int fieldNumber, Object value) {
    if (value == null) {
        // No value so nothing to do
        return;
    }
    ExecutionContext ec = sm.getExecutionContext();
    ClassLoaderResolver clr = ec.getClassLoaderResolver();
    AbstractMemberMetaData mmd = sm.getClassMetaData().getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
    if (mmd != null) {
        DatastoreClass table = rdbmsMgr.getDatastoreClass(sm.getObject().getClass().getName(), clr);
        JavaTypeMapping fieldMapping = table.getMemberMapping(mmd);
        if (fieldMapping != null) {
            if (fieldMapping instanceof InterfaceMapping) {
                // 1-1 Interface field
                InterfaceMapping intfMapping = (InterfaceMapping) fieldMapping;
                if (mmd.getFieldTypes() != null || mmd.hasExtension(MetaData.EXTENSION_MEMBER_IMPLEMENTATION_CLASSES)) {
                    // Field is defined to not accept this type so just return
                    return;
                }
                processInterfaceMappingForValue(intfMapping, value, mmd, ec);
            } else if (mmd.hasCollection() || mmd.hasArray()) {
                boolean hasJoin = false;
                if (mmd.getJoinMetaData() != null) {
                    hasJoin = true;
                } else {
                    AbstractMemberMetaData[] relMmds = mmd.getRelatedMemberMetaData(clr);
                    if (relMmds != null && relMmds[0].getJoinMetaData() != null) {
                        hasJoin = true;
                    }
                }
                if (!hasJoin) {
                    // Not join table so no supported schema updates
                    return;
                }
                Table joinTbl = fieldMapping.getStoreManager().getTable(mmd);
                ElementContainerTable collTbl = (ElementContainerTable) joinTbl;
                JavaTypeMapping elemMapping = collTbl.getElementMapping();
                if (elemMapping instanceof InterfaceMapping) {
                    InterfaceMapping intfMapping = (InterfaceMapping) elemMapping;
                    if (mmd.hasCollection()) {
                        Collection coll = (Collection) value;
                        if (coll.isEmpty()) {
                            return;
                        }
                        // Update value mapping using first element. Maybe we should do the same for all elements?
                        Object elementValue = coll.iterator().next();
                        processInterfaceMappingForValue(intfMapping, elementValue, mmd, ec);
                    } else if (mmd.hasArray()) {
                        if (Array.getLength(value) == 0) {
                            return;
                        }
                        // Update value mapping using first element. Maybe we should do the same for all elements?
                        Object elementValue = Array.get(value, 0);
                        processInterfaceMappingForValue(intfMapping, elementValue, mmd, ec);
                    }
                }
            } else if (mmd.hasMap()) {
                boolean hasJoin = false;
                if (mmd.getJoinMetaData() != null) {
                    hasJoin = true;
                } else {
                    AbstractMemberMetaData[] relMmds = mmd.getRelatedMemberMetaData(clr);
                    if (relMmds != null && relMmds[0].getJoinMetaData() != null) {
                        hasJoin = true;
                    }
                }
                if (!hasJoin) {
                    // Not join table so no supported schema updates
                    return;
                }
                Map map = (Map) value;
                if (map.isEmpty()) {
                    return;
                }
                Table joinTbl = fieldMapping.getStoreManager().getTable(mmd);
                MapTable mapTbl = (MapTable) joinTbl;
                JavaTypeMapping keyMapping = mapTbl.getKeyMapping();
                if (keyMapping instanceof InterfaceMapping) {
                    // Update key mapping using first key. Maybe we should do the same for all keys?
                    InterfaceMapping intfMapping = (InterfaceMapping) keyMapping;
                    Object keyValue = map.keySet().iterator().next();
                    processInterfaceMappingForValue(intfMapping, keyValue, mmd, ec);
                }
                JavaTypeMapping valMapping = mapTbl.getValueMapping();
                if (valMapping instanceof InterfaceMapping) {
                    // Update value mapping using first value. Maybe we should do the same for all values?
                    InterfaceMapping intfMapping = (InterfaceMapping) valMapping;
                    Object valValue = map.values().iterator().next();
                    processInterfaceMappingForValue(intfMapping, valValue, mmd, ec);
                }
            }
        }
    }
}
Also used : ElementContainerTable(org.datanucleus.store.rdbms.table.ElementContainerTable) Table(org.datanucleus.store.rdbms.table.Table) MapTable(org.datanucleus.store.rdbms.table.MapTable) InterfaceMapping(org.datanucleus.store.rdbms.mapping.java.InterfaceMapping) JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) ClassLoaderResolver(org.datanucleus.ClassLoaderResolver) MapTable(org.datanucleus.store.rdbms.table.MapTable) ExecutionContext(org.datanucleus.ExecutionContext) Collection(java.util.Collection) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) AbstractMemberMetaData(org.datanucleus.metadata.AbstractMemberMetaData) Map(java.util.Map) ElementContainerTable(org.datanucleus.store.rdbms.table.ElementContainerTable)

Example 50 with ClassLoaderResolver

use of org.datanucleus.ClassLoaderResolver in project datanucleus-rdbms by datanucleus.

the class ObjectExpression method is.

/**
 * An "is" (instanceOf) expression, providing a BooleanExpression whether this expression is an instanceof the provided type.
 * @param expr The expression representing the type
 * @param not Whether the operator is "!instanceof"
 * @return Whether this expression is an instance of the provided type
 */
public BooleanExpression is(SQLExpression expr, boolean not) {
    RDBMSStoreManager storeMgr = stmt.getRDBMSManager();
    ClassLoaderResolver clr = stmt.getClassLoaderResolver();
    // Extract instanceOf type
    String instanceofClassName = null;
    SQLExpression classExpr = expr;
    if (expr instanceof TypeConverterLiteral) {
        // For some reason the user has input "TYPE(field) = :param" and param is a type-converted value
        classExpr = ((TypeConverterLiteral) expr).getDelegate();
    }
    if (classExpr instanceof StringLiteral) {
        instanceofClassName = (String) ((StringLiteral) classExpr).getValue();
    } else if (classExpr instanceof CollectionLiteral) {
        // "a instanceof (b1,b2,b3)"
        CollectionLiteral typesLiteral = (CollectionLiteral) classExpr;
        Collection values = (Collection) typesLiteral.getValue();
        if (values.size() == 1) {
            Object value = values.iterator().next();
            String valueStr = null;
            if (value instanceof Class) {
                valueStr = ((Class) value).getCanonicalName();
            } else if (value instanceof String) {
                valueStr = (String) value;
            } else {
                throw new NucleusUserException("Do not support CollectionLiteral of element type " + value.getClass().getName());
            }
            return is(new StringLiteral(stmt, typesLiteral.getJavaTypeMapping(), valueStr, typesLiteral.getParameterName()), not);
        }
        List<BooleanExpression> listExp = new LinkedList<>();
        for (Object value : values) {
            String valueStr = null;
            if (value instanceof Class) {
                valueStr = ((Class) value).getCanonicalName();
            } else if (value instanceof String) {
                valueStr = (String) value;
            } else {
                throw new NucleusUserException("Do not support CollectionLiteral of element type " + value.getClass().getName());
            }
            listExp.add(is(new StringLiteral(stmt, typesLiteral.getJavaTypeMapping(), valueStr, typesLiteral.getParameterName()), false));
        }
        BooleanExpression result = null;
        for (BooleanExpression sqlExpression : listExp) {
            result = result == null ? sqlExpression : result.ior(sqlExpression);
        }
        if (result != null) {
            return not ? result.not() : result;
        }
    } else {
        throw new NucleusUserException("Do not currently support `instanceof` with class expression of type " + classExpr);
    }
    Class type = null;
    try {
        type = stmt.getQueryGenerator().resolveClass(instanceofClassName);
    } catch (ClassNotResolvedException cnre) {
        type = null;
    }
    if (type == null) {
        throw new NucleusUserException(Localiser.msg("037016", instanceofClassName));
    }
    // Extract type of member and check obvious conditions
    SQLExpressionFactory exprFactory = stmt.getSQLExpressionFactory();
    Class memberType = clr.classForName(mapping.getType());
    if (!memberType.isAssignableFrom(type) && !type.isAssignableFrom(memberType)) {
        // Member type and instanceof type are totally incompatible, so just return false
        JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
        return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, not));
    } else if (memberType == type) {
        // instanceof type is the same as the member type therefore must comply (can't store supertypes)
        JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
        return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, !not));
    }
    if (mapping instanceof EmbeddedMapping) {
        // Don't support embedded instanceof expressions
        AbstractClassMetaData fieldCmd = storeMgr.getMetaDataManager().getMetaDataForClass(mapping.getType(), clr);
        if (fieldCmd.hasDiscriminatorStrategy()) {
            // Embedded field with inheritance so add discriminator restriction
            JavaTypeMapping discMapping = ((EmbeddedMapping) mapping).getDiscriminatorMapping();
            AbstractClassMetaData typeCmd = storeMgr.getMetaDataManager().getMetaDataForClass(type, clr);
            SQLExpression discExpr = stmt.getSQLExpressionFactory().newExpression(stmt, table, discMapping);
            SQLExpression discValExpr = stmt.getSQLExpressionFactory().newLiteral(stmt, discMapping, typeCmd.getDiscriminatorValue());
            BooleanExpression typeExpr = (not ? discExpr.ne(discValExpr) : discExpr.eq(discValExpr));
            Iterator<String> subclassIter = storeMgr.getSubClassesForClass(type.getName(), true, clr).iterator();
            while (subclassIter.hasNext()) {
                String subclassName = subclassIter.next();
                AbstractClassMetaData subtypeCmd = storeMgr.getMetaDataManager().getMetaDataForClass(subclassName, clr);
                Object subtypeDiscVal = subtypeCmd.getDiscriminatorValue();
                discValExpr = stmt.getSQLExpressionFactory().newLiteral(stmt, discMapping, subtypeDiscVal);
                BooleanExpression subtypeExpr = (not ? discExpr.ne(discValExpr) : discExpr.eq(discValExpr));
                if (not) {
                    typeExpr = typeExpr.and(subtypeExpr);
                } else {
                    typeExpr = typeExpr.ior(subtypeExpr);
                }
            }
            return typeExpr;
        }
        JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
        return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, not));
    } else if (mapping instanceof PersistableMapping || mapping instanceof ReferenceMapping) {
        // Field has its own table, so join to it
        AbstractClassMetaData memberCmd = storeMgr.getMetaDataManager().getMetaDataForClass(mapping.getType(), clr);
        DatastoreClass memberTable = null;
        if (memberCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE) {
            // Field is a PC class that uses "subclass-table" inheritance strategy (and so has multiple possible tables to join to)
            AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(memberCmd, clr);
            if (cmds != null) {
                // TODO Allow for all possible tables. Can we do an OR of the tables ? How ?
                if (cmds.length > 1) {
                    NucleusLogger.QUERY.warn(Localiser.msg("037006", mapping.getMemberMetaData().getFullFieldName(), cmds[0].getFullClassName()));
                }
                memberTable = storeMgr.getDatastoreClass(cmds[0].getFullClassName(), clr);
            } else {
                // No subclasses with tables to join to, so throw a user error
                throw new NucleusUserException(Localiser.msg("037005", mapping.getMemberMetaData().getFullFieldName()));
            }
        } else {
            // Class of the field will have its own table
            memberTable = storeMgr.getDatastoreClass(mapping.getType(), clr);
        }
        DiscriminatorMetaData dismd = memberTable.getDiscriminatorMetaData();
        DiscriminatorMapping discMapping = (DiscriminatorMapping) memberTable.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, false);
        if (discMapping != null) {
            SQLTable targetSqlTbl = null;
            if (mapping.getTable() != memberTable) {
                // FK is on source table so inner join to target table (holding the discriminator)
                targetSqlTbl = stmt.getTable(memberTable, null);
                if (targetSqlTbl == null) {
                    targetSqlTbl = stmt.join(JoinType.INNER_JOIN, getSQLTable(), mapping, memberTable, null, memberTable.getIdMapping(), null, null);
                }
            } else {
                // FK is on target side and already joined
                targetSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(stmt, getSQLTable(), discMapping);
            }
            // Add restrict to discriminator for the instanceOf type and subclasses
            SQLTable discSqlTbl = targetSqlTbl;
            BooleanExpression discExpr = null;
            if (!Modifier.isAbstract(type.getModifiers())) {
                discExpr = SQLStatementHelper.getExpressionForDiscriminatorForClass(stmt, type.getName(), dismd, discMapping, discSqlTbl, clr);
            }
            Iterator<String> subclassIter = storeMgr.getSubClassesForClass(type.getName(), true, clr).iterator();
            boolean multiplePossibles = false;
            while (subclassIter.hasNext()) {
                String subclassName = subclassIter.next();
                Class subclass = clr.classForName(subclassName);
                if (Modifier.isAbstract(subclass.getModifiers())) {
                    continue;
                }
                BooleanExpression discExprSub = SQLStatementHelper.getExpressionForDiscriminatorForClass(stmt, subclassName, dismd, discMapping, discSqlTbl, clr);
                if (discExpr != null) {
                    multiplePossibles = true;
                    discExpr = discExpr.ior(discExprSub);
                } else {
                    discExpr = discExprSub;
                }
            }
            if (multiplePossibles && discExpr != null) {
                discExpr.encloseInParentheses();
            }
            return ((not && discExpr != null) ? discExpr.not() : discExpr);
        }
        // No discriminator, so the following is likely incomplete.
        // Join to member table
        DatastoreClass table = null;
        if (memberCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE) {
            // Field is a PC class that uses "subclass-table" inheritance strategy (and so has multiple possible tables to join to)
            AbstractClassMetaData[] cmds = storeMgr.getClassesManagingTableForClass(memberCmd, clr);
            if (cmds != null) {
                // TODO Allow for all possible tables. Can we do an OR of the tables ? How ?
                if (cmds.length > 1) {
                    NucleusLogger.QUERY.warn(Localiser.msg("037006", mapping.getMemberMetaData().getFullFieldName(), cmds[0].getFullClassName()));
                }
                table = storeMgr.getDatastoreClass(cmds[0].getFullClassName(), clr);
            } else {
                // No subclasses with tables to join to, so throw a user error
                throw new NucleusUserException(Localiser.msg("037005", mapping.getMemberMetaData().getFullFieldName()));
            }
        } else {
            // Class of the field will have its own table
            table = storeMgr.getDatastoreClass(mapping.getType(), clr);
        }
        if (table.managesClass(type.getName())) {
            // This type is managed in this table so must be an instance TODO Is this correct, what if member is using discrim?
            JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
            return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, !not));
        }
        if (table == stmt.getPrimaryTable().getTable()) {
            // This is member table, so just need to restrict to the instanceof type now
            JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
            if (stmt instanceof SelectStatement) {
                SelectStatement selectStmt = (SelectStatement) stmt;
                if (selectStmt.getNumberOfUnions() == 0) {
                    // No UNIONs so just check the main statement and return according to whether it is allowed
                    Class mainCandidateCls = clr.classForName(stmt.getCandidateClassName());
                    if (type.isAssignableFrom(mainCandidateCls) == not) {
                        SQLExpression returnExpr = exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, false));
                        return (BooleanExpression) returnExpr;
                    }
                    SQLExpression returnExpr = exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, true));
                    return (BooleanExpression) returnExpr;
                }
                NucleusLogger.QUERY.warn("TYPE/INSTANCEOF operator for class=" + memberCmd.getFullClassName() + " on table=" + memberTable + " for type=" + instanceofClassName + " but there is no discriminator and using UNIONs. Any subsequent handling is likely incorrect TODO");
                // a). we have unions for the member, so restrict to just the applicable unions
                // Note that this is only really valid is wanting "a instanceof SUB1".
                // It fails when we want to do "a instanceof SUB1 || a instanceof SUB2"
                // TODO What if this "OP_IS" is in the SELECT clause??? Need to update QueryToSQLMapper.compileResult
                Class mainCandidateCls = clr.classForName(stmt.getCandidateClassName());
                if (type.isAssignableFrom(mainCandidateCls) == not) {
                    SQLExpression unionClauseExpr = exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, false));
                    stmt.whereAnd((BooleanExpression) unionClauseExpr, false);
                }
                List<SelectStatement> unionStmts = selectStmt.getUnions();
                for (SelectStatement unionStmt : unionStmts) {
                    Class unionCandidateCls = clr.classForName(unionStmt.getCandidateClassName());
                    if (type.isAssignableFrom(unionCandidateCls) == not) {
                        SQLExpression unionClauseExpr = exprFactory.newLiteral(unionStmt, m, true).eq(exprFactory.newLiteral(unionStmt, m, false));
                        // TODO Avoid using whereAnd
                        unionStmt.whereAnd((BooleanExpression) unionClauseExpr, false);
                    }
                }
                // Just return true since we applied the condition direct to the unions
                SQLExpression returnExpr = exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, true));
                return (BooleanExpression) returnExpr;
            }
            // b). The member table doesn't manage the instanceof type, so do inner join to
            // the table of the instanceof to impose the instanceof condition
            DatastoreClass instanceofTable = storeMgr.getDatastoreClass(type.getName(), clr);
            stmt.join(JoinType.INNER_JOIN, this.table, this.table.getTable().getIdMapping(), instanceofTable, null, instanceofTable.getIdMapping(), null, this.table.getGroupName());
            return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, !not));
        }
        // Do inner join to this table to impose the instanceOf
        DatastoreClass instanceofTable = storeMgr.getDatastoreClass(type.getName(), clr);
        stmt.join(JoinType.INNER_JOIN, this.table, this.table.getTable().getIdMapping(), instanceofTable, null, instanceofTable.getIdMapping(), null, this.table.getGroupName());
        JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
        return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, !not));
    } else {
        // TODO Implement instanceof for other types
        throw new NucleusException("Dont currently support " + this + " instanceof " + type.getName());
    }
}
Also used : JavaTypeMapping(org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping) AbstractClassMetaData(org.datanucleus.metadata.AbstractClassMetaData) SelectStatement(org.datanucleus.store.rdbms.sql.SelectStatement) ReferenceMapping(org.datanucleus.store.rdbms.mapping.java.ReferenceMapping) SQLTable(org.datanucleus.store.rdbms.sql.SQLTable) Iterator(java.util.Iterator) LinkedList(java.util.LinkedList) List(java.util.List) DiscriminatorMapping(org.datanucleus.store.rdbms.mapping.java.DiscriminatorMapping) NucleusUserException(org.datanucleus.exceptions.NucleusUserException) ClassLoaderResolver(org.datanucleus.ClassLoaderResolver) ClassNotResolvedException(org.datanucleus.exceptions.ClassNotResolvedException) RDBMSStoreManager(org.datanucleus.store.rdbms.RDBMSStoreManager) DiscriminatorMetaData(org.datanucleus.metadata.DiscriminatorMetaData) PersistableMapping(org.datanucleus.store.rdbms.mapping.java.PersistableMapping) EmbeddedMapping(org.datanucleus.store.rdbms.mapping.java.EmbeddedMapping) Collection(java.util.Collection) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) DatastoreClass(org.datanucleus.store.rdbms.table.DatastoreClass) NucleusException(org.datanucleus.exceptions.NucleusException)

Aggregations

ClassLoaderResolver (org.datanucleus.ClassLoaderResolver)242 AbstractMemberMetaData (org.datanucleus.metadata.AbstractMemberMetaData)94 MetaDataManager (org.datanucleus.metadata.MetaDataManager)72 NucleusContext (org.datanucleus.NucleusContext)68 AbstractClassMetaData (org.datanucleus.metadata.AbstractClassMetaData)65 DatastoreClass (org.datanucleus.store.rdbms.table.DatastoreClass)65 PersistenceNucleusContextImpl (org.datanucleus.PersistenceNucleusContextImpl)56 JavaTypeMapping (org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping)56 ClassMetaData (org.datanucleus.metadata.ClassMetaData)54 JPAMetaDataManager (org.datanucleus.api.jpa.metadata.JPAMetaDataManager)51 RDBMSStoreManager (org.datanucleus.store.rdbms.RDBMSStoreManager)44 NucleusException (org.datanucleus.exceptions.NucleusException)42 PersistenceUnitMetaData (org.datanucleus.metadata.PersistenceUnitMetaData)40 SQLExpressionFactory (org.datanucleus.store.rdbms.sql.expression.SQLExpressionFactory)40 SQLExpression (org.datanucleus.store.rdbms.sql.expression.SQLExpression)39 NucleusUserException (org.datanucleus.exceptions.NucleusUserException)37 ArrayList (java.util.ArrayList)36 ExecutionContext (org.datanucleus.ExecutionContext)32 SelectStatement (org.datanucleus.store.rdbms.sql.SelectStatement)30 HashMap (java.util.HashMap)28