Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get parameters from PreparedStatement?

Tags:

I'm writing generic logger for SQLException and I'd like to get parameters that were passed into PreparedStatement, how to do it ? I was able to get the count of them.

ParameterMetaData metaData = query.getParameterMetaData(); parameterCount = metaData.getParameterCount(); 
like image 458
IAdapter Avatar asked Jan 14 '11 13:01

IAdapter


People also ask

What does PreparedStatement executeQuery return?

executeQuery. Executes the SQL query in this PreparedStatement object and returns the ResultSet object generated by the query.

What does PreparedStatement executeUpdate return?

When the method executeUpdate is used to execute a DDL (data definition language) statement, such as in creating a table, it returns the int value of 0.

How do you pass parameters in PreparedStatement?

To execute a statement with Where clause using PreparedStatement. Prepare the query by replacing the value in the clause with place holder “?” and, pass this query as a parameter to the prepareStatement() method.

Which methods on the PreparedStatement can be used to bind the parameters?

You must supply values for every parameter before executing the SQL statement. The setXXX() methods bind values to the parameters, where XXX represents the Java data type of the value you wish to bind to the input parameter.


2 Answers

Short answer: You can't.

Long answer: All JDBC drivers will keep the parameter values somewhere but there is no standard way to get them.

If you want to print them for debugging or similar purposes, you have several options:

  1. Create a pass-through JDBC driver (use p6spy or log4jdbc as a basis) which keeps copies of the parameters and offers a public API to read them.

  2. Use Java Reflection API (Field.setAccessible(true) is your friend) to read the private data structures of the JDBC drivers. That's my preferred approach. I have a factory which delegates to DB specific implementations that can decode the parameters and that allows me to read the parameters via getObject(int column).

  3. File a bug report and ask that the exceptions are improved. Especially Oracle is really stingy when it comes to tell you what's wrong.

like image 113
Aaron Digulla Avatar answered Oct 25 '22 15:10

Aaron Digulla


Solution 1: Subclass

Simply create a custom implementation of a PreparedStatement which delegates all calls to the original prepared statement, only adding callbacks in the setObject, etc. methods. Example:

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

If you want to save parameters and get them later, there are many solutions, but I prefer creating a new class like ParameterAwarePreparedStatement which has the parameters in a map. The structure could be similar to this:

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

Solution 2: Dynamic proxy

This second solution is shorter, but seems more hacky.

You can create a dynamic proxy by calling a factory method on java.lang.reflect.Proxy and delegate all calls on the original instance. Example:

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

Then you intercept the setObject, etc. calls by looking at method names and looking to the second method arguments for your values.

like image 39
voho Avatar answered Oct 25 '22 15:10

voho