2011-01-14 22 views
15

Estoy escribiendo registrador genérico para SQLException y me gustaría obtener los parámetros que se pasaron a PreparedStatement, ¿cómo hacerlo? Pude obtener el conteo de ellos.¿Cómo obtener los parámetros de PreparedStatement?

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

Respuesta

16

Respuesta corta: No se puede.

Respuesta larga: Todos los controladores JDBC mantendrán los valores de los parámetros en alguna parte, pero no existe una forma estándar de obtenerlos.

Si desea imprimirlos para la depuración o usos similares, tiene varias opciones:

  1. crear un controlador de paso a través de JDBC (uso p6spy o log4jdbc como base) que mantiene copias de los parámetros y ofrece una API pública para leerlos.

  2. Utilice Java Reflection API (Field.setAccessible(true) es su amigo) para leer las estructuras de datos privadas de los controladores JDBC. Ese es mi enfoque preferido. Tengo una fábrica que delega en implementaciones específicas de DB que pueden decodificar los parámetros y eso me permite leer los parámetros a través del getObject(int column).

  3. Archive un informe de error y solicite que se mejoren las excepciones. Especialmente Oracle es realmente tacaño cuando se trata de decirte lo que está mal.

1

This article, de Boulder, ahtoulgh DB 2 "específica", da un ejemplo completo de uso ParameterMetaData.

7

Solución 1: Subclase

Basta con crear una implementación personalizada de un PreparedStatement que delega todas las llamadas al declaración preparada el original, sólo se suma devoluciones de llamada en el setObject, etc. métodos. Ejemplo:

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

Si desea guardar los parámetros y obtener más adelante, hay muchas soluciones, pero yo prefiero la creación de una nueva clase como ParameterAwarePreparedStatement que tiene los parámetros en un mapa. La estructura podría ser similar a esto:

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

Solución 2: proxy dinámico

Esta segunda solución es más corto, pero parece más hacky.

Puede crear un proxy dinámico llamando a un método de fábrica en java.lang.reflect.Proxy y delegar todas las llamadas en la instancia original. Ejemplo:

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

A continuación, interceptar la setObject, etc. llamadas examinado los nombres de métodos y mirando a los segundos argumentos de los métodos para sus valores.

Cuestiones relacionadas