JDBC之 连接池

有这样的一种现象:

用java代码操作数据库,需要数据库连接对象,一个用户至少要用到一个连接。现在假设有成千上百万个用户,就要创建十分巨大数量的连接对象,这会使数据库承受极大的压力,为了解决这种现象,一种技术出现了,这就是数据库连接池。

什么是数据库连接池(原理)

所谓数据库连接池,可以看作 :在用户和数据库之间创建一个”池”,这个池中有若干个连接对象,当用户想要连接数据库,就要先从连接池中获取连接对象,然后操作数据库。一旦连接池中的连接对象被拿光了,下一个想要操作数据库的用户必须等待,等待其他用户释放连接对象,把它放回连接池中,这时候等待的用户才能获取连接对象,从而操作数据库。

数据库连接池的属性

连接对象初始的数量:initSize,一开始就创建若干个,当不够时再添加

连接对象最大数量:maxSize,添加到最大值则不会再添加

下面我们用代码下一个自己的连接池吧~

实现自己的连接池

看下面代码和注释吧~

public class MyPool {
//设置注册属性
private String url = "jdbc:mysql://localhost:3306/vmaxtam";
private String user = "root";
private String password = "root";
private static String driverClass="com.mysql.jdbc.Driver"; //设置连接池属性
private int initSize = 5;
private int maxSize = 8; //用LinkedList对象来保存connection对象
private LinkedList<Connection> connList = new LinkedList<Connection>();
//声明一个临时变量来计算连接对象的数量
private int currentsize = 0; //声明MyPool对象时自动注册驱动
static{
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} //获取连接的方法
private Connection getConnection()
{
Connection conn=null;
try {
conn = DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
} //构造方法,初始化连接池,并往里面添加连接对象
public MyPool() {
for(int i = 0; i < initSize; i++)
{
Connection connection = this.getConnection();
connList.add(connection);
currentsize++;
}
} //获取连接池中的一个连接对象
public Connection getConnFromPool()
{
//当连接池还没空
if(connList.size()>0){
Connection connection = connList.getFirst();
connList.removeFirst();
return connection; }else if(connList.size()==0&&currentsize<8){
//连接池被拿空,且连接数没有达到上限,创建新的连接
currentsize++;
connList.addLast(this.getConnection()); Connection connection = connList.getFirst();
connList.removeFirst();
return connection;
} throw new RuntimeException("连接数达到上限,请等待");
} //把用完的连接放回连接池
public void releaseConnection(Connection connection)
{
connList.addLast(connection);
} }

有了连接池后,我们写一个测试来调用一下它吧~

