/** * Convenience logging method to output the mapping information for an element, key, value field * * @param mapping The mapping */ protected void debugMapping(JavaTypeMapping mapping) { if (NucleusLogger.DATASTORE.isDebugEnabled()) { // Provide field->column mapping debug message StringBuffer columnsStr = new StringBuffer(); for (int i = 0; i < mapping.getNumberOfDatastoreMappings(); i++) { if (i > 0) { columnsStr.append(","); } columnsStr.append(mapping.getDatastoreMapping(i).getDatastoreField()); } if (mapping.getNumberOfDatastoreMappings() == 0) { columnsStr.append("[none]"); } StringBuffer datastoreMappingTypes = new StringBuffer(); for (int i = 0; i < mapping.getNumberOfDatastoreMappings(); i++) { if (i > 0) { datastoreMappingTypes.append(','); } datastoreMappingTypes.append(mapping.getDatastoreMapping(i).getClass().getName()); } NucleusLogger.DATASTORE.debug( LOCALISER.msg( "057010", mmd.getFullFieldName(), columnsStr.toString(), mapping.getClass().getName(), datastoreMappingTypes.toString())); } }
/** * Convenience method to create a new PreparedStatement for a query. * * @param conn The Connection to use for the statement * @param stmtText Statement text * @param resultSetType Type of result set * @param resultSetConcurrency Concurrency for the result set * @return The PreparedStatement * @throws SQLException thrown if an error occurs creating the statement */ public PreparedStatement getStatementForQuery( ManagedConnection conn, String stmtText, String resultSetType, String resultSetConcurrency) throws SQLException { Connection c = (Connection) conn.getConnection(); if (supportsBatching) { // Check for a waiting batched statement that is ready for processing ConnectionStatementState state = getConnectionStatementState(conn); if (state != null && state.processable) { // Process the batch statement before returning our new query statement processConnectionStatement(conn); } } // Create a new PreparedStatement for this query PreparedStatement ps = null; if (resultSetType != null || resultSetConcurrency != null) { int rsTypeValue = ResultSet.TYPE_FORWARD_ONLY; if (resultSetType != null) { if (resultSetType.equals("scroll-sensitive")) { rsTypeValue = ResultSet.TYPE_SCROLL_SENSITIVE; } else if (resultSetType.equals("scroll-insensitive")) { rsTypeValue = ResultSet.TYPE_SCROLL_INSENSITIVE; } } int rsConcurrencyValue = ResultSet.CONCUR_READ_ONLY; if (resultSetConcurrency != null && resultSetConcurrency.equals("updateable")) { rsConcurrencyValue = ResultSet.CONCUR_UPDATABLE; } ps = c.prepareStatement(stmtText, rsTypeValue, rsConcurrencyValue); ps .clearBatch(); // In case using statement caching and given one with batched statements // left hanging (C3P0) } else { ps = c.prepareStatement(stmtText); ps .clearBatch(); // In case using statement caching and given one with batched statements // left hanging (C3P0) } if (queryTimeout > 0) { // Apply any query timeout ps.setQueryTimeout(queryTimeout / 1000); // queryTimeout is in milliseconds } if (NucleusLogger.DATASTORE.isDebugEnabled()) { NucleusLogger.DATASTORE.debug(LOCALISER.msg("052110", StringUtils.toJVMIDString(ps))); } if (!jdbcStatements) { // Wrap with our parameter logger ps = new ParamLoggingPreparedStatement(ps, stmtText); ((ParamLoggingPreparedStatement) ps).setParamsInAngleBrackets(paramValuesInBrackets); } return ps; }
/** Convenience method to log the configuration of this store manager. */ protected void logConfiguration() { super.logConfiguration(); if (NucleusLogger.DATASTORE.isDebugEnabled()) { NucleusLogger.DATASTORE.debug("StorageVersion : " + storageVersion.toString()); NucleusLogger.DATASTORE.debug( "Default Relation Mode : " + getStringProperty(RELATION_DEFAULT_MODE)); NucleusLogger.DATASTORE.debug("==========================================================="); } }
/** * Convenience method to close a PreparedStatement. If the statement is currently being used as a * batch, will register it for closing when executing the batch * * @param conn The Connection * @param ps The PreparedStatement * @throws SQLException if an error occurs closing the statement */ public void closeStatement(ManagedConnection conn, PreparedStatement ps) throws SQLException { ConnectionStatementState state = getConnectionStatementState(conn); if (state != null && state.stmt == ps) { // Statement to be closed is the current batch, so register it for closing when it gets // processed state.closeStatementOnProcess = true; } else { try { if (NucleusLogger.DATASTORE.isDebugEnabled()) { NucleusLogger.DATASTORE.debug(LOCALISER.msg("052110", StringUtils.toJVMIDString(ps))); } ps.close(); } catch (SQLException sqle) { // workaround for DBCP bug: even though PreparedStatement.close() // is defined as having no effect if already closed, DBCP // will throw SQLException if (!sqle.getMessage().equals("Already closed")) { throw sqle; } } } }
/** * Convenience method to process the currently waiting statement for the passed Connection. Only * processes the statement if it is in processable state. * * @param conn The connection * @return The return codes from the statement batch * @throws SQLException if an error occurs processing the batch */ protected int[] processConnectionStatement(ManagedConnection conn) throws SQLException { ConnectionStatementState state = getConnectionStatementState(conn); if (state == null || !state.processable) { return null; } long startTime = System.currentTimeMillis(); if (NucleusLogger.DATASTORE_NATIVE.isDebugEnabled()) { if (state.stmt instanceof ParamLoggingPreparedStatement) { NucleusLogger.DATASTORE_NATIVE.debug( ((ParamLoggingPreparedStatement) state.stmt).getStatementWithParamsReplaced()); } else { NucleusLogger.DATASTORE_NATIVE.debug(state.stmtText); } } int[] ind = state.stmt.executeBatch(); state.stmt.clearBatch(); if (NucleusLogger.DATASTORE.isDebugEnabled()) { NucleusLogger.DATASTORE.debug( LOCALISER.msg( "045001", "" + (System.currentTimeMillis() - startTime), StringUtils.intArrayToString(ind), StringUtils.toJVMIDString(state.stmt))); } // Remove the current connection statement removeConnectionStatementState(conn); // Close the statement if it is registered for closing after processing if (state.closeStatementOnProcess) { state.stmt.close(); } return ind; }
/** * Method to initialise the table definition. * * @param clr The ClassLoaderResolver */ public void initialize(ClassLoaderResolver clr) { assertIsUninitialized(); MapMetaData mapmd = mmd.getMap(); if (mapmd == null) { throw new NucleusUserException(Localiser.msg("057017", mmd)); } PrimaryKeyMetaData pkmd = (mmd.getJoinMetaData() != null ? mmd.getJoinMetaData().getPrimaryKeyMetaData() : null); boolean pkColsSpecified = (pkmd != null && pkmd.getColumnMetaData() != null); boolean pkRequired = requiresPrimaryKey(); // Add owner mapping ColumnMetaData[] ownerColmd = null; if (mmd.getJoinMetaData() != null && mmd.getJoinMetaData().getColumnMetaData() != null && mmd.getJoinMetaData().getColumnMetaData().length > 0) { // Column mappings defined at this side (1-N, M-N) // When specified at this side they use the <join> tag ownerColmd = mmd.getJoinMetaData().getColumnMetaData(); } ownerMapping = ColumnCreator.createColumnsForJoinTables( clr.classForName(ownerType), mmd, ownerColmd, storeMgr, this, pkRequired, false, FieldRole.ROLE_OWNER, clr); if (NucleusLogger.DATASTORE.isDebugEnabled()) { logMapping(mmd.getFullFieldName() + ".[OWNER]", ownerMapping); } String keyValueFieldName = (mmd.getKeyMetaData() != null ? mmd.getKeyMetaData().getMappedBy() : null); String valueKeyFieldName = (mmd.getValueMetaData() != null ? mmd.getValueMetaData().getMappedBy() : null); // Add key mapping boolean keyPC = (mmd.hasMap() && mmd.getMap().keyIsPersistent()); Class keyCls = clr.classForName(mapmd.getKeyType()); if (keyValueFieldName != null && isEmbeddedValuePC()) { // Added in value code } else if (isSerialisedKey() || isEmbeddedKeyPC() || (isEmbeddedKey() && !keyPC) || ClassUtils.isReferenceType(keyCls)) { // Key = PC(embedded), PC(serialised), Non-PC(serialised), Non-PC(embedded), Reference keyMapping = storeMgr.getMappingManager().getMapping(this, mmd, clr, FieldRole.ROLE_MAP_KEY); if (Boolean.TRUE.equals(mmd.getContainer().allowNulls())) { // Make all key col(s) nullable so we can store null elements for (int i = 0; i < keyMapping.getNumberOfDatastoreMappings(); i++) { Column elementCol = keyMapping.getDatastoreMapping(i).getColumn(); elementCol.setNullable(true); } } if (NucleusLogger.DATASTORE.isDebugEnabled()) { logMapping(mmd.getFullFieldName() + ".[KEY]", keyMapping); } if (valueKeyFieldName != null && isEmbeddedKeyPC()) { // Key (PC) is embedded and value is a field of the key EmbeddedKeyPCMapping embMapping = (EmbeddedKeyPCMapping) keyMapping; valueMapping = embMapping.getJavaTypeMapping(valueKeyFieldName); } } else { // Key = PC ColumnMetaData[] keyColmd = null; KeyMetaData keymd = mmd.getKeyMetaData(); if (keymd != null && keymd.getColumnMetaData() != null && keymd.getColumnMetaData().length > 0) { // Column mappings defined at this side (1-N, M-N) keyColmd = keymd.getColumnMetaData(); } keyMapping = ColumnCreator.createColumnsForJoinTables( keyCls, mmd, keyColmd, storeMgr, this, false, false, FieldRole.ROLE_MAP_KEY, clr); if (mmd.getContainer().allowNulls() == Boolean.TRUE) { // Make all key col(s) nullable so we can store null elements for (int i = 0; i < keyMapping.getNumberOfDatastoreMappings(); i++) { Column elementCol = keyMapping.getDatastoreMapping(i).getColumn(); elementCol.setNullable(true); } } if (NucleusLogger.DATASTORE.isDebugEnabled()) { logMapping(mmd.getFullFieldName() + ".[KEY]", keyMapping); } } // Add value mapping boolean valuePC = (mmd.hasMap() && mmd.getMap().valueIsPersistent()); Class valueCls = clr.classForName(mapmd.getValueType()); if (valueKeyFieldName != null && isEmbeddedKeyPC()) { // Added in key code } else if (isSerialisedValue() || isEmbeddedValuePC() || (isEmbeddedValue() && !valuePC) || ClassUtils.isReferenceType(valueCls)) { // Value = PC(embedded), PC(serialised), Non-PC(serialised), Non-PC(embedded), Reference valueMapping = storeMgr.getMappingManager().getMapping(this, mmd, clr, FieldRole.ROLE_MAP_VALUE); if (mmd.getContainer().allowNulls() == Boolean.TRUE) { // Make all value col(s) nullable so we can store null elements for (int i = 0; i < valueMapping.getNumberOfDatastoreMappings(); i++) { Column elementCol = valueMapping.getDatastoreMapping(i).getColumn(); elementCol.setNullable(true); } } if (NucleusLogger.DATASTORE.isDebugEnabled()) { logMapping(mmd.getFullFieldName() + ".[VALUE]", valueMapping); } if (keyValueFieldName != null && isEmbeddedValuePC()) { // Value (PC) is embedded and key is a field of the value EmbeddedValuePCMapping embMapping = (EmbeddedValuePCMapping) valueMapping; keyMapping = embMapping.getJavaTypeMapping(keyValueFieldName); } } else { // Value = PC ColumnMetaData[] valueColmd = null; ValueMetaData valuemd = mmd.getValueMetaData(); if (valuemd != null && valuemd.getColumnMetaData() != null && valuemd.getColumnMetaData().length > 0) { // Column mappings defined at this side (1-N, M-N) valueColmd = valuemd.getColumnMetaData(); } valueMapping = ColumnCreator.createColumnsForJoinTables( clr.classForName(mapmd.getValueType()), mmd, valueColmd, storeMgr, this, false, true, FieldRole.ROLE_MAP_VALUE, clr); if (mmd.getContainer().allowNulls() == Boolean.TRUE) { // Make all value col(s) nullable so we can store null elements for (int i = 0; i < valueMapping.getNumberOfDatastoreMappings(); i++) { Column elementCol = valueMapping.getDatastoreMapping(i).getColumn(); elementCol.setNullable(true); } } if (NucleusLogger.DATASTORE.isDebugEnabled()) { logMapping(mmd.getFullFieldName() + ".[VALUE]", valueMapping); } } // Add order mapping if required boolean orderRequired = false; if (mmd.getOrderMetaData() != null) { // User requested order column so add one orderRequired = true; } else if (requiresPrimaryKey() && !pkColsSpecified) { // PK is required so maybe need to add an index to form the PK if (isEmbeddedKeyPC()) { if (mmd.getMap().getKeyClassMetaData(clr, storeMgr.getMetaDataManager()).getIdentityType() != IdentityType.APPLICATION) { // Embedded key PC with datastore id so we need an index to form the PK orderRequired = true; } } else if (isSerialisedKey()) { // Serialised key, so need an index to form the PK orderRequired = true; } else if (keyMapping instanceof ReferenceMapping) { // ReferenceMapping, so have order if more than 1 implementation ReferenceMapping refMapping = (ReferenceMapping) keyMapping; if (refMapping.getJavaTypeMapping().length > 1) { orderRequired = true; } } else if (!(keyMapping instanceof PersistableMapping)) { // Non-PC, so depends if the key column can be used as part of a PK // TODO This assumes the keyMapping has a single column but what if it is Color with 4 cols? Column elementCol = keyMapping.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/Adapter (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()); } orderMapping = storeMgr .getMappingManager() .getMapping(int.class); // JDO2 spec [18.5] order column is assumed to be "int" 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 using internal rules if (orderRequired) { // Order column specified so owner+order are the PK orderMapping.getDatastoreMapping(0).getColumn().setPrimaryKey(); } else { // No order column specified so owner+key are the PK for (int i = 0; i < keyMapping.getNumberOfDatastoreMappings(); i++) { keyMapping.getDatastoreMapping(i).getColumn().setPrimaryKey(); } } } } if (NucleusLogger.DATASTORE_SCHEMA.isDebugEnabled()) { NucleusLogger.DATASTORE_SCHEMA.debug(Localiser.msg("057023", this)); } storeMgr.registerTableInitialized(this); state = TABLE_STATE_INITIALIZED; }
/** * Convenience method to create a new PreparedStatement for an update. * * @param conn The Connection to use for the statement * @param stmtText Statement text * @param batchable Whether this statement is batchable. Whether we will process the statement * before any other statement * @param getGeneratedKeysFlag whether to request getGeneratedKeys for this statement * @return The PreparedStatement * @throws SQLException thrown if an error occurs creating the statement */ public PreparedStatement getStatementForUpdate( ManagedConnection conn, String stmtText, boolean batchable, boolean getGeneratedKeysFlag) throws SQLException { Connection c = (Connection) conn.getConnection(); if (supportsBatching) { ConnectionStatementState state = getConnectionStatementState(conn); if (state != null) { if (state.processable) { // We have a batchable statement in the queue that could be processed now if necessary if (!batchable) { // This new statement isnt batchable so process the existing one before returning our // new statement processConnectionStatement(conn); } else { // Check if we could batch onto this existing statement if (state.stmtText.equals(stmtText)) { // We can batch onto this statement if (maxBatchSize == -1 || state.batchSize < maxBatchSize) { state.batchSize++; state.processable = false; // Have to wait til we process this part til processable again if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled()) { NucleusLogger.DATASTORE_PERSIST.debug( LOCALISER.msg("052100", stmtText, "" + state.batchSize)); } return state.stmt; } else { // Reached max batch size so process it now and start again for this one if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled()) { NucleusLogger.DATASTORE_PERSIST.debug(LOCALISER.msg("052101", state.stmtText)); } processConnectionStatement(conn); } } else { // We cant batch using the current batch statement so process it first and return our // new one processConnectionStatement(conn); } } } else { if (batchable) { // The current statement is being batched so we cant batch this since cant process the // current statement now if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled()) { NucleusLogger.DATASTORE_PERSIST.debug( LOCALISER.msg("052102", state.stmtText, stmtText)); } batchable = false; } } } } PreparedStatement ps = (getGeneratedKeysFlag ? c.prepareStatement(stmtText, Statement.RETURN_GENERATED_KEYS) : c.prepareStatement(stmtText)); ps .clearBatch(); // In case using statement caching and given one with batched statements left // hanging (C3P0) if (!jdbcStatements) { // Wrap with our parameter logger ps = new ParamLoggingPreparedStatement(ps, stmtText); ((ParamLoggingPreparedStatement) ps).setParamsInAngleBrackets(paramValuesInBrackets); } if (NucleusLogger.DATASTORE.isDebugEnabled()) { NucleusLogger.DATASTORE.debug(LOCALISER.msg("052109", ps, StringUtils.toJVMIDString(c))); } if (batchable && supportsBatching) { // This statement is batchable so save it as the current batchable if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled()) { NucleusLogger.DATASTORE_PERSIST.debug(LOCALISER.msg("052103", stmtText)); } ConnectionStatementState state = new ConnectionStatementState(); state.stmt = ps; state.stmtText = stmtText; state.batchSize = 1; setConnectionStatementState(conn, state); } return ps; }