我们都知道,编程语言在与数据库打交道的时候,中间需要一个Driver,这个驱动,一般由数据库厂商开发提供,根据不同的数据库版本而提供不同版本的驱动,Oracle数据库对Java的版本就有好几种:比如Ojdbc14.jar等,这些驱动,通常实现了有JSR定义好的接口规范,比如Java.sql.connection,java.sql.Driver;接口等。
public interface Connection extends Wrapper { Statement createStatement() throws SQLException; PreparedStatement prepareStatement(String sql)throws SQLException; CallableStatement prepareCall(String sql) throws SQLException;
在Java通过JDBC驱动与Oracle数据的交互过程中,有没有办法在驱动层面实现对Sql语句执行过程的监控呢?利用Java的动态代理即可以实现。Java开发包中包含了对动态代理的支持,但是其实现只支持对接口的的实现。 其实现主要通过java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。我们知道,通过动态代理,可以拦截请求(或则说对请求做增强),那么我们通过这一点可以对JDBC的执行过程做增强处理。
JDBC在和Oracle交互的过程,主要经过以下几个步骤:
首先获取连接 getConnection,获取连接的过程需要注册驱动
public class DriverLoggingProxy implements Driver { static final Logger logger = LoggerFactory.getLogger(DriverLoggingProxy.class); static final String urlPrefix = "jdbc:jdbcdslog:"; static final String targetDriverParameter = "targetDriver"; private static DriverLoggingProxy defaultDriver; static { try { if(defaultDriver == null) { defaultDriver = new DriverLoggingProxy(); DriverManager.registerDriver(defaultDriver); } }catch (Exception exception) { } }
接着:构造Statement对象
然后:执行Execute
最后:获取结果,提交事务(Commit)
大致可可以分为这四个步骤
我们知道JDBC对数据库的处理构造Statement对象主要提供了三种接口方式:如下
createStatement() //最容用的方式,缺点容易被注入攻击 prepareStatement(String sql) //防止SQL注入 prepareCall(String sql) //存储过程调用
利用Java的动态代理,我们则可以实现在Execute的前后增加业务逻辑,来实现我们对Sql语句的执行时间的一个监控(也就是Sql的性能监控)
public class StatementLoggingProxy implements InvocationHandler { static final Logger logger = LoggerFactory.getLogger(StatementLoggingProxy.class); Object targetStatement = null; static List executeMethods = Arrays.asList(new String[] { "execute", "executeQuery", "executeUpdate","executeBatch" }); public StatementLoggingProxy(Object statement) { targetStatement = statement; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object r = null; try { boolean toLog = (StatementLogger.logger.isInfoEnabled() || SlowQueryLogger.logger .isInfoEnabled()) && executeMethods.contains(method.getName()); long t1 = 0; if (toLog) t1 = System.currentTimeMillis(); if(executeMethods.contains(method.getName())){ ConnectionAspectFacade.createStatementAdvice(this.targetStatement); } r = method.invoke(targetStatement, args); if (toLog) { long t2 = System.currentTimeMillis(); StringBuffer sb = LogUtils.createLogEntry(method, args[0], null, null); long time = t2 - t1; String logEntry = sb.append(" ").append(time).append(" ms.") .toString(); StatementLogger.logger.info(logEntry); if (time >= ConfigurationParameters.slowQueryThreshold) SlowQueryLogger.logger.info(logEntry); } return r; } catch (Throwable t) { LogUtils.handleException(t, StatementLogger.logger, LogUtils .createLogEntry(method, args[0], null, null)); } return r; }
同时,也可以利用Oracle的Identifi_context来实现Sql语句与执行的终端绑定,在Oracle的Session列表里有一个Identifi字段,可以通过JDBC 驱动的setEndToEndMetrics 方法来设置值(浏览器端的身份信息),这样,任何一个SQL语句执行的时候,Oracle都能知道是哪个终端发起的,那么我要怎么才知道浏览器端的人是谁呢?这是一个身份识别的问题,利用Filter就可以实现这一点。
我们BS程序通常都是有Session的,用来存放登录的身份相关信息
在向Server发起Http请求的时候,身份信息是要被验证的,当我们的身份信息在登录的时候,验证成功,那么身份信息会被保存到Session里面,我们在Web程序最前面加入一个Filter(拦截器),拦截Http请求,当有请求发生的时候,首先我们可以拿到他的Ip地址
HttpServletRequest request = (HttpServletRequest) req; String ip = request.getHeader("X-Forwarded-For"); if (ip != null && !"".equals(ip)) { logger.debug("origin ip:" + ip + " will replace proxy ip:" + req.getRemoteAddr()); } else { ip = req.getRemoteAddr(); } return ip;
X-Forwarded-For //这里指硬件的负载均衡(F5,Array,RedWare等)需要配置的参数
而后,我们可以采用Session迭代的方式,迭代出当前的Session信息,通过Session信息枚举的分析,分析出Session的结构(这个步骤需要测试阶段完成),然后记录下来,在每次Http请求的过程中,根据分析到的Session结构去获取Session信息(这里我们其实主要要取的是Username),加上前面的Ip地址,这样就可以确定到底是哪个”人“了。
拿到人以后,需要送去给后台的JDBC驱动,以至于让Oracle也知道这条SQL语句是谁执行的,那么我们可以通过 ThreadLocal identityContexts = new ThreadLocal() 绑定实现,把身份信息通过ThreadLocal与后台Http请求绑定,通过
setEndToEndMetrics方法传递给Jdbc的Connection
示例代码:
public class GenericLoggingProxy implements InvocationHandler { static final Logger logger = LoggerFactory.getLogger(GenericLoggingProxy.class); static List methodsBlackList = Arrays.asList(new String[] { "getAutoCommit", "getCatalog", "getTypeMap", "clearWarnings", "setAutoCommit", "getFetchSize", "setFetchSize", "commit" }); private Object target = null; public GenericLoggingProxy(Object target) { this.target = target; } public GenericLoggingProxy(Object target, String sql) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object r=null; try { r = method.invoke(target, args); if ("getConnection".equals(method.getName())) { r = (Connection) ConnectionLoggingProxy.wrap((Connection) r); return r; } if (method.getName().equals("prepareCall") || method.getName().equals("prepareStatement")) r = wrap(r, (String) args[0]); else r = wrap(r, null); return r; } catch (Throwable t) { LogUtils.handleException(t, ConnectionLogger.logger, LogUtils .createLogEntry(method, null, null, null)); } return r; }
if (r instanceof CallableStatement) return wrapByCallableStatementProxy(r, sql); if (r instanceof PreparedStatement) return wrapByPreparedStatementProxy(r, sql); if (r instanceof Statement) return wrapByStatementProxy(r); // if (r instanceof ResultSet) // return ResultSetLoggingProxy.wrapByResultSetProxy((ResultSet) r); return r;
Action
This is a String
ClientId
This is a String
ExecutionContextId
This is a combination of String and short SequenceNumber)
Module
This is a String
String metrics[] = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX]; String clientIdentifier = identityContext.getClientIdentifier(true); metrics[OracleConnection.END_TO_END_CLIENTID_INDEX] = clientIdentifier; metrics[OracleConnection.END_TO_END_ACTION_INDEX] = identityContext .getAction(); // metrics[OracleConnection.END_TO_END_MODULE_INDEX] = identityContext // .getModule(); Statement st=null; OracleConnection conns =null; try { if (logger.isDebugEnabled()) { logger.debug("call setEndToEndMetrics with [clientId=" + metrics[OracleConnection.END_TO_END_CLIENTID_INDEX] + "]"); } // OracleConnection conn = (OracleConnection) adviceContext // .getCurrentConnection(); st=(Statement)statement; conns = (OracleConnection)st.getConnection(); conns.setEndToEndMetrics(metrics, (short) 0);
在Oracle段,可以 利用Oracle的VPD技术,可以实现WEB程序的审计系统
相关推荐
它支持任何具有一个JDBC驱动程序数据库。它可以处理任何的外部数据源。 2、taos-jdbcdriver 是 TDengine 的官方 Java 语言连接器,Java 开发人员可以通过它开发存取 TDengine 数据库的应用软件。taos-jdbcdriver ...
用于 SQL Server 的 Microsoft JDBC 驱动程序是 Type 4 JDBC 驱动程序,通过 Java 平台企业版中提供的标准 JDBC 应用程序接口(API)提供数据库连接。 驱动程序提供从任何 Java 应用程序、应用程序服务器或启用 Java...
各种驱动 整理的 比较全面dom4j-1.6.1.jar,castor-1.2.jar,commons-logging-1.0.4.jar,jaxen-1.1-beta-6.jar等 对需要资源的朋友比较有帮助
2.2.1 跟踪可用驱动程序 6 2.2.2 建立连接 7 2.3 Statement对象 7 2.3.1 创建Statement对象 8 2.3.2 使用Statement对象执行语句 8 2.3.3 语句完成 8 2.3.4 语句完成 9 2.4 ResultSet对象 9 2.4.1 行和光标 9 ...
java芋道源码SQLite JDBC 驱动程序 Taro L. Saito 开发的 SQLite JDBC 驱动程序是使 Java 能够访问数据库文件的扩展。 我们的 SQLiteJDBC 库作为 的一部分开发,不需要配置,因为所有适用于 Windows、Mac OS X、...
压缩包中包含两个文件,分别是jre7和jre8,分别用于jdk1.7和jdk1.8,我用的是1.8,肯定没问题,1.7没试过
(Class) 管理一组JDBC驱动程序的基本服务。作为初始化的一部分,此接口会尝试加载在”jdbc.drivers”系统属性中引用的驱动程序。只是一个辅助类,是工具。 java.sql.Statement 用于执行静态SQL语句并返回其生成...
(java 中实现对 access 数据库的远程访问) java 中实现对 access 数据库的远程访问是指在 java 中通过远程访问 access 数据库,以满足项目中的需求。在实际项目中,可能会遇到这样的问题:A 服务器上的应用程序需要...
通过使用 openGauss-5.1.0-JDBC.tar.gz 中的 JDBC 驱动程序,开发人员可以在他们的 Java 应用程序中集成 OpenGauss 数据库,并利用其强大的功能和性能优势。 该压缩文件可以被用于将 OpenGauss 数据库的 JDBC 驱动...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
JAVA在跨平台开发与INTERNET开发中已经比较流行,ORACLE8i及以后的版本中都包含了对在数据库中运行JAVA的扩展支持,这里有两种方法可以使用:JDBC:与ODBC类似, JDBC 提供了一个驱动接口使你可以在JAVA程序中访问...
SPI(Service Provider Interface)是 Java 中一种基于接口的服务发现机制,旨在实现代码解耦和可扩展性。...对于 Java 基础编程,了解 SPI 机制能够帮助您更好地理解和使用不同的服务提供者,以及实现动态扩展功能。
util实现Java图片水印添加功能,有添加图片水印和文字水印,可以设置水印位置,透明度、设置对线段锯齿状边缘处理、水印图片的路径,水印一般格式是gif,png,这种图片可以设置透明度、水印旋转等,可以参考代码...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
jdbc4sapnw是用于SAP NetWeaver ABAP堆栈系统的只读jdbc驱动程序。 该驱动程序使用SAP Java Connector 3(sapjco3)中间件来调用SAP系统。 默认情况下,SAP系统不需要扩展。 仅可用的远程功能将用于从树液中提取数据...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...
javax.naming.spi 提供一些方法来动态地插入对通过 javax.naming 和相关包访问命名和目录服务的支持。 javax.net 提供用于网络应用程序的类。 javax.net.ssl 提供用于安全套接字包的类。 javax.print 为 JavaTM ...
它通过JDBC接口引进了一个虚拟数据库,只要基于拥有JDBC驱动的数据库上面的应用程序,程序无须任何改动就能运行。CJDBC具有灵活的体系结构,支持大型复杂的数据库集群体系结构,为数据库集群提供的高性能、强容错...
util实现Java图片水印添加功能,有添加图片水印和文字水印,可以设置水印位置,透明度、设置对线段锯齿状边缘处理、水印图片的路径,水印一般格式是gif,png,这种图片可以设置透明度、水印旋转等,可以参考代码...
WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...