    @Test
public void test1()
{
//获得连接池
MyPool mypool = new MyPool(); /*从连接池中尝试获取9个连接
for(int i = 0 ; i<9; i++){
Connection conn = mypool.getConnFromPool();
System.out.println(conn.toString());
}*/ //获取第五个连接后,释放一下,然后再获取
for(int i = 0 ; i<9; i++){
Connection conn = mypool.getConnFromPool();
if(i==5){
mypool.releaseConnection(conn);
}
System.out.println(conn.toString());
}
}

上面这样就实现了自己的一个连接池,但是这个连接池依然存在着很多问题,一个较为明显的问题就是:

如果一个用户获取了一个连接对象,然后调用了close()方法把它关闭了,没有放回池中,这样池中的这个对象就回不来了,造成最大连接上限为8个的连接池实际上只有7个连接在工作。

为了解决这个问题,我们需要对close()方法进行改造,是用户调用close()方法时,实际上是把连接放回连接池中,而不是关闭它。

下面就为解决这个问题来分析下~

解决用户调用close()方法关闭连接

方法一:使用静态代理,写一个myConnection()类来继承connection的实现类,然后重写它的close()方法.

方法二:使用动态代理,使用jdbc动态代理类:java.lang.reflect.Proxy类

动态代理类中有这样一个方法可以创建它的实例

public static Object newProxyInstance(ClassLoader loader,

Class<?>[] interfaces,

InvocationHandler h)

参数解析:

ClassLoader:类加载器,只要在同一个JDK中的类即可

Class<?>[]:要代理的接口的集合

InvocationHandler:代理接口的方法处理器

根据需要,我们要给MyPool中的Connection加一个动态代理,所以我们用的前两个参数是:MyPool.Class.GetClassLoader 与 new Class<>{Connection}

最后还剩方法处理器,我们要修改Connection中的close方法,所以我们写出一个这样做的处理器即可,具体实现看下面代码与注释~

//获取连接的方法
private Connection getConnection()
{ try {
//获取一个连接
final Connection conn=DriverManager.getConnection(url, user, password); //把连接交给动态代理类转换为代理的连接对象
Connection myconn = (Connection)Proxy.newProxyInstance(
MyPool.class.getClassLoader(),
new Class[] {Connection.class},
//编写一个方法处理器
new InvocationHandler() { @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object value = null; //当遇到close方法,就会把对象放回连接池中,而不是关闭连接
if(method.getName().equals("close"))
{
MyPool.connList.addLast(conn);
}else
{
//其它方法不变
value = method.invoke(conn, args);
}
return value;
}}
);
return myconn;
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}

以上就是利用动态代理的方式解决close的问题了~~但是,我们自己写的连接池还有很多其他问题:

1)当前多个并发用户同时获取连接时,可能拿到同一个连接对象

2)当前用户连接数超过了最大连接数时,不能直接抛出异常,应该有机制,控制用户等待时间........

所以,这时候已经有人站出来,为我们写好了一些功能相对完善的连接池,这些第三方的连接池得到了广泛的用途,下面我们来介绍一下常见的连接池工具吧~

DBCP连接池

简介:DBCP连接池是开源组织Apache软件基金组织开发的连接池实现。

事实上,tomcat服务器默认就会使用这个连接池道具。

如何使用DBCP连接池呢,下面我来一一演示。

DBCP的使用步骤

步骤一:导包,使用第三方的道具,必须导入相应的jar包。

需要导入两个jar包:commons-dbcp-1.4.jar包

commons-pool-1.5.6.jar包

步骤二:使用代码~看看下面代码的演示吧

public class DBCPTest {
private String url = "jdbc:mysql://localhost:3306/vmaxtam";
private String user = "root";
private String password = "root";
private String classDriver = "com.mysql.jdbc.Driver"; @Test
public void Test1()
{
//创建DBCP连接池对象
BasicDataSource ds = new BasicDataSource(); //设置连接参数来进行连接
ds.setUrl(url);
ds.setUsername(user);
ds.setPassword(password);
ds.setDriverClassName(classDriver); //然后可以设置连接池的一些属性啦~
ds.setInitialSize(5);
ds.setMaxActive(8);
ds.setMaxWait(3000);//设置最大的等待时长,毫秒为单位 //从连接池中获取对象
for(int i = 0 ; i<8;i++)
{
Connection conn = null;
try {
conn = ds.getConnection();
System.out.println(conn.hashCode()); } catch (SQLException e) {
e.printStackTrace();
}
} }
}

为了测试效果,我们可以在循环中设置拿9个连接额,这样在拿第九个连接时就会出现等待,等待到结束都没有连接被释放回连接池,就会出现报错。

也可以把For循环改成下面那样,测试close方法:

//从连接池中获取对象
for(int i = 0 ; i<9;i++)
{
Connection conn = null;
try {
conn = ds.getConnection();
System.out.println(conn.hashCode());
if(i==5)
{
conn.close();
} } catch (SQLException e) {
e.printStackTrace();
}
}

上面的代码还是有点地方可以得到优化,例如可以通过配置文件来配置连接的参数,还有数据库连接池的属性参数。

配置文件:

url=jdbc:mysql://localhost:3306/vmaxtam
username=root
password=root
classDriver=com.mysql.jdbc.Driver
initialSize=5
maxActive=8
maxWait=3000

用对象读取配置文件:

