Java动态代理监控JDBC连接和执行SQL的脚本参数
2019-01-14 15:05
721 查看
前言
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 ......
相关文章推荐
- JDBC之java数据库的连接与简单的sql语句执行
- java连接数据库和执行静态和动态的sql语句
- Python小脚本,其中需要连接MySQL数据库,在执行sql命令时需要传递参数
- 使用java以及jdbc不使用第三方库执行sql文件脚本
- eclipse 连接 sqlserver2005错误信息:"java.lang.ClassNotFoundException: com.microsoft.jdbc.sqlserver.SQLServerDriver"
- 动态sql,单引号连接变量在存储过程内执行.
- sql server执行动态拼接sql(带传参数)和去掉小数点后0的函数
- 如何在java环境下执行sql脚本文件
- SQL Server中动态SQL执行解析 传参数等
- java 连接SQLServer No suitable driver found for jdbc
- c#和java中执行sql文件脚本的代码(非常有用)
- (sql2005)com.microsoft.sqlserver.jdbc.SQLServerException: 到主机 的 TCP/IP 连接失败。 java.net.ConnectException: Connection refused: con
- .net连接数据库,执行sql脚本
- Java 执行 SQL 脚本文件
- 动态SQL的执行,注:exec sp_executesql 其实可以实现参数查询和输出参数的
- java 执行sql脚本的3种方式 (ant,ibatis,ScriptRunner)
- [转] java 执行sql脚本的3种方式 (ant,ibatis,ScriptRunner)
- java连接mysql,执行sql结果的陷阱
- 使用脚本引擎增加程序运行时动态执行能力(Java篇)