[java] PreparedStatement의 SQL을 어떻게 얻을 수 있습니까?

다음과 같은 메소드 서명이있는 일반적인 Java 메소드가 있습니다.

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

연결을 열고 PreparedStatementsql 문을 사용하여 변수를 작성하고 queryParams변수 길이 배열 의 매개 변수를 실행하고 실행하고 ResultSet(a CachedRowSetImpl)를 캐시 하고 연결을 닫은 다음 캐시 된 결과 세트를 리턴합니다.

오류를 기록하는 방법에 예외 처리가 있습니다. 디버깅에 매우 도움이되므로 SQL 문을 로그의 일부로 기록합니다. 내 문제는 String 변수를 sql로깅하면 템플릿 값을 실제 값 대신?로 기록한다는 것입니다. 실행 된 실제 명령문 을 기록하고 싶습니다 .

그래서 …에 의해 실행될 실제 SQL 문을 얻는 방법이 PreparedStatement있습니까? ( 내가 직접 구축 하지 않고 . PreparedStatement'sSQL 에 액세스하는 방법을 찾을 수 없다면 아마도 내 자신 의 SQL에 직접 구축 할 수 catch있습니다.)



답변

준비된 명령문을 사용하면 “SQL 쿼리”가 없습니다.

  • 자리 표시자가 포함 된 명세서가 있습니다.
    • DB 서버로 전송
    • 그리고 거기에서 준비
    • 이는 SQL 문이 “분석 됨”, 구문 분석 됨,이를 나타내는 일부 데이터 구조가 메모리에서 준비됨을 의미합니다.
  • 그런 다음 변수를 바인딩했습니다.
    • 서버로 전송
    • 준비된 문장이 실행됩니다.

그러나 실제 실제 SQL 쿼리의 재구성은 없습니다. Java 측이나 데이터베이스 측이 아닙니다.

따라서 준비된 명령문의 SQL을 얻을 수있는 방법이 없습니다. 그러한 SQL이 없기 때문입니다.

디버깅 목적으로 솔루션은 다음 중 하나입니다.

  • 자리 표시 자와 데이터 목록과 함께 명세서 코드를 출력합니다.
  • 또는 “손으로”일부 SQL 쿼리를 “빌드”합니다.

답변

JDBC API 계약에는 정의되어 있지 않지만 운이 좋으면 문제가되는 JDBC 드라이버는을 호출하여 전체 SQL을 반환 할 수 있습니다 PreparedStatement#toString(). 즉

System.out.println(preparedStatement);

적어도 MySQL 5.x 및 PostgreSQL 8.x JDBC 드라이버가이를 지원합니다. 그러나 대부분의 다른 JDBC 드라이버는이를 지원하지 않습니다. 그러한 것이 있다면 가장 좋은 방법은 Log4jdbc 또는 P6Spy를 사용하는 것 입니다.

또는, ConnectionSQL 문자열 및 명령문 값을 사용하고 PreparedStatementSQL 문자열 및 값을 로깅 한 후를 리턴하는 일반 함수를 작성할 수도 있습니다. 킥오프 예 :

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을 랩 (장식)하고 모든 메소드를 재정 의하여 사용자가 실제 메소드를 호출하고 모든 메소드 에서 값을 수집하고 다음 중 하나 일 때마다 “실제”SQL 문자열을 느리게 구성 하는 사용자 정의를 구현하는 것입니다. 방법 (아주 작동하지만 대부분의 IDE의는 이클립스가하는 장식 방법에 대한 autogenerators을 제공합니다)라고합니다. 마지막으로 대신 사용하십시오. 그것은 기본적으로 P6Spy와 컨소시엄이 이미 후드에서 수행하는 것입니다. PreparedStatement PreparedStatementsetXXX()executeXXX()


답변

MySQL 커넥터 v. 5.1.31과 함께 Java 8, JDBC 드라이버를 사용하고 있습니다.

이 방법을 사용하여 실제 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;


답변

쿼리를 실행하고 기대하고있는 경우 ResultSet(당신이 적어도,이 시나리오에) 당신이 간단하게 호출 할 수 있습니다 ResultSet이야 ‘ getStatement()과 같이 :

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

변수 executedQuery에는를 만드는 데 사용 된 명령문이 포함됩니다 ResultSet.

이제이 질문이 상당히 오래되었다는 것을 알고 있지만 이것이 누군가에게 도움이되기를 바랍니다.


답변

readyStatement.toString ()을 사용하여 PreparedStatement에서 SQL을 추출했습니다. 제 경우에는 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)를 만들었습니다.

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 ()이 제 경우와 다른 경우 정규 표현식을 “조정”하는 것입니다.


답변

공식 Java 드라이버와 함께 PostgreSQL 9.6.x 사용 42.2.4:

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

SQL이 ?이미 교체 된 상태로 표시 됩니다. postgres 사례를 다루기 위해이 답변을 추가했습니다.

나는 그것이 그렇게 간단 할 수 있다고 생각하지 않았을 것입니다.


답변

PrepareStatement에서 SQL을 인쇄하기 위해 다음 코드를 구현했습니다.

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[" +  "] ...");
            }

    }