@Test
public void Test2()
{ try {
//创建配置对象
Properties properties = new Properties();
properties.load(DBCPTest.class.getResourceAsStream("/dbcp.properties")); //创建连接池对象,并且用连接池工厂来加载配置对象的信息
BasicDataSource ds = (BasicDataSource)BasicDataSourceFactory.createDataSource(properties); //从连接池中获取对象
for(int i = 0 ; i<8;i++)
{
Connection conn = null;
conn = ds.getConnection();
System.out.println(conn.hashCode());
}
}catch (Exception e2) {
e2.printStackTrace();
}
}

以上就是DBCP连接池的基本用法了~下面我们来学习另一个连接池~

C3P0连接池

简介: C3P0是一个开源组织的产品,开源框架的内部的连接池一般都使用C3P0来实现,例如:Hibernate

C3P0的使用步骤

步骤一:导包,使用第三方的工具必须导入jar包

要导入的包:c3p0-0.9.1.2.jar 包

步骤二:看下面的代码显示怎么使用这个连接池吧~

@Test
public void Test1()
{ try {
//获取连接池对象
ComboPooledDataSource cp = new ComboPooledDataSource(); //设置连接参数
cp.setJdbcUrl(url);
cp.setUser(user);
cp.setPassword(password);
cp.setDriverClass(classDriver); //设置连接池的参数
cp.setInitialPoolSize(5);//初始数量
cp.setMaxPoolSize(8);//最大数量
cp.setCheckoutTimeout(3000);//最大等待时间 for(int i = 0 ; i<8 ; i++)
{
Connection conn = cp.getConnection();
System.out.println(conn.hashCode());
} } catch (Exception e) {
e.printStackTrace();
}
}

可以看出,C3P0的用法和DBCP的用法非常的相似~这里不做累赘。

特别的是C3PO读取参数文件的方式,C3P0除了能像DBCP那样读取配置文件,它还提供了一种特殊的设置参数的方式,就是把参数数据写在一个名叫c3p0-config.xml的XML文件中,在创建C3P0对象时会自动在classpath去寻找该文件来读取~

也就是说:c3p0会到classpath下读取名字为c3p0-config.xml文件

这份XML文件有特殊的要求,下面我们来写一下这份XML文件:

<c3p0-config>
<!-- 默认配置 -->
<default-config>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/vmaxtam</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="driverClass">com.mysql.jdbc.Driver</property> <property name="initialPoolSize">5</property>
<property name="maxPoolSize">8</property>
<property name="checkoutTimeout">3000</property> </default-config> <!-- mysql的连接配置 -->
<named-config name="mysql">
<property name="jdbcUrl">jdbc:mysql://localhost:3306/vmaxtam</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="driverClass">com.mysql.jdbc.Driver</property> <property name="initialPoolSize">5</property>
<property name="maxPoolSize">8</property>
<property name="checkoutTimeout">3000</property>
</named-config> <!-- 使用 oracal就会用这份配置-->
<!-- 也可以写其他数据库的配置 --> </c3p0-config>

写完xml文件,现在我们就读取它吧~

@Test
public void Test2()
{
try {
//获取连接池对象,写上参数就会去找xml文件找这个数据库的配置来读取,当无参时,就会使用默认设置。
ComboPooledDataSource cp = new ComboPooledDataSource("mysql"); for(int i = 0 ; i<9 ; i++)
{
Connection conn = cp.getConnection();
System.out.println(conn.hashCode());
if(i==5)
{
conn.close();
}
} } catch (Exception e) {
e.printStackTrace();
}
}

使用这种读取方法,显得代码十分简便。

