2011-01-14 20 views
15

Piszę generic logger dla SQLException i chciałbym uzyskać parametry, które zostały przekazane do PreparedStatement, jak to zrobić? Udało mi się je policzyć.Jak uzyskać parametry z PreparedStatement?

ParameterMetaData metaData = query.getParameterMetaData(); 
parameterCount = metaData.getParameterCount(); 

Odpowiedz

16

Krótka odpowiedź: Nie możesz.

Długa odpowiedź: wszystkie sterowniki JDBC zachowają wartości parametrów gdzieś, ale nie ma standardowego sposobu ich uzyskania.

Jeśli chcesz wydrukować je do debugowania lub podobnych celów, masz kilka opcji:

  1. stworzyć sterownik pass-through JDBC (użyj p6spy lub log4jdbc jako podstawa), które utrzymują kopie parametrów i oferuje publiczny interfejs API do ich odczytu.

  2. Użyj Java Reflection API (Field.setAccessible(true) jest twoim przyjacielem), aby odczytać prywatne struktury danych sterowników JDBC. To moje preferowane podejście. Posiadam fabrykę, która deleguje do konkretnych implementacji DB, które mogą dekodować parametry i która pozwala mi odczytać parametry przez getObject(int column).

  3. Zgłoś błąd i poproś, aby wyjątki zostały poprawione. Szczególnie Oracle jest naprawdę skąpy, jeśli chodzi o informowanie o tym, co jest nie tak.

1

This article z Boulder, ahtoulgh DB 2 "specyficzny", podaje się całkowitą przykład użycia ParameterMetadata.

7

Rozwiązanie 1: Podklasa

Wystarczy utworzyć niestandardową implementację o PreparedStatement których delegaci wszystkich wywołań oryginalnego przygotowanym oświadczeniu, dodając tylko wywołania zwrotne w setObject itp metody. Przykład:

public PreparedStatement prepareStatement(String sql) { 
     final PreparedStatement delegate = conn.prepareStatement(sql); 
     return new PreparedStatement() { 
      // TODO: much more methods to delegate 

      @Override 
      public void setString(int parameterIndex, String x) throws SQLException { 
       // TODO: remember value of X 
       delegate.setString(parameterIndex, x); 
      } 
     }; 
    } 

Jeśli chcesz zapisać parametry i je później, istnieje wiele rozwiązań, ale wolę tworząc nową klasę jak ParameterAwarePreparedStatement która ma parametry na mapie. Struktura może być podobny do tego:

public class ParameterAwarePreparedStatement implements PreparedStatement { 
    private final PreparedStatement delegate; 
    private final Map<Integer,Object> parameters; 

    public ParameterAwarePreparedStatement(PreparedStatement delegate) { 
     this.delegate = delegate; 
     this.parameters = new HashMap<>(); 
    } 

    public Map<Integer,Object> getParameters() { 
     return Collections.unmodifiableMap(parameters); 
    } 

    // TODO: many methods to delegate 

    @Override 
    public void setString(int parameterIndex, String x) throws SQLException { 
     delegate.setString(parameterIndex, x); 
     parameters.put(parameterIndex, x); 
    } 
} 

Rozwiązanie 2: Dynamiczne proxy

To drugie rozwiązanie jest krótsza, ale wydaje się bardziej hacky.

Można utworzyć dynamiczny serwer proxy, wywołując metodę fabryczną na java.lang.reflect.Proxy i delegować wszystkie wywołania w oryginalnej instancji. Przykład:

public PreparedStatement prepareStatement(String sql) { 
    final PreparedStatement ps = conn.prepareStatement(sql); 
    final PreparedStatement psProxy = (PreparedStatement) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class<?>[]{PreparedStatement.class}, new InvocationHandler() { 
     @Override 
     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
      if (method.getName().equals("setLong")) { 
       // ... your code here ... 
      } 
      // this invokes the default call 
      return method.invoke(ps, args); 
     } 
    }); 
    return psProxy; 
} 

Następnie przechwycić setObject itd połączeń patrząc na nazwy metody i patrząc do drugich argumentów metoda swoimi wartościami.

Powiązane problemy