Получение полного запроса, выполняемого Sql [duplicate]

Это означает, что указанная переменная не указана ни на что. Я мог бы сгенерировать это так:

SqlConnection connection = null;
connection.Open();

Это вызовет ошибку, потому что, пока я объявил переменную «connection», она не указала ни на что. Когда я пытаюсь вызвать член «Open», для его устранения нет ссылки, и он будет вызывать ошибку.

Чтобы избежать этой ошибки:

  1. Всегда инициализируйте свои объекты, прежде чем пытаться что-либо с ними делать.
  2. Если вы не уверены, что объект имеет значение null, проверьте его с помощью object == null.

Инструмент Resharper JetBrains определит каждое место в вашем коде, которое имеет возможность ошибки нулевой ссылки, позволяя вам ввести нулевую проверку. Эта ошибка является источником ошибок номер один, IMHO.

126
задан BalusC 4 March 2010 в 21:53
поделиться

12 ответов

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

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

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

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

Для цели отладки решения должны быть либо:

  • . Вывести код инструкции, с помощью заполнителей и список данных
  • Или «построить» некоторый SQL-запрос «вручную».
144
ответ дан Matthias Braun 27 August 2018 в 21:23
поделиться
  • 1
    Хотя это функционально верно, нет ничего, что предотвращало бы восстановление кода утилиты эквивалентным неподготовленным оператором. Например, в log4jdbc: «В выходном файле журнала для подготовленных операторов аргументы привязки автоматически вставляются в вывод SQL. Это значительно улучшает читаемость и отладку для многих случаев. & Quot; Очень полезно для отладки, если вы знаете, что это не то, как оператор фактически выполняется сервером БД. – sidereal 4 March 2010 в 21:47
  • 2
    Это также зависит от реализации. В MySQL - по крайней мере, версия, которую я использовал несколько лет назад - драйвер JDBC фактически построил обычный SQL-запрос из шаблона и привязал переменные. Я предполагаю, что версия MySQL не поддерживала подготовленные инструкции изначально, поэтому они реализовали их в драйвере JDBC. – Jay 4 March 2010 в 21:50
  • 3
    @sidereal: вот что я подразумевал под & quot; построить запрос вручную & quot; ; но ты сказал это лучше меня ;;; @Jay: у нас есть такой же mecanism на месте в PHP (реальные подготовленные операторы при поддержке; псевдоподготовленные операторы для драйверов баз данных, которые их не поддерживают) – Pascal MARTIN 4 March 2010 в 21:54
  • 4
    Если вы используете java.sql.PreparedStatement, то простой .toString () в подготовленномStatement будет содержать сгенерированный SQL, который я проверил в 1.8.0_60 – Preston 24 January 2016 в 00:09
  • 5
    @Preston Для Oracle DB PreparedStatement # toString () не отображает SQL. Поэтому я думаю, это зависит от драйвера DB JDBC. – Mike Argyriou 23 February 2016 в 09:00

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

System.out.println(preparedStatement);

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

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

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, который обертывает (украшает) real PreparedStatement по построению и переопределяет все методы, чтобы он вызывал методы real PreparedStatement и собирал значения во всех методах setXXX() и лениво конструировал «фактическую» строку SQL когда вызывается один из методов executeXXX() (довольно много работы, но большинство IDE обеспечивает автогенераторы для методов декоратора, Eclipse делает). Наконец, просто используйте его. Это также в основном то, что P6Spy и супруги уже делают под капотами.

45
ответ дан BalusC 27 August 2018 в 21:23
поделиться
  • 1
    Это похоже на метод, который я использую (ваш метод prepareStatement). Мой вопрос заключается не в том, как это сделать - мой вопрос заключается в том, как log инструкция sql. Я знаю, что могу сделать logger.debug(sql + " " + Arrays.asList(values)). Я ищу способ зарегистрировать оператор sql с уже интегрированными в него параметрами. Без зацикливания и замены вопросительных знаков. – froadie 4 March 2010 в 22:11
  • 2
    Затем отправляйтесь в последний параграф моего ответа или смотрите P6Spy. Они делают «неприятный». зацикливание и замена работы для вас;) – BalusC 4 March 2010 в 22:19
  • 3
    Ссылка на P6Spy теперь сломана. – Stephen P 14 August 2015 в 18:04
  • 4
    @BalusC Я новичок в JDBC. У меня есть одно сомнение. Если вы пишете такую ​​общую функцию, она будет создавать PreparedStatement каждый раз. Разве это не так-то эффективно, поскольку цель PreparedStatement состоит в том, чтобы создать их один раз и повторно использовать их повсюду? – Bhushan Patil 9 March 2017 в 05:41
  • 5
    Это также работает с драйвером voltdb jdbc, чтобы получить полный запрос sql для подготовленного оператора. – k0pernikus 1 August 2017 в 10:02

Я использую Oralce 11g и не смог получить окончательный SQL из PreparedStatement. После прочтения @Pascal MARTIN ответ я понимаю, почему.

Я просто отказался от идеи использования PreparedStatement и использовал простой текстовый форматировщик, который соответствовал моим потребностям. Вот мой пример:

