Tomca教程
Tomcat Manager
Tomcat Realm 配置
Tomcat 安全管理
Tomcat JNDI 资源
Tomcat JDBC 数据源
Tomcat 类加载机制
Tomcat JSPs
Tomcat SSL/TLS配置
Tomcat SSI
Tomcat CGI
Tomcat 代理支持
Tomcat MBean 描述符
Tomcat 默认 Servlet
Tomcat 集群
Tomcat 连接器
Tomcat监控与管理
Tomcat 日志机制
Tomcat 基于 APR 的原生库
Tomcat 虚拟主机
Tomcat 高级 IO 机制
Tomcat 附加组件
Tomcat 安全性注意事项
Tomcat Windows 服务
Tomcat Windows 认证
Tomcat 的 JDBC 连接池
Tomcat WebSocket 支持
Tomcat 重写机制

Oracle 8i 与 OCI 客户端

虽然并不能严格地解决如何使用 OCI 客户端来创建 JNDI 数据源的问题,但这些注意事项却能和上文提到的 Oracle 与 DBCP 解决方案结合起来使用。

为了使用 OCI 驱动,应该先安装一个 Oracle 客户。你应该已经通过光盘安装好了 Oracle 8i(8.1.7)客户端,并从 otn.oracle.com 下载了适用的 JDBC/OCI 驱动(Oracle8i 8.1.7.1 JDBC/OCI 驱动)。

将 classes12.zip 重命名为 classes12.jar 后,将其复制到 $CATALINA_HOME/lib 中。根据 Tomcat 的版本以及你所使用的 JDK,你可能还必须该文件中的删除 javax.sql.* 类。

连接起来

确保在 $PATH 或 LD_LIBRARY_PATH(可能在 $ORAHOME\bin)目录下存在 ocijdbc8.dll 或 .so 文件,另外还要确认能否使用 System.loadLibrary("ocijdbc8"); 这样的简单测试程序加载本地库。

下面你应该创建一个简单测试用 servlet 或 jsp,其中应该包含以下关键代码:

DriverManager.registerDriver(new
oracle.jdbc.driver.OracleDriver());
conn =
DriverManager.getConnection("jdbc:oracle:oci8:@database","username","password");

目前数据库是 host:port:SID 形式,如果你试图访问测试用servlet/jsp,那么你会得到一个 ServletException 异常,造成异常的根本原因在于 java.lang.UnsatisfiedLinkError:get_env_handle。

分析一下,首先 UnsatisfiedLinkError 表明:

JDBC 类文件和 Oracle 客户端版本不匹配。消息中透露出的意思是没有找到需要的库文件。比如,你可能使用 Oracle 8.1.6 的 class12.zip 文件,而 Oracle 客户端版本则是 8.1.5。classeXXXs.zip 文件必须与 Oracle 客户端文件版本相一致。

出现了一个 $PATH, LD_LIBRARY_PATH 问题。

  • 有报告称,忽略从 otn 网站下载的驱动,使用 $ORAHOME\jdbc\lib 目录中的 class12.zip 文件,同样能够正常运作。

接下来,你可能还会遇到另一个错误消息:ORA-06401 NETCMN: invalid driver designator。

Oracle 文档是这么说的:“异常原因:登录(连接)字符串包含一个不合法的驱动标识符。解决方法:修改字符串,重新提交。”所以,如下面这样来修改数据库(host:port:SID)连接字符串:(description=(address=(host=myhost)(protocol=tcp)(port=1521))(connect_data=(sid=orcl)))

常见问题

下面是一些 Web 应用在使用数据库时经常会遇到的问题,以及一些应对技巧。

数据库连接间歇性失败

Tomcat 运行在 JVM 中。JVM 周期性地会执行垃圾回收(GC),清除不再使用的 Java 对象。当 JVM 执行 GC 时,Tomcat 中的代码执行就会终止。如果配置好的数据库连接建立的最长时间小于垃圾回收的时间,数据库连接就会失败。

在启动 Tomcat 时,将 -verbose:gc 参数添加到 CATALINA_OPTS 环境变量中,就能知道垃圾回收所占用的时间了。在启用 verbose:gc 后, $CATALINA_BASE/logs/catalina.out 日志文件就能包含每次垃圾回收的数据,其中也包括它所占用的时间。

正确调整 JVM 后,垃圾回收可以做到在 99% 的情况下占用时间不超过 1 秒。剩余的情况则只占用几秒钟的时间,只有极少数情况下 GC 会占用超过 10 秒钟的时间。

保证让数据库连接超时设定在 10~15 秒。对于 DBCP,可以使用 maxWaitMillis 参数来设置。

随机性的连接关闭异常

当某一请求从连接池中获取了一个数据库连接,然后关闭了它两次时,往往会出现这样的异常消息。使用连接池时,关闭连接,就会把它归还给连接池,以便之后其他的请求能够重用该连接,而并不会关闭连接。Tomcat 使用多个线程来处理并发请求。下面这个范例就演示了,在 Tomcat 中,一系列事件导致了这种错误。

运行在线程 1 中的请求 1 获取了一个连接。

请求 1 关闭了数据库连接。

JVM 将运行的线程切换为线程 2

线程 2 中运行的请求 2 获取了一个数据库连接。
(同一个数据库连接刚被请求 1 关闭)

JVM 又将运行的线程切换回为线程 1

请求 1 第二次关闭了数据库连接。

JVM 将运行的线程切换回线程 2

请求 2 和线程 2 试图使用数据库连接,但却失败了。因为请求 1 已经关闭了它。

 }
  Connection conn = null;
  Statement stmt = null;  // Or PreparedStatement if needed
  ResultSet rs = null;
  try {
    conn = ... get connection from connection pool ...
    stmt = conn.createStatement("select ...");
    rs = stmt.executeQuery();
    ... iterate through the result set ...
    rs.close();
    rs = null;
    stmt.close();
    stmt = null;
    conn.close(); // Return to connection pool
    conn = null;  // Make sure we don't close it twice
  } catch (SQLException e) {
    ... deal with errors ...
  } finally {
    // Always make sure result sets and statements are closed,
    // and the connection is returned to the pool
    if (rs != null) {
      try { rs.close(); } catch (SQLException e) { ; }
      rs = null;
    }
    if (stmt != null) {
      try { stmt.close(); } catch (SQLException e) { ; }
      stmt = null;
    }
    if (conn != null) {
      try { conn.close(); } catch (SQLException e) { ; }
      conn = null;
    }

上下文与全局命名资源

注意,虽然在上面的说明中,把 JNDI 声明放在一个 Context 元素里面,但还是有可能(而且有时更需要)把这些声明放在服务器配置文件的 GlobalNamingResources 区域。被放置在 GlobalNamingResources 区域的资源将会被服务器的各个上下文所共享。

JNDI 资源命名和 Realm 交互

为了让 Realm 能运作,realm 必须指向定义在  或  区域中的数据源,而不是 重新命名的数据源。