it-roy-ru.com

Как я могу получить SQL PreparedStatement?

У меня есть общий метод Java со следующей сигнатурой метода:

private static ResultSet runSQLResultSet(String sql, Object... queryParams)

Он открывает соединение, создает PreparedStatement с помощью оператора sql и параметров в массиве переменной длины queryParams, запускает его, кэширует ResultSetCachedRowSetImpl), закрывает соединение и возвращает кэшированный результирующий набор.

У меня есть обработка исключений в методе, который регистрирует ошибки. Я записываю SQL-оператор как часть журнала, так как он очень полезен для отладки. Моя проблема заключается в том, что регистрация строковой переменной sql регистрирует оператор шаблона с? Вместо фактических значений. Я хочу записать фактический оператор, который был выполнен (или пытался выполнить).

Итак ... Есть ли способ получить фактический оператор SQL, который будет выполняться PreparedStatement? ( Без . Собираю его сам. Если я не смогу найти способ доступа к SQL PreparedStatement's, я, вероятно, в конечном итоге сам соберу его в своих catches.)

132
froadie

Используя подготовленные операторы, «SQL-запрос» отсутствует:

  • У вас есть заявление, содержащее заполнители
    • отправлено на сервер БД
    • и подготовил там
    • что означает, что оператор SQL «анализируется», анализируется, некоторая структура данных, представляющая его, подготавливается в памяти
  • И тогда у вас есть связанные переменные
    • которые отправляются на сервер
    • и подготовленное заявление выполняется - работает над этими данными

Но реальный реальный запрос SQL не перестраивается - ни на стороне Java, ни на стороне базы данных.

Таким образом, нет никакого способа получить подготовленный оператор SQL - поскольку такого SQL нет.


В целях отладки решения могут быть следующими:

  • Ouput код заявления, с заполнителями и списком данных
  • Или «построить» какой-нибудь SQL-запрос «вручную».
150
Pascal MARTIN

Это нигде не определено в контракте API JDBC, но, если вам повезет, рассматриваемый драйвер JDBC может вернуть полный SQL, просто вызвав PreparedStatement#toString(). То есть.

System.out.println(preparedStatement);

По крайней мере, драйверы MySQL 5.x и PostgreSQL 8.x JDBC поддерживают его. Однако большинство других драйверов JDBC не поддерживают его. Если у вас есть такой, то лучше всего использовать Log4jdbc или P6Spy

В качестве альтернативы вы также можете написать обобщенную функцию, которая принимает Connection, строку SQL и значения оператора и возвращает PreparedStatement после регистрации строки SQL и значений. Начальный пример:

public static PreparedStatement prepareStatement(Connection connection, String sql, Object... values) throws SQLException {
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    for (int i = 0; i < values.length; i++) {
        preparedStatement.setObject(i + 1, values[i]);
    }
    logger.debug(sql + " " + Arrays.asList(values));
    return preparedStatement;
}

и использовать его как 

try {
    connection = database.getConnection();
    preparedStatement = prepareStatement(connection, SQL, values);
    resultSet = preparedStatement.executeQuery();
    // ...

Другой альтернативой является реализация пользовательской PreparedStatement, которая оборачивает (украшает) конструкцию realPreparedStatement и переопределяет все методы так, что она вызывает методы realPreparedStatement и собирает значения во всех методах setXXX() и лениво конструирует «фактическую» строку SQL всякий раз, когда вызывается один из методов executeXXX() (вполне работа, но большинство IDE предоставляет автогенераторы для методов декоратора, как это делает Eclipse). Наконец, просто используйте его вместо. Это также в основном то, что P6Spy и супруги уже делают под капотами.

47
BalusC

Я использую Java 8, драйвер JDBC с MySQL коннектором v. 5.1.31.

Я могу получить настоящую строку SQL, используя этот метод:

// 1. make connection somehow, it's conn variable
// 2. make prepered statement template
PreparedStatement stmt = conn.prepareStatement(
    "INSERT INTO oc_manufacturer" +
    " SET" +
    " manufacturer_id = ?," +
    " name = ?," +
    " sort_order=0;"
);
// 3. fill template
stmt.setInt(1, 23);
stmt.setString(2, 'Google');
// 4. print sql string
System.out.println(((JDBC4PreparedStatement)stmt).asSql());

Таким образом, он возвращает что-то вроде этого:

INSERT INTO oc_manufacturer SET manufacturer_id = 23, name = 'Google', sort_order=0;
23
userlond

Если вы выполняете запрос и ожидаете ResultSet (по крайней мере, вы в этом сценарии), то вы можете просто вызвать ResultSet's getStatement() следующим образом:

ResultSet rs = pstmt.executeQuery();
String executedQuery = rs.getStatement().toString();

Переменная executedQuery будет содержать оператор, который использовался для создания ResultSet.

Теперь я понимаю, что этот вопрос довольно старый, но я надеюсь, что это поможет кому-то ..

18
Elad Stern

Я извлек свой sql из PreparedStatement с помощью prepareStatement.toString (). В моем случае toString () возвращает String следующим образом:

[email protected][sql=[INSERT INTO 
TABLE_NAME(COLUMN_NAME, COLUMN_NAME, COLUMN_NAME) VALUES(?, ?, ?)],
parameters=[[value], [value], [value]]]

Теперь я создал метод (Java 8), который использует регулярные выражения, чтобы извлечь запрос и значения и поместить их в карту:

private Map<String, String> extractSql(PreparedStatement preparedStatement) {
    Map<String, String> extractedParameters = new HashMap<>();
    Pattern pattern = Pattern.compile(".*\\[sql=\\[(.*)],\\sparameters=\\[(.*)]].*");
    Matcher matcher = pattern.matcher(preparedStatement.toString());
    while (matcher.find()) {
      extractedParameters.put("query", matcher.group(1));
      extractedParameters.put("values", Stream.of(matcher.group(2).split(","))
          .map(line -> line.replaceAll("(\\[|])", ""))
          .collect(Collectors.joining(", ")));
    }
    return extractedParameters;
  }

Этот метод возвращает карту, где у нас есть пары ключ-значение:

"query" -> "INSERT INTO TABLE_NAME(COLUMN_NAME, COLUMN_NAME, COLUMN_NAME) VALUES(?, ?, ?)"
"values" -> "value,  value,  value"

Теперь - если вы хотите значения в виде списка, вы можете просто использовать: 

List<String> values = Stream.of(yourExtractedParametersMap.get("values").split(","))
    .collect(Collectors.toList());

Если ваш readyStatement.toString () отличается от того, что в моем случае, это просто вопрос «корректировки» регулярного выражения.

2
RichardK

Использование PostgreSQL 9.6.x с официальным драйвером Java 42.2.4:

...myPreparedStatement.execute...
myPreparedStatement.toString()

Покажет SQL с? уже заменил, что я и искал . Просто добавил этот ответ, чтобы покрыть дело postgres.

Я бы никогда не подумал, что это может быть так просто.

1
Christophe Roussy

Очень поздно :), но вы можете получить исходный SQL из OraclePreparedStatementWrapper по

((OraclePreparedStatementWrapper) preparedStatement).getOriginalSql();
1
user497087

Если вы используете MySQL, вы можете регистрировать запросы, используя MySQL query log . Я не знаю, предоставляют ли другие поставщики эту функцию, но скорее всего, они делают.

0
Ionuț G. Stan

Фрагмент кода для преобразования SQL PreparedStaments со списком аргументов. Меня устраивает

  /**
         * 
         * formatQuery Utility function which will convert SQL
         * 
         * @param sql
         * @param arguments
         * @return
         */
        public static String formatQuery(final String sql, Object... arguments) {
            if (arguments != null && arguments.length <= 0) {
                return sql;
            }
            String query = sql;
            int count = 0;
            while (query.matches("(.*)\\?(.*)")) {
                query = query.replaceFirst("\\?", "{" + count + "}");
                count++;
            }
            String formatedString = Java.text.MessageFormat.format(query, arguments);
            return formatedString;
        }
0
Harsh Maheswari

Я реализовал следующий код для печати SQL из PrepareStatement

public void printSqlStatement(PreparedStatement preparedStatement, String sql) throws SQLException{
        String[] sqlArrya= new String[preparedStatement.getParameterMetaData().getParameterCount()];
        try {
               Pattern pattern = Pattern.compile("\\?");
               Matcher matcher = pattern.matcher(sql);
               StringBuffer sb = new StringBuffer();
               int indx = 1;  // Parameter begin with index 1
               while (matcher.find()) {
             matcher.appendReplacement(sb,String.valueOf(sqlArrya[indx]));
               }
               matcher.appendTail(sb);
              System.out.println("Executing Query [" + sb.toString() + "] with Database[" + "] ...");
               } catch (Exception ex) {
                   System.out.println("Executing Query [" + sql + "] with Database[" +  "] ...");
            }

    }
0
user3349710