JDBC之 连接池的更多相关文章

  1. c3p0、dbcp、tomcat jdbc pool 连接池配置简介及常用数据库的driverClass和驱动包

    [-] DBCP连接池配置 dbcp jar包 c3p0连接池配置 c3p0 jar包 jdbc-pool连接池配置 jdbc-pool jar包 常用数据库的driverClass和jdbcUrl ...

  2. jdbc数据连接池dbcp要导入的jar包

    jdbc数据连接池dbcp要导入的jar包 只用导入commons-dbcp-x.y.z.jarcommons-pool-a.b.jar

  3. 关于JDBC和连接池我学到的(转载保存)

    1.JDBC数据库连接池的必要性 在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤: 在主程序(如servlet.beans)中建立数据库连接. 进行sql操作 断开数据库连接. 这种模 ...

  4. JDBC数据源连接池(4)---自定义数据源连接池

    [续上文<JDBC数据源连接池(3)---Tomcat集成DBCP>] 我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究 ...

  5. JDBC数据源连接池(3)---Tomcat集成DBCP

    此文续<JDBC数据源连接池(2)---C3P0>. Apache Tomcat作为一款JavaWeb服务器,内置了DBCP数据源连接池.在使用中,只要进行相应配置即可. 首先,确保Web ...

  6. JDBC数据源连接池(2)---C3P0

    我们接着<JDBC数据源连接池(1)---DBCP>继续介绍数据源连接池. 首先,在Web项目的WebContent--->WEB-INF--->lib文件夹中添加C3P0的j ...

  7. DBCP,C3P0与Tomcat jdbc pool 连接池的比较

    hibernate开发组推荐使用c3p0; spring开发组推荐使用dbcp(dbcp连接池有weblogic连接池同样的问题,就是强行关闭连接或数据库重启后,无法reconnect,告诉连接被重置 ...

  8. JDBC数据源连接池的配置和使用实例

    个人学习参考所用,勿喷! 使用JDBC建立数据库连接的两种方式: 1.在代码中使用DriverManager获得数据库连接.这种方式效率低,并且其性能.可靠性和稳定性随着用户访问量得增加逐渐下降. 2 ...

  9. mysql,jdbc、连接池

    show processlist; select * from information_schema.processlist; Command: The type of command the thr ...

随机推荐

  1. shell jq

    Mark 下,周末来补充 参考资料: https://stedolan.github.io/jq/tutorial/

  2. cin,cout,printf,scanf效率对比

    From:http://www.cnblogs.com/killerlegend/p/3918452.html Author:KillerLegend Date:2014.8.17 杭电OJ之3233 ...

  3. Asp.net操作Word文档,原来这么简单啊!

    引用Word对象库文件  具体做法是打开菜单栏中的项目>添加引用>浏览,在打开的“选择组件”对话框中找到MSWORD.OLB后按确定即可引入此对象库文件,vs.net将会自动将库文件转化为 ...

  4. Maven 环境的配置

    Maven 环境的配置 现在Java新架构的不断出现,例如Struts,Spring,Hibernate等,项目的配置文件的增多,给开发人员带来很大麻烦.在实际的开发当中,Myeclipse中的pro ...

  5. webp实践的javascript检测方案

    function hasWebp () { // 查看Cookie,如果没有则进行以下逻辑 var img = new Image(); img.onload = handleSupport; img ...

  6. 大话C#中能使用foreach的集合的实现

    大家都知道foreach的语法:foreach(var item in items){ Console.Writeln(item);} 通过这样一个简单的语句,就能实现遍历集合items中的所有元素. ...

  7. 第11月第18天 RACSequence

    1. RACSequence的内部存储结构就像一个单链表,有两个指针head和tail,head指针指向了当前链表的第一个元素,tail指向head指针下一个元素:根据RACSequence是否还有内 ...

  8. Number of Airplanes in the Sky

    Given an interval list which are flying and landing time of the flight. How many airplanes are on th ...

  9. linux c中select使用方法

    1.select函数作为定时器使用    it_value.tv_sec = 0;    it_value.tv_usec = 100000:    select(1,NULL,NULL,NULL,& ...

  10. 过滤掉文本中的javascript标签代码

    2014年1月21日 11:51:19 php代码: $content = preg_replace('#<\s*[script].*>#', '', $a);//有些攻击可以在scrip ...