2011-01-14 10 views
15

Sto scrivendo logger generico per SQLException e mi piacerebbe ottenere i parametri passati in PreparedStatement, come si fa? Sono stato in grado di ottenere il conteggio di loro.Come ottenere parametri da PreparedStatement?

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

risposta

16

Risposta breve: non è possibile.

Risposta lunga: tutti i driver JDBC manterranno i valori dei parametri da qualche parte ma non esiste un modo standard per ottenerli.

Se si desidera stamparli per il debugging o per usi simili, sono disponibili diverse opzioni:

  1. creare un driver pass-through JDBC (uso p6spy o log4jdbc come base) che mantiene copie dei parametri e offre una API pubblica per leggerli.

  2. Utilizzare l'API Java Reflection (Field.setAccessible(true) è il tuo amico) per leggere le strutture di dati private dei driver JDBC. Questo è il mio approccio preferito. Ho una fabbrica che delega a DB specifiche implementazioni che possono decodificare i parametri e che mi permette di leggere i parametri tramite getObject(int column).

  3. Segnala un bug report e chiedi che le eccezioni siano migliorate. Soprattutto Oracle è davvero avaro quando si tratta di dirti cosa c'è che non va.

1

This article, da Boulder, ahtoulgh DB 2 "specifico", fornisce un esempio completo di utilizzo ParameterMetadata.

7

Soluzione 1: sottoclasse

sufficiente creare un'implementazione personalizzata di un PreparedStatement che delega tutte le chiamate alla dichiarazione preparata originale, aggiungendo solo le richiamate nel setObject, ecc metodi. Esempio:

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

Se si desidera salvare i parametri e farli poi, ci sono molte soluzioni, ma io preferisco la creazione di una nuova classe come ParameterAwarePreparedStatement che ha i parametri in una mappa. La struttura potrebbe essere simile a questo:

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

Soluzione 2: delega dinamico

Questa seconda soluzione è più breve, ma sembra più hacky.

È possibile creare un proxy dinamico chiamando un metodo factory su java.lang.reflect.Proxy e delegare tutte le chiamate sull'istanza originale. Esempio:

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

Quindi è intercettare il setObject, ecc chiamate, cercando in nomi dei metodi e guardando ai secondi argomenti del metodo per i vostri valori.

Problemi correlati