JDBC 数据源
简介
许多 Web 应用都需要 JDBC 驱动来访问数据库,以便能够支持该应用所需要的功能。Java EE 平台规范要求 Java EE 应用服务器针对该需求提供一个 DataSource 实现(也就是说,用于 JDBC 连接的连接池)。Tomcat 就能提供同样的支持,因此在 Tomcat 上,由于使用了这种服务,基于数据库的应用可以不用修改就能移植到任何 Java EE 服务器上运行。
要想详细了解 JDBC,可以参考以下网站或信息来源:
http://www.oracle.com/technetwork/java/javase/jdbc/index.html 一个 Java 数据库连接性能相关信息网站。
http://java.sun.com/j2se/1.3/docs/guide/jdbc/spec2/jdbc2.1.frame.html JDBC 2.1 API 规范。
http://java.sun.com/products/jdbc/jdbc20.stdext.pdf JDBC 2.0 标准扩展 API(包括 javax.sql.DataSource API)。该包现在被称为“JDBC 可选包”。
http://www.oracle.com/technetwork/java/javaee/overview/index.htm Java EE 平台规范(介绍了所有 Java EE 平台必须为应用提供的 JDBC 附加功能)
注意:Tomcat 默认所支持的数据源是基于 Commons 项目 的 DBCP 连接池。但也可以通过编写自定义的资源工厂,使用其他实现了 javax.sql.DataSource 的连接池,详见下一章节
安装 JDBC 驱动
使用 JDBC 数据源的 JNDI 资源工厂需要一个适合的 JDBC 驱动,要求它既能被 Tomcat 内部类所使用,也能被你的 Web 应用所使用。这很容易实现,只需将驱动的 JAR 文件(或多个文件)安装到 $CATALINA_HOME/lib 目录中即可,这样资源工厂和应用就都能使用了这一驱动了。
声明资源需求
下一步,修改 Web 应用的部署描述符文件(/WEB-INF/web.xml),声明 JNDI 名称以便借此查找预配置的数据源。按照惯例,所有这样的名称都应该在jdbc 子上下文中声明(这个“子”是相对于标准的 java:comp/env 环境命名上下文而言的。java:comp/env 环境命名上下文是所有资源工厂的根引用)。典型的 web.xml 文件应如下所示:
<resource-ref> <description> Resource reference to a factory for java.sql.Connection instances that may be used for talking to a particular database that is configured in the <Context> configurartion for the web application. </description> <res-ref-name> jdbc/EmployeeDB </res-ref-name> <res-type> javax.sql.DataSource </res-type> <res-auth> Container </res-auth> </resource-ref>
警告:一定要遵从 Web 应用部署描述符文件中 DTD 所需要的元素顺序。关于这点,可参看Servlet 规范中的解释。
使用资源
资源引用的典型用例如下所示:
Context initCtx = new InitialContext(); Context envCtx = (Context) initCtx.lookup("java:comp/env"); DataSource ds = (DataSource) envCtx.lookup("jdbc/EmployeeDB"); Connection conn = ds.getConnection(); ... use this connection to access the database ... conn.close();
注意,该应用所用的资源引用名与 Web 应用部署符中声明的完全相同。这是与下文会讲到的 <Context> 元素里所配置的资源工厂相匹配的。
配置 Tomcat 资源工厂
为了配置 Tomcat 的资源工厂,在 <Context> 元素中添加以下元素:
<Context ...> ... <Resource name="jdbc/EmployeeDB" auth="Container" type="javax.sql.DataSource" username="dbusername" password="dbpassword" driverClassName="org.hsql.jdbcDriver" url="jdbc:HypersonicSQL:database" maxTotal="8" maxIdle="4"/> ... </Context>
注意上述代码中的资源名(这里是 jdbc/EmployeeDB)必须跟 Web 应用部署描述符文件中指定的值相同。
该例假定使用的是 HypersonicSQL 数据库 JDBC 驱动。可自定义 driverClassName 和 driverName 参数,使其匹配实际数据库的 JDBC 驱动与连接 URL。
Tomcat 标准数据源资源工厂(org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory)的配置属性如下:
driverClassName 所用的 JDBC 驱动的完全合格的类名。
username JDBC 驱动所要接受的数据库用户名。
password JDBC 驱动所要接受的数据库密码。
url 传入 JDBC 驱动的连接 URL(为了向后兼容性考虑,也同样认可
driverName
属性,即与之等同)。initialSize 连接池初始化过程中所创建的初始连接的数目。默认为 0。
maxTotal 连接池同时所能分配的最大连接数。默认为 8。
minIdle 连接池中同时空闲的最少连接数。默认为 0。
maxIdle 连接池中同时空闲的最多连接数。默认为 8。
maxWaitMillis 在抛出异常前,连接池等待(没有可用的连接)连接返回的最长等待毫秒数。默认为 -1(无限长时间)。
还有一些额外的用来验证连接的属性,如下所示:
validationQuery 在连接返回应用之前,连接池用于验证连接的 SQL 查询。如果指定了该属性值,查询必须是一个至少能返回一行的 SQL SELECT 语句。
validationQueryTimeout 验证查询返回的超时时间。默认为 -1(无限长时间)。
testOnBorrow 布尔值,true 或 false,针对每次从连接池中借出的连接,判断是否应用验证查询对其验证。默认:true。
testOnReturn 布尔值,true 或 false,针对每次归还给连接池的连接,判断是否应用验证查询对其验证。默认:false。
可选的 evictor thread 会清除空闲较长时间的连接,从而缩小连接池。evictor thread 不受 minIdle 属性值的空闲。注意,如果你只想通过配置的 minIdle 属性来缩小连接池,那么不需要使用 evictor thread。
默认 evictor 是禁用的,另外,可以使用下列属性来配置它:
timeBetweenEvictionRunsMillis
evictor 线程连续运行之间的毫秒数。默认为 -1(禁止)numTestsPerEvictionRun
evictor 每次运行中,evictor 实施的用来检测空闲与否的连接数目。默认为 3。minEvictableIdleTimeMillis
evictor 从连接池中清除某连接后的空闲时间,以毫秒计,默认为 30 * 60 * 1000(30分钟)。testWhileIdle
布尔值,true 或 false。对于在连接池中处于空闲状态的连接,是否应被 evictor 线程通过验证查询来验证。默认为false。
另一个可选特性是对废弃连接的移除。如果应用很久都不把某个连接返回给连接池,那么该连接就被称为废弃连接。连接池就会自动关闭这样的连接,并将其从池中移除。这么做是为了防止应用泄露连接。
默认是禁止废弃连接的,可以通过下列属性来配置:
removeAbandoned 布尔值,true 或 false。确定是否去除连接池中的废弃连接。默认为 false。
removeAbandonedTimeout 经过多少秒后,借用的连接可以认为被废弃。默认为 300。
logAbandoned 布尔值,true 或 false。确定是否需要针对废弃了语句或连接的应用代码来记录堆栈跟踪。如果记录的话,将会带来很大的开销。默认为 false。
最后再介绍一些可以对连接池行为进行进一步微调的属性:
defaultAutoCommit 布尔值,
true
或false
。由连接池所创建的连接的默认自动提交状态。默认为 true。defaultReadOnly 布尔值,
true
或false
。由连接池所创建的连接的默认只读状态。默认为 false。defaultTransactionIsolation 设定默认的事务隔离级别。可取值为:
NONE
、READ_COMMITTED
、READ_UNCOMMITTED
、REPEATABLE_READ
与SERIALIZABLE
。没有默认设置。poolPreparedStatements 布尔值,true 或 false。 是否池化 PreparedStatements 和 CallableStatements。默认为
false
。maxOpenPreparedStatements 同时能被语句池分配的开放语句的最大数目。默认为 -1(无限)
defaultCatalog catalog 默认值。默认值:未设定。
connectionInitSqls 连接建立后运行的一系列 SQL 语句。各个语句间用分号(
;
)进行分隔。默认为:没有语句。connectionInitSqls 传入驱动用于创建连接的驱动特定属性。每一属性都以
name = value
的形式给出,多个属性用分号(;
)进行分隔。默认:没有属性。accessToUnderlyingConnectionAllowed 布尔值,
true
或false
。 是否可访问底层连接。默认为false
。
要想更详细地了解这些属性,请参阅 commons-dbcp 文档。
添加自定义资源工厂
如果标准资源工厂无法满足你的需求,你还可以自己编写资源工厂,然后将其集成到 Tomcat 中,在 Web 应用的 <Context> 元素中配置该工厂的使用方式。在下面的范例中,我们将创建一个资源工厂,只懂得如何 com.mycompany.MyBean bean
编写资源工厂类
你必须编写一个类来实现 JNDI 服务提供者 javax.naming.spi.ObjectFactory 接口。每次 Web 应用在绑定到该工厂(假设该工厂配置中,singleton = "false")的上下文项上调用 lookup() 时,就会调用 getObjectInstance() 方法,该方法有如下这些参数:
Object obj
创建一个能够生成 MyBean 实例的资源工厂,需要像下面这样来创建类:
package com.mycompany; import java.util.Enumeration; import java.util.Hashtable; import javax.naming.Context; import javax.naming.Name; import javax.naming.NamingException; import javax.naming.RefAddr; import javax.naming.Reference; import javax.naming.spi.ObjectFactory; public class MyBeanFactory implements ObjectFactory { public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws NamingException { // Acquire an instance of our specified bean class MyBean bean = new MyBean(); // Customize the bean properties from our attributes Reference ref = (Reference) obj; Enumeration addrs = ref.getAll(); while (addrs.hasMoreElements()) { RefAddr addr = (RefAddr) addrs.nextElement(); String name = addr.getType(); String value = (String) addr.getContent(); if (name.equals("foo")) { bean.setFoo(value); } else if (name.equals("bar")) { try { bean.setBar(Integer.parseInt(value)); } catch (NumberFormatException e) { throw new NamingException("Invalid 'bar' value " + value); } } } // Return the customized instance return (bean); } }
/ Acquire an instance of our specified bean class 需要我们所指定的bean 类的一个实例
// Customize the bean properties from our attributes 从属性中自定义 bean 属性。
// Return the customized instance 返回自定义实例
在上例中,无条件地创建了 com.mycompany.MyBean 类的一个新实例, 并根据工厂配置中的 <ResourceParams> 元素(下文详述)包括的参数来填充这一实例。你应该记住,必须忽略任何名为 factory 的参数——参数应该用来指定工厂类自身的名字(com.mycompany.MyBeanFactory),而不是配置的 bean 属性。
关于 ObjectFactory 的更多信息,可参见 JNDI 服务提供者接口(SPI)规范。
首先参照一个 $CATALINA_HOME/lib 目录中包含所有 JAR 文件的类路径来编译该类。完成之后,将这个工厂类以及相应的 Bean 类解压缩到 $CATALINA_HOME/lib,或者 $CATALINA_HOME/lib 内的一个 JAR 文件中。这样,所需的类文件就能被 Catalina 内部资源与 Web 应用看到了。
声明资源需求
下一步,修改 Web 应用的部署描述符文件(/WEB-INF/web.xml),声明 JNDI 名称以便借此请求该 bean 的新实例。最简单的方法是使用 <resource-env-ref> 元素,如下所示:
<resource-env-ref> <description> Object factory for MyBean instances. </description> <resource-env-ref-name> bean/MyBeanFactory </resource-env-ref-name> <resource-env-ref-type> com.mycompany.MyBean </resource-env-ref-type> <resource-env-ref>
警告:一定要遵从 Web 应用部署描述符文件中 DTD 所需要的元素顺序。关于这点,可参看Servlet 规范中的解释。
使用资源
资源引用的典型用例如下所示:
Context initCtx = new InitialContext(); Context envCtx = (Context) initCtx.lookup("java:comp/env"); MyBean bean = (MyBean) envCtx.lookup("bean/MyBeanFactory"); writer.println("foo = " + bean.getFoo() + ", bar = " + bean.getBar());
配置 Tomcat 资源工厂
为了配置 Tomcat 的资源工厂,在 <Context> 元素中添加以下元素:
<Context ...> ... <Resource name="bean/MyBeanFactory" auth="Container" type="com.mycompany.MyBean" factory="com.mycompany.MyBeanFactory" singleton="false" bar="23"/> ... </Context>
注意上述代码中的资源名(这里是 bean/MyBeanFactory)必须跟 Web 应用部署描述符文件中指定的值相同。另外,我们还初始化了 bar 属性值,从而在返回新 bean 时,导致 setBar(23) 被调用。由于我们没有初始化 foo 属性(虽然完全可以这样做),所以 bean 将含有构造函数所定义的各种默认值。
另外,你肯定能注意到,从应用开发者的角度来看,资源环境引用的声明,以及请求新实例的编程方式,都跟通用 JavaBean 资源(Generic JavaBean Resources)范例所用方式如出一辙。这揭示了使用 JNDI 资源封装功能的一个优点:只要维持兼容的 API,无需修改使用资源的应用,只需改变底层实现。
感觉本站内容不错,读后有收获?小额赞助,鼓励网站分享出更好的教程