/**
   * 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 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;
  }