/** * 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; } } } }
/** * Method to execute a PreparedStatement update. Prints logging information about timings. * * @param conn The connection (required since the one on PreparedStatement is not always the same * so we cant use it) * @param stmt The statement text * @param ps The Prepared Statement * @param processNow Whether to process this statement now (only applies if is batched) * @return The numer of rows affected (as per PreparedStatement.executeUpdate) * @throws SQLException Thrown if an error occurs */ public int[] executeStatementUpdate( ExecutionContext ec, ManagedConnection conn, String stmt, PreparedStatement ps, boolean processNow) throws SQLException { ConnectionStatementState state = getConnectionStatementState(conn); if (state != null) { if (state.stmt == ps) { // Mark as processable if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled()) { NucleusLogger.DATASTORE_PERSIST.debug( LOCALISER.msg("052104", state.stmtText, "" + state.batchSize)); } state.processable = true; state.stmt.addBatch(); if (processNow) { // Process the batch now state.closeStatementOnProcess = false; // user method has requested execution so they can close it themselves now return processConnectionStatement(conn); } else { // Leave processing til later return null; } } else { // There is a waiting batch yet it is a different statement, so process that one now since // we need // our statement executing processConnectionStatement(conn); } } // Process the normal update statement long startTime = System.currentTimeMillis(); if (NucleusLogger.DATASTORE_NATIVE.isDebugEnabled()) { if (ps instanceof ParamLoggingPreparedStatement) { NucleusLogger.DATASTORE_NATIVE.debug( ((ParamLoggingPreparedStatement) ps).getStatementWithParamsReplaced()); } else { NucleusLogger.DATASTORE_NATIVE.debug(stmt); } } int ind = ps.executeUpdate(); if (ec != null && ec.getStatistics() != null) { // Add to statistics ec.getStatistics().incrementNumWrites(); } ps.clearBatch(); if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled()) { NucleusLogger.DATASTORE_PERSIST.debug( LOCALISER.msg( "045001", "" + (System.currentTimeMillis() - startTime), "" + ind, StringUtils.toJVMIDString(ps))); } return new int[] {ind}; }
/** * 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; }