前言
Java後他開發無法避免的需要和資料庫打交道,我們希望能夠能夠再資料庫中列印每一次JDBC的連接配接URL資訊,執行的SQL腳本和傳遞的參數資訊,本文僅僅實作了一個簡單的Demo,算是簡單的一個思路。
JDBC的執行流程
Java通路資料庫的架構有很多,類似Mybatis,JPA等,熟悉Java開發的童鞋應該都知道不論使用何種架構,都無法避免的需要mysql-connector-java這個jar包(通路MySQL資料庫,其他的資料庫需要加入對應廠商的)。Java本身提供了java.sql.*包,這個包裡面僅僅提供了通路資料庫的接口,例如public interface Connection extends Wrapper, AutoCloseable,具體的實作類再不同的驅動程式中。
// jdbc通路資料庫的代碼
//1.加載驅動程式
Class.forName("com.mysql.jdbc.Driver");
//2.獲得資料庫連結
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
//3.通過資料庫的連接配接操作資料庫,實作增删改查(使用PreparedStatement 類)
PreparedStatement ps = conn.prepareStatement("select 1");
//4、設定參數
// ps.setLong();
//5、擷取查詢的結果ResultSet
ResultSet resultSet = ps.executeQuery();
整個方式資料庫的流程大概分為6步驟:
1、加載驅動程式
2、獲得資料庫連結
3、通過資料庫的連接配接操作資料庫,實作增删改查(使用PreparedStatement 類)
4、設定參數,如果需要的話
5、擷取查詢的結果ResultSet
6、處理查詢結果
jdbc通路資料庫的流程大概就是上述的六個過程,那麼我們需要從哪裡去動态代理?代理Connection和PreparedStatement這兩個給,Connection類裡面擷取資料庫連接配接資訊和SQL語句,PreparedStatement中擷取設定的參數
JdbcProxy類
package com.parallel.stomp.test.jdbc.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.*;
/**
* @author rewnei2
* @version v0.1 2019/1/14 10:37
*/
public class JdbcProxy {
public Connection getConnection() throws ClassNotFoundException, SQLException {
String URL = "jdbc:mysql://192.168.94.38:3306/dp";
String USER = "root";
String PASSWORD = "root";
//1.加載驅動程式
Class.forName("com.mysql.jdbc.Driver");
//2.獲得資料庫連結
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
return conn;
//3.通過資料庫的連接配接操作資料庫,實作增删改查(使用Statement類)
//PreparedStatement ps = conn.prepareStatement("select 1");
//Object obj = ps.executeQuery();
}
public Connection proxyConnection(Connection connection) {
Object obj = Proxy.newProxyInstance(JdbcProxy.class.getClassLoader(), new Class[]{Connection.class}, new ConnectionProxyHandler(connection));
return (Connection) obj;
}
class ConnectionProxyHandler implements InvocationHandler {
private Connection connection;
public ConnectionProxyHandler(Connection connection) {
this.connection = connection;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
boolean targetMethodFlag = false;
Object result = null;
if ("prepareStatement".equals(method.getName())) {
targetMethodFlag = true;
}
if (targetMethodFlag) {
System.out.println("database=" + connection.getCatalog() + ",url=" + connection.getMetaData().getURL());
System.out.println(String.format("開始執行時間%s,執行sql語句%s", System.currentTimeMillis(), args[0]));
result = method.invoke(connection, args);
System.out.println(String.format("結束執行時間%s,執行sql語句%s", System.currentTimeMillis(), args[0]));
}
if (targetMethodFlag && result instanceof PreparedStatement) {
PreparedStatement ps = (PreparedStatement) result;
result = proxyPreparedStatement(ps);
}
return result;
}
}
public PreparedStatement proxyPreparedStatement(final PreparedStatement statement) {
Object c = Proxy.newProxyInstance(JdbcProxy.class.getClassLoader()
, new Class[]{PreparedStatement.class}, new PreparedStatementHandler(statement));
return (PreparedStatement) c;
}
/**
* PreparedStatement 代理處理
*/
public class PreparedStatementHandler implements InvocationHandler {
private final PreparedStatement statement;
public PreparedStatementHandler(PreparedStatement statement) {
this.statement = statement;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("setLong")) {
System.out.println("position:" + args[0] + "value:" + args[1]);
}
Object result = method.invoke(statement, args);
return result;
}
}
}
測試類
public class TestMain {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
JdbcProxy jdbcProxy = new JdbcProxy();
Connection connection = jdbcProxy.getConnection();
Connection connectionProxy = jdbcProxy.proxyConnection(connection);
PreparedStatement ps = connectionProxy.prepareStatement("SELECT * FROM client WHERE id=?");
ps.setLong(1, 141);
ResultSet resultSet = ps.executeQuery();
System.out.println(resultSet);
while (resultSet.next()) {
System.out.println(resultSet.getLong(1));
}
/* byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{PreparedStatement.class});
String path = "D:/codes/Stomp_Communicate/src/main/java/com/parallel/stomp/test/jdbc/proxy/JdbcProxy.class";
try (FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("代理類class檔案寫入成功");
} catch (Exception e) {
System.out.println("寫檔案錯誤");
}*/
}
}
運作結果
database=xxxx,url=jdbc:mysql://127.0.0.1:3306/xxxx
開始執行時間1547449304808,執行sql語句SELECT * FROM client WHERE id=?
結束執行時間1547449304838,執行sql語句SELECT * FROM client WHERE id=?
position:1value:141
......