使用web服务器配置jndi时怎么在项目中取出原生的OracleConnection

使用web服务器配置jndi时如何在项目中取出原生的OracleConnection
当我们使用web服务器配置JNDI,而在项目中使用dataSource.getConnection()时取出的链接往往是经过web容器包装后的Connection,如Websphere包装后的连接是com.ibm.ws.rsadapter.jdbc.WSJdbcConnection,但在应用中有时需要使用原生的OracleConnection(包括其子类)来进行特定的操作,比如使用Oracle AQ时就必须要用OracleConnection。本文将简单的介绍如何对取出的连接进行转换获取需要的连接,以及在tomcat下如何配置,让取出的Connection就是OracleConnection家的。

1.对取出的连接进行转换获取需要的连接
当然你可以自己编写代码,不过这件事spring已经帮忙解决了,spring提供的NativeJdbcExtractor可以很出色的完成这个任务,针对不同的web服务器有不同的实现类:
使用web服务器配置jndi时怎么在项目中取出原生的OracleConnection
下面一段简单的代码是依靠NativeJdbcExtractor为Oracle AQ获取原生的OracleConnection
package com.lc.oracle.aq;

import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.sql.DataSource;

import oracle.jdbc.OracleConnection;
import oracle.jms.AQjmsQueueConnectionFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;


public class OracleAQConnectionFactory implements ConnectionFactory {
    
	protected static final Logger logger = LoggerFactory.getLogger(OracleAQConnectionFactory.class);
	
	private DataSource dataSource;
    
    private NativeJdbcExtractor nativeJdbcExtractor;
    
	public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }    

	public void setNativeJdbcExtractor(NativeJdbcExtractor nativeJdbcExtractor) {
		this.nativeJdbcExtractor = nativeJdbcExtractor;
	}

	@Override
    public javax.jms.Connection createConnection() throws JMSException {
		java.sql.Connection con = DataSourceUtils.getConnection(this.dataSource);
        java.sql.Connection conToUse = con;
		if (!(con instanceof OracleConnection)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Unwrapping JDBC Connection of type:" + con.getClass().getName());
			}
			try {
				conToUse = (OracleConnection) this.nativeJdbcExtractor.getNativeConnection(con);
				if (logger.isDebugEnabled()) {
					logger.debug("Using Native JDBC Connection [" + conToUse + "]");
				}
			} catch (Exception e) {
				throw new RuntimeException("Error unwrapping the Oracle Connection: " + e.getMessage(), e);
			}
		}
		return AQjmsQueueConnectionFactory.createQueueConnection(conToUse);
    }

    @Override
    public javax.jms.Connection createConnection(String username, String password) throws JMSException {
        throw new UnsupportedOperationException("creating connection with explicit username/password not supported");
    }
}

当然如果你的项目中使用了Oracle AQ,完全不需要这么做,你可以使用以下代码:
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/test" proxy-interface="javax.sql.DataSource" />

<!-- Oracle Advanced Queue Connection Factory -->
	<orcl:aq-jms-connection-factory id="connectionFactory" connection-factory-type="QUEUE_CONNECTION"
		data-source="dataSource" native-jdbc-extractor="webSphereNativeJdbcExtractor"
		use-local-data-source-transaction="true" />
	
	<!-- Jdbc Extractor -->
	<bean id="webSphereNativeJdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.WebSphereNativeJdbcExtractor" />

既然是这样,那么我给出上面那段简单代码是什么用意呢?也许你已经发现上面那段代码在获取连接时并没有使用dataSource.getConnection(),而使用的是DataSourceUtils.getConnection(this.dataSource),这么做的目的是为了防止连接泄漏,这篇文章里有很好的讲解http://www.ibm.com/developerworks/cn/java/j-lo-spring-ts3/,我在这里点出来是为了大家更好的学习。

2.在tomcat下如何配置,让取出的Connection就是OracleConnection?
我们一般在tomcat下配置jndi会使用下面的方式:
<Resource name="jdbc/test"
            auth="Container"
            type="javax.sql.DataSource"
            username="dbusername"
            password="dbpassword"
            driverClassName="oracle.jdbc.driver.OracleDriver"
            url="jdbc:oracle:thin:@ip:1521:orcl"
            maxActive="100"
            maxIdle="10"
            maxWait="10000"/>

这样配置在使用时不经转换就会报错:
使用web服务器配置jndi时怎么在项目中取出原生的OracleConnection
(图中的oracle.jdbc.internal.OracleConnection extends oracle.jdbc.OracleConnection)

如果要让取出的连接不经转换就是Oracle的Connection,需要把type和driverClassName改成oracle.jdbc.pool.OracleDataSource,factory默认是org.apache.commons.dbcp.BasicDataSourceFactory,改成oracle.jdbc.pool.OracleDataSourceFactory,这时你再测试就会报错:用户名或密码错误,这个问题很奇怪,需要把上面的username改成user,至于为什么,没有找到相关的文档说明。
修改之后的配置如下:
<Resource name="jdbc/test"
            auth="Container"
            type="oracle.jdbc.pool.OracleDataSource"
            factory="oracle.jdbc.pool.OracleDataSourceFactory"
            user="dbusername"
            password="dbpassword"
            driverClassName="oracle.jdbc.pool.OracleDataSource"
            url="jdbc:oracle:thin:@ip:1521:orcl"
            maxActive="100"
            maxIdle="10"
            maxWait="10000"/>