`
chenhua_1984
  • 浏览: 1237821 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

使用Java代理来实现对JDBC驱动的一些扩展

    博客分类:
  • java
阅读更多

     

      我们都知道,编程语言在与数据库打交道的时候,中间需要一个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;

    

写道
JDBC supports four end-to-end metrics, all of which are set on a per-connection basis:

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程序的审计系统

     

     

 

分享到:
评论

相关推荐

    DBeaver安装包和TDengine的JDBC驱动

    它支持任何具有一个JDBC驱动程序数据库。它可以处理任何的外部数据源。 2、taos-jdbcdriver 是 TDengine 的官方 Java 语言连接器,Java 开发人员可以通过它开发存取 TDengine 数据库的应用软件。taos-jdbcdriver ...

    微软开源的JDBC 驱动 MSSQL-JDBC.zip

    用于 SQL Server 的 Microsoft JDBC 驱动程序是 Type 4 JDBC 驱动程序,通过 Java 平台企业版中提供的标准 JDBC 应用程序接口(API)提供数据库连接。 驱动程序提供从任何 Java 应用程序、应用程序服务器或启用 Java...

    JAVA扩展包 整理 有关jdbc 和xml的jar包驱动dom4j等

    各种驱动 整理的 比较全面dom4j-1.6.1.jar,castor-1.2.jar,commons-logging-1.0.4.jar,jaxen-1.1-beta-6.jar等 对需要资源的朋友比较有帮助

    Java数据库接口JDBC介绍

    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:JDBC的SQLite/Spatialite驱动程序

    java芋道源码SQLite JDBC 驱动程序 Taro L. Saito 开发的 SQLite JDBC 驱动程序是使 Java 能够访问数据库文件的扩展。 我们的 SQLiteJDBC 库作为 的一部分开发,不需要配置,因为所有适用于 Windows、Mac OS X、...

    java jdk1.8 连接sqlserver数据库使用的扩展jar包-sqljdbc42

    压缩包中包含两个文件,分别是jre7和jre8,分别用于jdk1.7和jdk1.8,我用的是1.8,肯定没问题,1.7没试过

    JDBC笔记 JDBC笔记

    (Class) 管理一组JDBC驱动程序的基本服务。作为初始化的一部分,此接口会尝试加载在”jdbc.drivers”系统属性中引用的驱动程序。只是一个辅助类,是工具。 java.sql.Statement 用于执行静态SQL语句并返回其生成...

    在java中实现对access数据库的远程访问

    (java 中实现对 access 数据库的远程访问) java 中实现对 access 数据库的远程访问是指在 java 中通过远程访问 access 数据库,以满足项目中的需求。在实际项目中,可能会遇到这样的问题:A 服务器上的应用程序需要...

    openGauss-5.1.0-JDBC.tar.gz

    通过使用 openGauss-5.1.0-JDBC.tar.gz 中的 JDBC 驱动程序,开发人员可以在他们的 Java 应用程序中集成 OpenGauss 数据库,并利用其强大的功能和性能优势。 该压缩文件可以被用于将 OpenGauss 数据库的 JDBC 驱动...

    java开源包4

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    Java连接Oracle数据库的各种方法

    JAVA在跨平台开发与INTERNET开发中已经比较流行,ORACLE8i及以后的版本中都包含了对在数据库中运行JAVA的扩展支持,这里有两种方法可以使用:JDBC:与ODBC类似, JDBC 提供了一个驱动接口使你可以在JAVA程序中访问...

    java基础编程必须知道的:SPI、反射、位运算

    SPI(Service Provider Interface)是 Java 中一种基于接口的服务发现机制,旨在实现代码解耦和可扩展性。...对于 Java 基础编程,了解 SPI 机制能够帮助您更好地理解和使用不同的服务提供者,以及实现动态扩展功能。

    JAVA上百实例源码以及开源项目

     util实现Java图片水印添加功能,有添加图片水印和文字水印,可以设置水印位置,透明度、设置对线段锯齿状边缘处理、水印图片的路径,水印一般格式是gif,png,这种图片可以设置透明度、水印旋转等,可以参考代码...

    java开源包3

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    jdbc4sapnw:JDBC驱动程序,用于访问基于SAP NetWeaver的系统-开源

    jdbc4sapnw是用于SAP NetWeaver ABAP堆栈系统的只读jdbc驱动程序。 该驱动程序使用SAP Java Connector 3(sapjco3)中间件来调用SAP系统。 默认情况下,SAP系统不需要扩展。 仅可用的远程功能将用于从树液中提取数据...

    java开源包8

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

    JAVA_API1.6文档(中文)

    javax.naming.spi 提供一些方法来动态地插入对通过 javax.naming 和相关包访问命名和目录服务的支持。 javax.net 提供用于网络应用程序的类。 javax.net.ssl 提供用于安全套接字包的类。 javax.print 为 JavaTM ...

    论文研究-CJDBC:可扩展的数据库集群中间件.pdf

    它通过JDBC接口引进了一个虚拟数据库,只要基于拥有JDBC驱动的数据库上面的应用程序,程序无须任何改动就能运行。CJDBC具有灵活的体系结构,支持大型复杂的数据库集群体系结构,为数据库集群提供的高性能、强容错...

    JAVA上百实例源码以及开源项目源代码

     util实现Java图片水印添加功能,有添加图片水印和文字水印,可以设置水印位置,透明度、设置对线段锯齿状边缘处理、水印图片的路径,水印一般格式是gif,png,这种图片可以设置透明度、水印旋转等,可以参考代码...

    java开源包11

    WebSocket4J 是一个用 Java 实现的 WebSocket 协议的类库,可使用 Java 来构建交互式 Web 应用。WebSocket4J 并未实现客户端通讯协议,所以不能用它来连接 WebSocket 服务器。 Struts验证码插件 JCaptcha4Struts2 ...

Global site tag (gtag.js) - Google Analytics