2011-01-14 16 views
15

Tôi đang viết nhật ký chung cho SQLException và tôi muốn lấy các tham số được chuyển vào PreparedStatement, làm thế nào để thực hiện nó? Tôi đã có thể nhận được số lượng của họ.Cách nhận thông số từ PreparedStatement?

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

Trả lời

16

Câu trả lời ngắn gọn: Bạn không thể.

Câu trả lời dài: Tất cả các trình điều khiển JDBC sẽ giữ giá trị tham số ở đâu đó nhưng không có cách nào tiêu chuẩn để lấy chúng.

Nếu bạn muốn in chúng để gỡ lỗi hoặc các mục đích tương tự, bạn có nhiều lựa chọn:

  1. Tạo một trình điều khiển JDBC pass-through (sử dụng p6spy hoặc log4jdbc làm cơ sở) mà giữ các bản sao của các thông số và cung cấp API công khai để đọc chúng.

  2. Sử dụng API phản chiếu Java (Field.setAccessible(true) là bạn của bạn) để đọc cấu trúc dữ liệu riêng tư của trình điều khiển JDBC. Đó là cách tiếp cận ưa thích của tôi. Tôi có một nhà máy đại diện cho DB triển khai cụ thể có thể giải mã các thông số và cho phép tôi đọc các thông số qua getObject(int column).

  3. Gửi báo cáo lỗi và yêu cầu các ngoại lệ được cải thiện. Đặc biệt là Oracle thực sự keo kiệt khi nói với bạn những gì sai.

1

This article, từ Boulder, ahtoulgh DB 2 "cụ thể", đưa ra ví dụ hoàn chỉnh về sử dụng thông sốMetadata.

7

Giải pháp 1: Subclass

Đơn giản chỉ cần tạo một cài đặt tùy chỉnh của một PreparedStatement mà các đại biểu tất cả các cuộc gọi đến các tuyên bố chuẩn bị ban đầu, chỉ có thêm callbacks trong setObject, vv phương pháp. Ví dụ:

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

Nếu bạn muốn lưu các thông số và nhận được chúng sau này, có rất nhiều giải pháp, nhưng tôi thích cách tạo ra một lớp mới như ParameterAwarePreparedStatement trong đó có các thông số trong bản đồ. Cấu trúc có thể là tương tự như sau:

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

Giải pháp 2: Năng động, Proxy

giải pháp thứ hai này là ngắn hơn, nhưng dường như hacky hơn.

Bạn có thể tạo proxy động bằng cách gọi phương thức gốc trên java.lang.reflect.Proxy và ủy quyền tất cả các cuộc gọi trên phiên bản gốc. Ví dụ:

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

Sau đó, bạn đánh chặn các setObject vv cuộc gọi bằng cách nhìn vào tên phương pháp và tìm cách để các đối số phương pháp thứ hai cho các giá trị của bạn.