//I jump to the point after connexion has been made ...
java.sql.Statement stmt = cnx.createStatement();
String sqlTemplate = "SELECT * FROM Users WHERE Id IN ({0})";
String sqlInParam = "21,34,3434,32"; //some random ids
String sqlFinalSql = java.text.MesssageFormat(sqlTemplate,sqlInParam);
System.out.println("SQL : " + sqlFinalSql);
rsRes = stmt.executeQuery(sqlFinalSql);

Вы понимаете, что sqlInParam может быть динамически построена в цикле (for while). Я просто упростил доступ к тому, чтобы использовать класс MessageFormat для использования в качестве шаблонный шаблон строки для SQL-запроса.

0
ответ дан Diego Tercero 27 August 2018 в 21:23
поделиться
  • 1
    Этот вид взрывает всю причину использования подготовленных инструкций, например, избегая инъекции sql и улучшенной производительности. – ticktock 1 August 2016 в 19:52
  • 2
    Я согласен с вами на 100%. Я должен был четко указать, что я сделал этот код, чтобы выполнить несколько раз максимум для массивной интеграции объемных данных и отчаянно нуждался в быстром способе иметь некоторый выход из журнала, не вдаваясь в весь log4j enchilada, который был бы излишним для чего Мне было нужно. Это не должно входить в производственный код :-) – Diego Tercero 16 August 2016 в 15:29
  • 3
    Это работает для меня с драйвером Oracle (но не включает параметры): ((OraclePreparedStatementWrapper) myPreparedStatement).getOriginalSql() – latj 31 May 2017 в 20:23

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

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

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

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

17
ответ дан Elad Stern 27 August 2018 в 21:23
поделиться
  • 1
    @Elad Stern Он печатает, oracle.jdbc.driver.OraclePreparedStatementWrapper@1b9ce4b вместо того, чтобы печатать выполненный SQL-запрос! Пожалуйста, направляйте нас! – AVA 7 December 2015 в 13:20
  • 2
    @AVA, вы использовали toString ()? – Elad Stern 8 December 2015 в 08:43
  • 3
    @EladStern toString () используется! – AVA 8 December 2015 в 09:55
  • 4
    @AVA, ну, я не уверен, но это может иметь отношение к вашему драйверу jdbc. Я успешно использовал mysql-connector-5. – Elad Stern 8 December 2015 в 14:27
  • 5
    Функция rs.getStatement () просто возвращает объект оператора, поэтому зависит от того, использует ли драйвер, который вы используете, реализует .toString (), который определяет, вернете ли вы SQL-запрос – Daz 6 February 2017 в 21:57

Фрагмент кода для преобразования 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 27 August 2018 в 21:23
поделиться

Просто функция:

public static String getSQL (Statement stmt){
    String tempSQL = stmt.toString();

    //please cut everything before sql from statement
    //javadb...: 
    int i1 = tempSQL.indexOf(":")+2;
    tempSQL = tempSQL.substring(i1);

    return tempSQL;
}

Хорошо, что и для подготовленногоStatement.

-1
ответ дан Hobson 27 August 2018 в 21:23
поделиться
  • 1
    Это просто .toString() с несколькими дополнительными строками, чтобы обмануть беспроигрышных пользователей и уже ответили давным-давно. – Pere 8 February 2018 в 18:24

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

0
ответ дан Ionuț G. Stan 27 August 2018 в 21:23
поделиться

Я использую 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());

Итак, это возвращает smth следующим образом:

INSERT INTO oc_manufacturer SET manufacturer_id = 23, name = 'Google', sort_order=0;
20
ответ дан Kevin Guan 27 August 2018 в 21:23
поделиться
  • 1
    У этого должны быть все upvotes, поскольку это именно то, что ищет OP. – HuckIt 8 August 2016 в 20:10
  • 2
    Есть ли аналогичная функция для Postgres-драйвера? – davidwessman 7 April 2017 в 12:17

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

org.hsqldb.jdbc.JDBCPreparedStatement@7098b907[sql=[INSERT INTO 
TABLE_NAME(COLUMN_NAME, COLUMN_NAME, COLUMN_NAME) VALUES(?, ?, ?)],
parameters=[[value], [value], [value]]]

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

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());

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

0
ответ дан RichardK 27 August 2018 в 21:23
поделиться

Для этого вам нужно подключение JDBC и / или драйвер, который поддерживает ведение журнала sql на низком уровне.

Взгляните на log4jdbc

-3
ответ дан sidereal 27 August 2018 в 21:23
поделиться
  • 1
    Взгляните на log4jdbc, а потом что? Как ты это используешь? Вы попадаете на этот сайт и видите случайное блуждание по проекту без четкого примера того, как на самом деле использовать эту технологию. – Hooli 25 April 2016 в 11:29

Я применил следующий код для печати 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
ответ дан user 27 August 2018 в 21:23
поделиться

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

((OraclePreparedStatementWrapper) preparedStatement).getOriginalSql();
0
ответ дан user497087 27 August 2018 в 21:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: