开始使用dbcp
- 开发环境及版本:
- Java Version:JDK 1.6
- IDE:Eclipse 4.3
- dbcp:dbcp 1.4
首先打开eclipse,首先新建一个Maven的pom项目,配置一些常用的插件。具体的pom.xml的如下:
4.0.0
com.xg.testproject
mydbcp
0.0.1-SNAPSHOT
pom
clean compile
org.apache.maven.plugins
maven-clean-plugin
3.0.0
org.apache.maven.plugins
maven-compiler-plugin
3.2
1.6
UTF8
src/main/java
true
**/*.xml
**/*.properties
src/main/resources
true
下载和编译dbcp源码
为了方便查看和学习,在dbcp使用阶段,我选择自己编辑源码,这样做首先我们可以清晰看到dbcp在设计实现的时候,依赖了哪些包,同时也方便我们dubug断点查看它的工作流程,为稍后详细的分析dbcp的设计和使用逻辑打下基础。
dbcp 1.4的源码可以在官网找到:
下载完成后解压,得到dbcp的源码文件,解压的目录结构如下:
在这些文件中,我们仅需要关注src目录和pom.xml文件。其他文件夹,像doc和xdocs都是项目的文档,可以不用理会,剩下的文件一部分是checkstyle,findbugs这类工具的配置,一部分是项目自身的描述文件,各位有兴趣的可以单独去研究一下,这里就不一一介绍了。
接下来,按照下述步骤,将代码迁移到我们自己的项目中:
-
新建项目:
选中我们项目的pom.xml,鼠标右键-》Maven-》New Maven Model Project,得到如下界面:
输入model名称(我自己新建的项目名称是dbcp),点击Finish按钮,完成项目创建。
- 迁移源码:将src/java下的内容,整个copy到dbcp项目下的src/java目录下;
- 添加依赖:打开下载下来的pom.xml,将
标签的内的内容
commons-pool
commons-pool
1.5.4
org.apache.geronimo.specs
geronimo-jta_1.1_spec
1.1.1
true
tomcat
naming-common
5.0.28
test
tomcat
naming-java
5.0.28
test
commons-logging
commons-logging
1.1.1
test
org.apache.geronimo.components
geronimo-transaction
2.0.1
test
copy到dbcp项目的pom.xml的
- 编译项目:选中我们新建的项目,鼠标右键-》Maven-》Update Project
按照上述步骤,如果没有看到报错,就证明dbcp的源码已经编译成功了。
总结一下dbcp的配置
在当前项目基础上,再新建一个jar包,并引入我们刚刚自己新建编译的dbcp源码包(以后统一简称为本地dbcp包),此时,我们就可以着手实现使用dbcp连接池的应用了。像所有的连接池一样,在使用之前,我们要先来看一下都有哪些配置项。在dbcp官网的api页面上,各个配置项都有详细的描述,我们就结合官网的内容,简单总结一下。
按照功能类型,dbcp的配置项可以大致分为以下几类:
- 核心基础配置
- username
- password
- url
- driverClassName
有点JDBC编程基础的人一看就知道上面这几个参数分别是干啥用的了,没有它们,“连接池”中的“连接”,就无从谈起了。它们是dbcp的基本配置,其他属性都有默认值,唯独这四个属性一定要由用户指定,否则就dbcp就无法使用。话说回来,一个连接池不论性能好坏,功能多少,它最重要的作用,就是得到一个java.sql.Connection对象。
- connection属性的配置
- connectionProperties:连接属性,以键值对的形式将下面这些具体的属性串起来
- defaultAutoCommit:是否默认自动提交
- defaultReadOnly:是否为只读
- defaultTransactionIsolation:默认的事务隔离级别
- defaultCatalog:connection的默认路径
这部分配置的作用都是标记从池中获取的connection应该具备的属性,而它们是否生效,就要看具体的JDBC驱动是不是支持了。
- 池属性的配置
- initialSize
- maxActive
- maxIdle
- minIdle
- maxWait
又是一组关键的配置,这组配置的作用控制连接池的容量。
一个稳定的连接池,其对系统资源的占用应该是稳定在一个固定的范围内的,maxActive、maxIdle和minIdle的这三个参数的作用正在于此。首先,maxActive的意思是,池中最多可容纳的活着的连接数量,它是整个连接池的边界,当池中活着的连接达到这个数值时,dbcp将不会再新建connection,而是等待获取其他线程释放的。maxIdle的意思是连接池最多可以保持的连接数,应用场景就是dbcp会定时回收池中那些空闲连接(已激活但未使用的连接),直到池中的数量减少到maxIdle为止。minIdle是maxIdle的反义词,及连接池中最少保持的连接。maxWait定义了获取连接的等待时间,如果超过这个时间则抛出异常(默认配置),initialSize则定义了dbcp在初始化时,新建的连接数量。
- 高可用属性的配置
- connectionInitSqls
- testOnBorrow
- testOnReturn
- validationQuery
- validationQueryTimeout
在连接池的使用过程中保持高可用,是一个优秀的连接池必不可少的素质,那如何做到这一点呢,dbcp给出的答案就在上面这些配置项上。
connectionInitSqls是在一个connection被创建之后调用的一组sql,这组sql可用来记录日志,也可以用来初始化一些数据,总之,经过connectionInitSqls之后的connection一定是正常可用的。testOnBorrow和testOnReturn的关注点则在connection从池中“取出”和“归还”,这两个关键的动作上,当他们被设置为true时,在取出和归还connection时,都需要完成校验,如果校验不通过,这个connection将被销毁。校验的sql由validationQuery定义,且定义的sql语句必须是查询语句,而且查询至少一列。validationQueryTimeout定义的是校验查询时长,如果超过这个时间,则认定为校验失败。
除了上述配置,dbcp在运行时还在内部维护了一个“清理器”(Eviction),主要用于销毁那些已被创建,但长时间未被使用的连接,Eviction在运行的时候,会用到下列属性:
- testWhileIdle:清楚一个连接时是否需要校验
- timeBetweenEvictionRunsMillis:Eviction运行的时间周期
- numTestsPerEvictionRun:Eviction在运行时一次处理几个连接
PreparedStatements是可以缓存是,尤其在一些支持游标的数据库中(Oracle、SQL Server、DB2、Sybase),启用PreparedStatements缓存和不启用直接的性能可能相差一个数量级。dbcp配置PreparedStatements缓存主要用到以下两个配置。
- poolPreparedStatements:是否缓存PreparedStatements
- maxOpenPreparedStatements:缓存PreparedStatements的最大个数
普通项目使用dbcp
介绍完dbcp的配置项,我们就可以实际动手,开始使用它了。
- 新建一个普通的java项目:新建项目的方式和刚刚新建dbcp源码项目的方法一样。
- 添加依赖和配置文件:
项目建好之后,首先需要引入必须的依赖,打开项目的pom.xml,将下面的内容copy到
junit
junit
4.10
test
com.xg.testproject
dbcp
0.0.1-SNAPSHOT
com.oracle
ojdbc6
11.2.0.4
在项目的../src/main/resources/路径下,新建一个properties类型的文件dbcp.properties,文件内容如下:
# dbcp配置
# 参考说明:http://commons.apache.org/proper/commons-dbcp/configuration.html
username=scott
password=tiger
url=jdbc:oracle:thin:@192.168.22.63:1521:orcl
driverClassName=oracle.jdbc.OracleDriver
- 创建数据源类
直接在src目录下创建class的话不是很规范,所以我们先来建一个包:com.xg.testproject.dbcp.simple,建好之后,创建一个名为MyDBCPDataSource.java的文件,源码如下:
package com.xg.testproject.dbcp.simple;
import java.io.IOException;
import java.util.Properties;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.pool.impl.GenericObjectPool;
public class MyDBCPDataSource {
// 单例数据源
public static BasicDataSource DBCPDATASOURCE = getDBCPDataSource();
private static BasicDataSource getDBCPDataSource() {
// 加载配置文件
Properties properties = new Properties();
try {
properties.load(MyDBCPDataSource.class.getResourceAsStream("/dbcp.properties"));
} catch (IOException e) {
e.printStackTrace();
}
BasicDataSource dataSource = new BasicDataSource();
// 获取配置
if(properties.isEmpty()){
return null;
}
// 基础配置
dataSource.setUsername(properties.getProperty("username"));
dataSource.setPassword(properties.getProperty("password"));
dataSource.setUrl(properties.getProperty("url"));
dataSource.setDriverClassName(properties.getProperty("driverClassName"));
// 数据库连接配置,主要由数据库驱动提供
dataSource.setDefaultAutoCommit(Boolean.parseBoolean(oracle.jdbc.OracleConnection.CONNECTION_PROPERTY_AUTOCOMMIT_DEFAULT));
// ...
// 连接池的相关配置,这部分的默认配置完全由apache-commons-pool组件提供
dataSource.setInitialSize(GenericObjectPool.DEFAULT_MIN_IDLE);
dataSource.setMaxActive(GenericObjectPool.DEFAULT_MAX_ACTIVE);
dataSource.setMaxIdle(GenericObjectPool.DEFAULT_MAX_ACTIVE);
dataSource.setMinIdle(GenericObjectPool.DEFAULT_MIN_IDLE);
dataSource.setMaxWait(GenericObjectPool.DEFAULT_MAX_WAIT);
dataSource.setTestOnBorrow(GenericObjectPool.DEFAULT_TEST_ON_BORROW);
dataSource.setTestOnReturn(GenericObjectPool.DEFAULT_TEST_ON_RETURN);
dataSource.setTestWhileIdle(GenericObjectPool.DEFAULT_TEST_WHILE_IDLE);
// 其他配置见 http://commons.apache.org/proper/commons-dbcp/configuration.html
return dataSource;
}
}
- 使用连接池:数据源创建好后,就可以测试使用了
新建一个AppMain.java,源码如下:
package com.xg.testproject.dbcp.simple;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.commons.dbcp.BasicDataSource;
public class AppMain {
public static void main(String[] args) {
BasicDataSource dataSource = MyDBCPDataSource.DBCPDATASOURCE;
System.out.println("当前数据库连接池的容量" + dataSource.getNumActive());
Connection conn = null;
Statement sm = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
sm = conn.createStatement();
rs = sm.executeQuery("SELECT ID FROM EASYPASSPORT_USERS ");
while (rs.next()) {
long id = rs.getLong(1);
System.out.println("从数据库中得到一条记录的值" + id);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
sm.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
System.out.println("当前数据库连接池的容量" + dataSource.getNumActive());
conn.close();
System.out.println("当前数据库连接池的容量" + dataSource.getNumActive());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
直接运行AppMain,可以看到程序正常运行,普通Java项目使用dbcp的例子也就到此结束
Spring集成dbcp
Spring是现行的Java应用开发标准,同样它在Java持久层方面的贡献也十分突出,但是“弱水三千只取一瓢”,我们今天仅简单介绍一下Spring如何集成dbcp并完成一些简单操作,更多Spring在Java持久层的内容,以后我会专门开专题去介绍。
- 新建一个java项目
- 添加Spring运行环境
为了配合dbcp的版本,Spring的版本我选择的是3.0.5(我本地仓库里恰好已经下载好了,其他3.x版本的Spring都可以),结合下图,我们可以清晰的看到需要将哪些依赖添加到项目中。
具体的pom.xml的内容如下:
org.springframework
spring-beans
3.0.5.RELEASE
org.springframework
spring-core
3.0.5.RELEASE
org.springframework
spring-context
3.0.5.RELEASE
org.springframework
spring-expression
3.0.5.RELEASE
org.springframework
spring-tx
3.0.5.RELEASE
org.springframework
spring-jdbc
3.0.5.RELEASE
junit
junit
4.10
test
com.xg.testproject
dbcp
0.0.1-SNAPSHOT
com.oracle
ojdbc6
11.2.0.4
cglib
cglib
2.2.2
- Spring配置
在项目的../src/main/resources/路径下,添加dbcp.properties(和之前一样)后,添加Spring的配置文件applicationContext.xml:
- 自定义业务操作Bean
完成了Spring的配置后,就可以定义我们自己的的业务实现Bean了,源码如下。
package com.xg.testproject.dbcp.spring;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;
public class MyDBCPSpringTest {
private JdbcTemplate template;
public void setTemplate(JdbcTemplate template) {
this.template = template;
}
@Transactional
public long getNum(){
return template.queryForLong("SELECT ID FROM EASYPASSPORT_USERS ");
}
@Transactional
public double getSal(){
return template.queryForLong("SELECT SAL FROM EMP WHERE EMPNO = 7369 ");
}
@Transactional
public int setSal(Double sal){
return template.update("UPDATE EMP T SET T.SAL = " + sal + " WHERE T.EMPNO = 7369");
}
}
定义好之后,还要将我们这个bean的配置添加到applicationContext.xml中。
- 测试使用
package com.xg.testproject.dbcp.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AppMain {
public static void main(String[] args) {
// 启动Spring容器,并加载配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 从容器中获取我们定义的业务操作bean
MyDBCPSpringTest test = (MyDBCPSpringTest)ctx.getBean("myDBCPSpringTest");
// 完成业务操作
System.out.println("从数据库中得到一条记录的值" + test.getNum());
System.out.println("更新数据库中的一条记录");
test.setSal(new Double("808.01"));
System.out.println("得到刚刚更新的记录" + test.getSal());
}
}
使用Tomcat内置的dbcp
在Tomcat 7之前,Tomcat都是默认将dbcp作为其数据库连接池的实现的,一些有相互依赖,同时规模也不是很大的应用,使用Tomcat内部集成的dbcp也不失为另一种选择。
- Tomcat配置
要使用Tomcat集成的dbcp,应首先配置JNDI,具体做法如下。
-
给当前Eclipse项目添加Server Runtime Environments
按步骤操作,Windows-》Preferences-》Server-》Runtime Environment,得到如下界面,点击Add按钮。
选择Apache Tomcat v6.0。
选中本地的tomcat 6路径,点击Finish。
上述步骤都完成后,就可以在本地查看Eclipse自动生成的本地Servers文件
- 修改tomcat的contex.xml
选中上图中的servers文件夹中的context.xml,右键-》Open,在
至此,Tomcat的配置就完成了。
新建web项目:
新建web的内容请参考拙作《快速搭建web项目》在程序中使用dbcp
在新建的web中添加一个service类,用来获取获取Tomcat容器中的数据源,并完成jdbc操作。源码如下:
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
public class MainService {
public String userDb() throws Exception {
// 初始化上下文环境
Context context = new InitialContext();
// 从上下文中获取JDBC资源,其中java:/comp/env/jdbc是通用的写法mydb是JNDI的名称
DataSource ds = (DataSource) context.lookup("java:/comp/env/jdbc/mydb");
// 执行sql脚本
Connection conn = null;
Statement sm = null;
ResultSet rs = null;
String rtnStr = "";
try {
conn = ds.getConnection();
sm = conn.createStatement();
rs = sm.executeQuery("SELECT EMAIL FROM EASYPASSPORT_USERS WHERE ID = 999");
while (rs.next()) {
rtnStr = rs.getString(1);
System.out.println("从数据库中得到一条记录的值" + rtnStr);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
sm.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return rtnStr;
}
}
然后新建一个servlet,用来验证service类是否生效。
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.xg.testproject.dbcp.javaee.service.MainService;
public class MainServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(final HttpServletRequest req,
final HttpServletResponse resp) throws ServletException,
IOException {
String rtnStr = "";
try {
rtnStr = new MainService().userDb();
} catch (Exception e1) {
e1.printStackTrace();
}
resp.setCharacterEncoding("UTF-8");
PrintWriter writer;
try {
writer = resp.getWriter();
writer.print(rtnStr);
writer.flush();
writer.close();
} catch (IOException e) {
System.out.println("向客户端返回数据时发生异常!");
e.printStackTrace();
}
}
}
配置web.xml。
MainServlet
com.xg.testproject.dbcp.javaee.servlet.MainServlet
MainServlet
/main.do
DB Connection
jdbc/mydb
javax.sql.DataSource
Container
启动Tomcat,并将当前web项目添加到容器中,打开浏览器,键入url:,可见浏览器中显示下图。
此时查看后台日志,得到:
2017-2-22 18:01:11 org.apache.coyote.http11.Http11Protocol init
信息: Initializing Coyote HTTP/1.1 on http-8088
2017-2-22 18:01:11 org.apache.catalina.startup.Catalina load
信息: Initialization processed in 1799 ms
2017-2-22 18:01:11 org.apache.catalina.core.StandardService start
信息: Starting service Catalina
2017-2-22 18:01:11 org.apache.catalina.core.StandardEngine start
信息: Starting Servlet Engine: Apache Tomcat/6.0.35
2017-2-22 18:01:13 org.apache.coyote.http11.Http11Protocol start
信息: Starting Coyote HTTP/1.1 on http-8088
2017-2-22 18:01:14 org.apache.jk.common.ChannelSocket init
信息: JK: ajp13 listening on /0.0.0.0:8009
2017-2-22 18:01:14 org.apache.jk.server.JkMain start
信息: Jk running ID=0 time=0/164 config=null
2017-2-22 18:01:14 org.apache.catalina.startup.Catalina start
信息: Server startup in 2519 ms
从数据库中得到一条记录的值[email protected]
证明我们已经成功调用了Tomcat内部基础的dbcp。
参考
- 连接池详解
- Oracle JDBC Connection默认常量
- 数据库连接池性能比对