不同DataSource种对Oracle数据库url的识别
使用Spring辅助Hibernat操作Oracle数据库,驱动使用 classes12.jar ,先在配置文件applicationContext.xml中配置DataSource,初始使用内
置的DriverManagerDataSource作试验,连接数据库、CRUD记录均无错。这时配置文件中有关dataSource的配置信息如下:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jbdc:oracle:thin:@172.16.0.220:1521:enterinfo"/>
<property name="username" value="enterinfo"/>
<property name="password" value="enterinfo"/>
</bean>
在其它内容均不动的情况下,单纯将DriverManagerDataSource改为Apache DBCP的BasicDataSource, 报错:
Exception in thread "main" org.apache.commons.dbcp.SQLNestedException: Cannot create JDBC driver of class
'oracle.jdbc.driver.OracleDriver' for connect URL ':oracle:thin:@172.16.0.220:1521:enterinfo'
at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1150)
at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:880)
at Test.main(Test.java:16)
Caused by: java.sql.SQLException: No suitable driver
at java.sql.DriverManager.getDriver(Unknown Source)
at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1143)
... 2 more
没合适的驱动是因为驱动程序没有加载吗?不对,刚使用DriverManagerDataSource一点问题均没有。还是因为BasicDataSource类名写错,如
果是这样应该抛ClassNotFoundException,也不对。只有一种可能:URL写错。仔细检查,j,d,b,c,冒号 ...。果不其然,将 jdbc 写成 jbdc
。改正,一切又恢复正常。
真粗心啊!懊恼之余,想想:Spring真神奇,能自动纠正url的错误?能实例化Bean对象、装配对象间关系,管理对象生命周期,还能...? 不
可能!JDBC中DriverManager也好,Spring中DriverManagerDataSource也好以及DBCP的BasiceDataSource也好,无非是对Driver对象的进一步
包装,真正创建连接,识别判断URL正误的是java.sql.Driver接口的实现类,而该类是Oracle官网所下。以此作为指导方向,怀着打破砂锅问
到底的态度,向源码进发:
先打开DriverManagerDataSource的源码(Spring2.5.1版本),虽然Spring 2.5.1版本出来不久,但该类创建时间却较早:since 14.03.2003。找
到 getConnection()方法处,只有一行代码:
return getConnectionFromDriverManager();
再看 getConnectionFromDriverManager(),又只有一行,转而调用 getConnectionFromDriverManager(getUsername(), getPassword());而后
面这个方面也简单,将用户名称和密码封装成 Properties 对象,转而调用 getConnectionFromDriverManager(String url, Properties
props)。最后在该方法中找到建立连接最核心的代码:
return DriverManager.getConnection(url, props);
看到这,我们能理解,为什么说Spring的DriverManagerDataSource没有连接池化功能了。DriverManager.getConnection()无非转而调用
Driver.connect()方法,难道真的是Oracle的驱动问题?继续实验,写一个最简单的JDBC连接程序:
String driverName="oracle.jdbc.driver.OracleDriver";
String url="jbdc:oracle:thin:@172.16.0.220:1521:enterinfo";
String username="enterinfo";
String password="enterinfo";
Connection conn=null;
try{
Class.forName(driverName);
conn=DriverManager.getConnection(url,username,password);
} catch(Exception e) {
e.printStackTrace();
} finally {
if(conn!=null) try {conn.close();}catch(Exception e){}
}
System.out.println(conn);
刻意将url第一个单词 jdbc 写成 jbdc, 能正确连接。写成 jaaa, 能正确连接,写成 aaaa, 能正确连接。去掉,能正确连接。实验结果表明
,这时Oracle驱动压根就未对第一个冒号前的内容进行验正。
至此,粗步得到结论:DriverManagerDataSource仅是对DriverManager的getConnection(url,username,password)进行了简单包装。
那Apache的DBCP中BasicDataSource呢?打开BasicDataSource,同样定位至getConnection()方法处。该方法也是一行:return
createDataSource().getConnection(); 继承定位至 createDataSource() 方法,这个方法就较复杂一点,结合之前的错误提示(at
org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1150)),将目光聚焦在该方法接下来几行:
1140 // Create a JDBC driver instance
1141 Driver driver = null;
1142 try {
1143 driver = DriverManager.getDriver(url);
1144 } catch (Throwable t) {
1145 String message = "Cannot create JDBC driver of class '" +
1146 (driverClassName != null ? driverClassName : "") +
1147 "' for connect URL '" + url + "'";
1148 logWriter.println(message);
1149 t.printStackTrace(logWriter);
1150 throw new SQLNestedException(message, t);
1151 }
这段代码能抛出异常的地方就是第1143行,driver = DriverManager.getDriver(url); 至此,DriverManagerDataSource较之BasicDataSource
能所谓的纠正url错误的地方,就体现在DriverManager类的二个方法上:
DriverManagerDataSource -> DriverManager.getConnection(url,username,password)
BasicDataSource -> DriverManager.getDriver(url)
如果 url 第一个冒号写错或不写,对于Oracle数据库,getConnection(...)能正确获得连接,getDriver(...)会报错。继续深入
java.sql.DriverManager类的源码,首先看 getConnection(...), 该方法中遍历多个所加载的Driver接口的实现类,转而调用
java.sql.Driver的connect(url,pro)方法,最核心的代码是第582行:
Connection result = di.driver.connect(url, info);
再看 getDriver(url) 方法, 最核心的代码是第252行。
252 if (di.driver.acceptsURL(url)) {
253 // Success!
254 println("getDriver returning " + di);
255 return (di.driver);
256 }
该方法如果能接受指定的url, 就返回正确的Driver接口,否则接下来就报错。因此问题的焦点便集中在Driver接口的二个方法上:
方法一:Driver.connect(url,info);
方法二:Driver.acceptsURL(url);
方法一,对于Oracle数据库, 不判断url中第一个冒号前内容正误。而acceptsURL则因url不正确而抛异常。继续前进,源码...? 因不能
获得Oracle数据库有关Driver接口的实现类源码,这个问题看来就此止步。如果说还有什么细节的话,可以继续看看不同的驱动是否会有
不同的表现。顺便说一下,之前的数据库驱动是 classes12.jar, 现在换成ojdbc14.jar。为了得到更全面结果,进一点试验MySQL数据库,所
使用驱动为mysql-connector-java-5.0-nightly-20071116-bin.jar, 试验结果:
在url第一个冒号错误时:
classes12.jar ojdbc14.jar mysqlXXX.jar
方法一:Driver.connect(url,info); -> 正确返回连接 -> 正确返回连接 -> 不能返回连接
方法二:Driver.acceptsURL(url); -> 返回false -> 返回true -> 返回false
因没有Oracle数据库驱动源码,问题分析就此止步。回首分析的路程:
DriverManagerDataSource.getConnection() ->
DriverManager.getConnection(url,pro) ->
Driver.connect(url,pro);
BascicDataSource.getConnection() ->
首先通过DriverManager.getDriver(url)获得Driver对象,再进一点创建连接->
Driver.acceptsURL(url);
结论:
在使用驱动classes12.jar连接Oracle数据库,
. 使用DriverManagerDataSource创建连接底层依赖的Driver.connect(url,info)不对url第一个冒号前内容正误作判断;
. 使用BasicDataSource创建连接底层依赖的Driver.acceptsURL(url)对url第一个冒号前内容正误作判断;