这段时间,正在学Hibernate,在学习的过程中,回去复习了一下JDBC,收获颇大。其实,很早以前(大二),就接触JDBC了,只是当时觉得很简单,大概是那时对于软件开发的认识比较肤浅吧!总认为,能够通过别人提供的高级工具,例如:Struts、Hibernate、Spring开发出一个东西出来,就是很厉害了。所以,在那时候,感觉能够熟练使用JDBC的一些API,就算是精通JDBC了。
现在才觉得,自己当时的想法是幼稚的,原谅我的年轻,我追求进步。
今天,看了一下DBCP(Apache提供的通过连接池获取Connection的一个项目),然后用MyEclipse的debug功能,看了下使用DBCP的API获取Connection的执行能够过程。接着,花了几个小时的时间,自己写了一个数据源,与大家分享,倘若在设计上有什么不合理的地方,还请大家给鄙人指出。鄙人的进步需要你们。
先谈下“为什么要使用连接池(数据源)”?
我们知道,在我们使用JDBC操作数据库的时候,过程一般都是如此的:注册数据库驱动-->取得连接(Connection)-->取得Statement或者PreparedStatement-->(可能需要)取得ResultSet,而我们知道,在这一操作中,最耗时的操作是“取得Connection”这个过程。
所以,我们在想?既然Connection创建的成本这么高。为什么不去复用它们呢!用完了就关闭,太浪费了。Connection就像一座桥,而PreparedStatement或Statement对象就像一辆车,ResultSet就像一匹货物。货物运完了,就把桥拆了,多浪费呀!所以,我们将这座桥(Connection对象)保留起来。用什么方式保留呢?一种比较容易实现的方式,当Connection对象不要用时,将Connection对象放在一个集合里,而不是关闭了。可以是ArrayList,可以是LinkedList(下面会强调两者的区别)。
好,下面就是具体的实现过程。
<?xml version="1.0" encoding="UTF-8"?>
<datasource-configuraction>
<!--连接设置 -->
<driver-name>com.mysql.jdbc.Driver</driver-name>
<url>jdbc:mysql://localhost:3306/jdbc</url>
<username>root</username>
<password>root</password>
<!-- 初始化连接数 -->
<inital-size>10</inital-size>
<!-- 最大连接数 -->
<max-active>20</max-active>
<!-- 每个连接最大使用次数 -->
<max-used>5</max-used>
</datasource-configuraction>
该配置文件,配置了以下信息:
- 数据库连接信息:例如驱动,url,username,password
- 初始化连接数:当数据源(下面的MyDataSource)实例化时,创建的Connection的个数
-
最大连接数:我们知道,WEB环境,一般都是多线程环境的。由于Connection是非线程安全的,所以要确保每个线程拿到的都是不同的Connection。那假如有100个线程同时访问数据库,很显然,连接池里的Connection是不够用的,所以必须额外创建。而数据库所能承受的Connection个数是有限的,我们为了保证数据库的性能,不能让Connection无休止地创建下去。
- 接着,有了配置文件,就要去读取了,我使用的是dom4j读取
package com.langgou.jdbc.datasource.util;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
* 读取数据源配置信息
*
* @author <a href="fumingfu1990@gmail.com">胡铭福</a>
*
*/
public final class DataSourceCfgReader {
private static Document doc;
private static DataSourceInfo dateSourceInfo;
private DataSourceCfgReader() {
}
static {
try {
doc = new SAXReader().read(DataSourceCfgReader.class
.getClassLoader().getResource("dataSourceCfg.xml"));
setDataSourceInfo(doc);
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//设置数据源配置信息
private static void setDataSourceInfo(Document doc) {
Element driverNameElt = (Element) doc
.selectObject("/datasource-configuraction/driver-name");
Element urlElt = (Element) doc
.selectObject("/datasource-configuraction/url");
Element userNameElt = (Element) doc
.selectObject("/datasource-configuraction/username");
Element passwordElt = (Element) doc
.selectObject("/datasource-configuraction/password");
Element initalSizeElt = (Element) doc
.selectObject("/datasource-configuraction/inital-size");
Element maxActiveElt = (Element) doc
.selectObject("/datasource-configuraction/max-active");
Element maxUsedElt = (Element) doc
.selectObject("/datasource-configuraction/max-used");
dateSourceInfo = new DataSourceInfo();
dateSourceInfo.setDriverName(driverNameElt.getText());
dateSourceInfo.setUrl(urlElt.getText());
dateSourceInfo.setUserName(userNameElt.getText());
dateSourceInfo.setPassword(passwordElt.getText());
dateSourceInfo.setInitalSize(Integer.parseInt(initalSizeElt.getText()));
dateSourceInfo.setMaxActive(Integer.parseInt(maxActiveElt.getText()));
dateSourceInfo.setMaxUsed(Integer.parseInt(maxUsedElt.getText()));
}
//取得数据源配置信息
public static DataSourceInfo getDataSourceInfo() {
return dateSourceInfo;
}
}
- 读取出来的信息,得用一个JavaBean来存放,这样会比较合理一点。因为类和类之间,一般都是通过JavaBean作数据通信的。
package com.langgou.jdbc.datasource.util;
/**
* 保存数据源配置信息
* @author <a href="fumingfu1990@gmail.com">胡铭福</a>
*
*/
public class DataSourceInfo {
/**
* 连接设置
*/
private String driverName;
private String url;
private String userName;
private String password;
/**
* 初始化的Connection个数
*/
private int initalSize;
/**
* 已创建Connection的最大个数
*/
private int maxActive;
/**
* Connection的最大使用次数
*/
private int maxUsed;
public String getDriverName() {
return driverName;
}
public void setDriverName(String driverName) {
this.driverName = driverName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getInitalSize() {
return initalSize;
}
public void setInitalSize(int initalSize) {
this.initalSize = initalSize;
}
public int getMaxActive() {
return maxActive;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public int getMaxUsed() {
return maxUsed;
}
public void setMaxUsed(int maxUsed) {
this.maxUsed = maxUsed;
}
}
- 现在,配置信息读取出来了。接下来,就是重头戏了,也就是数据源的实现.(程序的注释已经写的很详细了,这里就不再详细解释了)
package com.langgou.jdbc.datasource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import com.langgou.jdbc.datasource.util.DataSourceCfgReader;
import com.langgou.jdbc.datasource.util.DataSourceInfo;
public class MyDataSource {
/**
* 数据源配置文件中的所有信息,当MyDateSource一被加载入虚拟机,就读取所有配置文件信息
*/
protected static DataSourceInfo dataSourceInfo = DataSourceCfgReader
.getDataSourceInfo();
private final static String PROP_DRIVERCLASSNAME = dataSourceInfo
.getDriverName();
private final static String PROP_URL = dataSourceInfo.getUrl();
private final static String PROP_USERNAME = dataSourceInfo.getUserName();
private final static String PROP_PASSWORD = dataSourceInfo.getPassword();
/**
* 初始化的Connection个数
*/
private final static int PROP_INITIALSIZE = dataSourceInfo.getInitalSize();
/**
* 最大的Connection个数
*/
private final static int PROP_MAXACTIVE = dataSourceInfo.getMaxActive();
/**
* 每个Connection的最大使用次数
*/
private final static int PROP_MAXUSED = dataSourceInfo.getMaxUsed();
/**
* 记录已创建的Connection个数
*/
protected int currentCount = 0;
/**
* 连接池,将connectionPool设成protected的,只允许包内访问。外部只能通过调用getConnection()方法取得Connection
*/
protected LinkedList<Connection> connectionPool = new LinkedList<Connection>();
/**
* 当MyDataSource被加载到虚拟机,就注册数据库驱动
*/
static {
try {
Class.forName(PROP_DRIVERCLASSNAME);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 当MyDataSource被实例化,就创建一批Connection,放入连接池里 构造方法
*/
public MyDataSource() {
try {
for (int i = 0; i < PROP_INITIALSIZE; i++) {
this.connectionPool.addLast(this.createConnection());
this.currentCount++;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
*
* @author <a href="fumingfu1990@gmail.com">胡铭福</a> 功能:从连接池里取得Connection
* @return
*/
public Connection getConnection() throws SQLException {
// Connection属于非线程安全的,要确保每个线程拿到的都是不相同的Connection
synchronized (connectionPool) {
// 判断连接池里是否还有Connection
if (this.connectionPool.size() > 0) {
// 如果连接池里还有Connection,则直接从连接池中取出Connection
return connectionPool.removeFirst();
} else if (this.currentCount < PROP_MAXACTIVE) {
// 判断当前的Connection个数是否超过Connection个数的上限
// 因为数据库所能承受的Connection个数是有限的,不能让其无限创建下去
this.currentCount++;
try {
// 创建一个新的Connection
return this.createConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
throw new SQLException("已经没有连接啦!");
}
}
/**
*
* @author <a href="fumingfu1990@gmail.com">胡铭福</a> 功能:释放Connection
* @param conn
*/
public void free(Connection conn) {
// 为了达到Connection的复用,不能将Connection关闭,要放回连接池中
this.connectionPool.add(conn);
}
/**
*
* @author <a href="fumingfu1990@gmail.com">胡铭福</a> 功能:创建Connection
* @return
* @throws SQLException
* 问题:创建Connection的时候,采用ThreadLocal封装,是否有效?
*/
private Connection createConnection() throws SQLException {
// //采用ThreadLocal封装Connection,确保每个线程只对应一个Connection
// ThreadLocal<Connection> connectionHolder = new
// ThreadLocal<Connection>();
Connection conn = null;
try {
// 取得与当前线程绑定的Connection
// conn = connectionHolder.get();
// 如果没有与当前线程绑定的Connection
// if(conn==null){
// 创建Connection
conn = DriverManager.getConnection(PROP_URL, PROP_USERNAME,PROP_PASSWORD);
// 将创建的Connection与当前线程绑定
// connectionHolder.set(conn);
// }
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
// /**
// *
// * @author <a href="fumingfu1990@gmail.com">胡铭福</a>
// * 功能:测试程序
// * @param args
// */
// public static void main(String[] args) {
// MyDataSource myDataSource = new MyDataSource();
//
// for(int i=0;i<5;i++){
// try {
// System.out.println(myDataSource.getConnection());
// } catch (SQLException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
// }
}
存在的问题:由于当用户显式的调用Connection对象的close()方法时,默认会将Connection对象关闭,而不是返回连接池中,从而不能达到Connection的复用。所以要采取一种机制,改写掉Connection的close()方法默认的行为。
解决方案:
一般采用采用JDK动态代理,拦截所有Connection对象的close()方法,由代理类实现。将默认的关闭Connection对象的行为改为将Connection对象返回连接池,从而达到Connection对象的复用。而除了close()方法,外的其余方法,都由真实对象(realConnection对象)实现.
------------------------------------------------------------------这是一条分割线----------------------------------------------------------------------------------------------
由于时间关系,采用代理类包装Connection,这篇文章就不再介绍了。下篇文章,会详细介绍,并实现类似于DBCP包装Connection的方法。
由于是“处女贴”,写的不好的地方,请谅解。
分享到:
相关推荐
DBCP(Database Connection Pool)是Apache的一个开源项目Commons DBCP,它提供了一个数据库连接池的实现,用于管理数据库连接。数据库连接池是应用程序管理数据库连接的一种方式,通过复用已建立的数据库连接,避免...
Java 数据源连接池是Java应用程序在处理数据库连接时的一个重要技术。它允许程序在需要时获取数据库连接,使用完毕后释放回池,而不是直接关闭,从而提高了应用的性能和资源利用效率。本压缩包文件主要涵盖了两个...
这里提到的几个jar包都是用于实现数据库连接池功能的库,分别是Apache的DBCP(Jakarta Commons DBCP)和C3P0。 1. **Apache Commons DBCP**: `commons-dbcp.jar` 是Apache Commons项目的一部分,提供了数据库连接...
在JSP和MVC应用中,我们通常使用像Apache DBCP、C3P0或HikariCP这样的连接池实现。这些库提供了初始化连接池、设置最大最小连接数、超时策略等功能。配置连接池通常涉及以下步骤: 1. 添加对应连接池的依赖到项目中...
这里,`connection.pool_size`用于设置连接池的大小,而`hibernate.connection.datasource`指定使用DBCP数据源。 3. **Apache DBCP2连接池配置** DBCP2是DBCP的升级版,解决了DBCP的一些已知问题并提供了更多功能...
1. **连接池管理**:类似于DBCP,c3p0也负责创建、分配、回收和管理数据库连接。 2. **自动测试**:c3p0会在连接返回到连接池前进行自动测试,确保连接的可用性。 3. **性能优化**:c3p0引入了一些特有的性能优化...
而"c3p0.gz"则可能是一个c3p0的压缩文件,c3p0是另一个常见的JDBC连接池实现,与Apache Commons DBCP类似,用于管理数据库连接。 总结一下,这个压缩包可能包含的是Apache Commons DBCP的组件和依赖,用于搭建和...
DBCP提供了一个基本的连接池实现,但如描述中提到的,它存在一个问题:在强制关闭连接或数据库重启后,无法自动重新连接。为了解决这个问题,可以配置验证查询(validationQuery)和检查连接是否可用(testOnBorrow...
配置方法与局部数据源类似,但其影响范围更广,适用于服务器内的所有应用。 配置完成后,应用可以通过JNDI(Java Naming and Directory Interface)查找并使用数据源,例如在Spring框架中,可以使用`@Resource`注解...
这通常可以通过使用`org.springframework.jdbc.datasource.DriverManagerDataSource`或更高级的如`HikariCP`、`Apache Commons DBCP`等连接池实现。例如,对于主数据库,我们可以这样配置: ```java @Bean(name =...
【标题】:Tomcat数据源配置 ...总结,Tomcat数据源配置是一个涉及多方面因素的过程,包括版本差异、连接池实现选择以及最佳实践的应用。理解并掌握这些知识点,将有助于我们构建稳定、高效的Web应用环境。
在配置连接池时,可以在这个文件中定义数据源(DataSource)的配置,例如使用Apache DBCP或C3P0等连接池实现。这通常涉及 `<resource-ref>` 和 `<context-param>` 标签,指定连接池的类型、数据库URL、用户名、密码...
`commons-dbcp-1.4.jar`是Apache Commons Database Connection Pooling(DBCP)项目的一部分,它是一个基于Apache Commons Pool对象池实现的数据库连接池。数据库连接池在初始化时会创建一定数量的数据库连接,并在...
Apache Commons DBCP(Database Connection Pool)是一个开源的数据库连接池组件,它提供了数据源对象,方便管理和复用数据库连接,从而提高应用性能。 要解决这个问题,你需要确保你的Java项目正确地包含了Apache ...
在Java开发中,常见的MySQL连接池实现有C3P0、DBCP、HikariCP、Druid等。以HikariCP为例,这是一个高性能的连接池,它的设计目标是提供低延迟和高吞吐量。下面将详细介绍HikariCP的配置和使用。 首先,我们需要在...
DBCP是Apache Commons库中的一个数据源实现,它是Spring框架早期推荐的连接池。DBCP提供了一个基础的数据源实现,能够有效地管理和复用数据库连接。然而,DBCP存在一个问题,即在数据库重启或强制关闭连接后,它...
综上所述,"java-jdbc-连接池大礼包"涵盖了JDBC的基本使用、连接池的概念及其实现,以及针对SQL Server 2000/2005、Oracle和MySQL等常见数据库的交互。掌握这些知识,开发者能够有效地管理和优化数据库连接,提升...
在Spring框架中,我们可以通过DataSource接口来配置数据源,通常使用Apache Commons DBCP或HikariCP等连接池实现。每个数据源都可以被定义为一个Bean,并通过@Configuration和@Bean注解进行配置。例如: ```java @...