我正在尝试使用tomcat jdbc连接池,并在我的应用程序context.xml文件中定义它。
<Context>
<Resource auth="Container" name="jdbc/iup" type="javax.sql.DataSource"
maxActive="300" maxIdle="30" maxWait="20000"
username="${db.username}" password="${db.password}" driverClassName="net.sf.log4jdbc.DriverSpy"
url="jdbc:log4jdbc:sqlserver://${db.server};databaseName=${db.name}"/>
</Context>
类net.sf.log4jdbc.DriverSpy
在log4jdbc4-1.2.jar
中定义,它位于我的应用程序库文件夹中。它对我来说很好。但是here说,带有驱动程序类的jar应该只放在tomcat lib文件夹中。
Tomcat使用它的BasicDataSource
类来加载驱动程序:
if (driverClassName != null) {
try {
try {
if (driverClassLoader == null) {
Class.forName(driverClassName);
} else {
Class.forName(driverClassName, true, driverClassLoader);
}
} catch (ClassNotFoundException cnfe) {
driverFromCCL = Thread.currentThread(
).getContextClassLoader().loadClass(
driverClassName);
}
} catch (Throwable t) {
String message = "Cannot load JDBC driver class '" +
driverClassName + "'";
logWriter.println(message);
t.printStackTrace(logWriter);
throw new SQLNestedException(message, t);
}
}
driverClassLoader
为null,并且尝试通过Class.forName(driverClassName)
加载驱动程序类。据我所知,在这种情况下,驱动程序类正在加载与BasicDataSource
相同的类加载器实例。这是StandardClassLoader
,如果我的jar在tomcat库中,它将加载该类。在我的情况下抛出异常并使用Thread.currentThread().getContextClassLoader()
,这是WebappClassLoader
实例,可以从webapp lib加载类,它可以。所以我很困惑。为什么说,如果我使用容器资源中的数据源,我必须将我的驱动程序类放在tomcat库中。
请解释,谢谢
答案 0 :(得分:5)
Tomcat会自动将容器管理的连接池添加到类型jaxaz.sql.DataSource
的每个资源。提供此池的库(Commons DBCP的包重命名版本)由共享类加载器加载(在默认配置中与公共加载器相同)。池实现需要能够加载已配置的JDBC驱动程序,并且共享(和公共)加载器无法查看Web应用程序类加载器。因此,带有JDBC驱动程序的JAR需要位于$CATALINA_BASE/lib
目录中,以便可以加载它。
但是,从r754776开始,如果DBCP无法加载指定的驱动程序,则它会回退到Thread的上下文类加载器。如果线程上下文类加载器设置为Web应用程序的类加载器,则可以加载驱动程序。此更改已包含在DBCP 1.3
和1.4
之后,这意味着它已包含在5.5.30
以后,6.0.27
以及每7.0.x
个版本中。它也将出现在每个8.0.x
版本中。
使用 MarkMail 对查询量进行相当不科学的观察表明,Tomcat用户邮件列表中的ClassNotFoundException
问题有所减少,但同样可能会让人们更加意识到问题。
我猜基本问题是这可靠吗?如果在线程上下文类加载器是Web应用程序类加载器时始终实例化DataSource
,那么它将是可靠的。通过JNDI
访问这些资源,这取决于正确设置的线程上下文类加载器。如果不是 - JNDI
将无法找到Web应用程序资源。在此基础上,这应该有效。
全球资源(显然)仍然需要将JDBC
驱动程序放在$CATALINA_HOME/lib
可能导致问题的方案是JDBC
中是否存在JAR
驱动程序$CATALINA_HOME/lib and WEB-INF/lib
。如果Web应用程序试图转换为特定于数据库的对象,那么事情将会失败,因为这将尝试将由共享加载器加载的类转换为由Web应用程序类加载器加载的同名类,这将始终是失败。
简而言之:
WEB-INF/lib
和$CATALINA_[HOME|BASE]/lib
中没有JDBC驱动程序的长期建议6.0.27
开始,可以将JDBC驱动程序打包到Web应用程序中,一切都可以正常工作。对最初的错误/不完整答案表示歉意。这不是我第一次完全忘记我所做的提交,我怀疑它不会是最后一次。