JDBC之 连接池
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&¤tsize<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,
参数解析:
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之 连接池的更多相关文章
- c3p0、dbcp、tomcat jdbc pool 连接池配置简介及常用数据库的driverClass和驱动包
[-] DBCP连接池配置 dbcp jar包 c3p0连接池配置 c3p0 jar包 jdbc-pool连接池配置 jdbc-pool jar包 常用数据库的driverClass和jdbcUrl ...
- jdbc数据连接池dbcp要导入的jar包
jdbc数据连接池dbcp要导入的jar包 只用导入commons-dbcp-x.y.z.jarcommons-pool-a.b.jar
- 关于JDBC和连接池我学到的(转载保存)
1.JDBC数据库连接池的必要性 在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤: 在主程序(如servlet.beans)中建立数据库连接. 进行sql操作 断开数据库连接. 这种模 ...
- JDBC数据源连接池(4)---自定义数据源连接池
[续上文<JDBC数据源连接池(3)---Tomcat集成DBCP>] 我们已经 了解了DBCP,C3P0,以及Tomcat内置的数据源连接池,那么,这些数据源连接池是如何实现的呢?为了究 ...
- JDBC数据源连接池(3)---Tomcat集成DBCP
此文续<JDBC数据源连接池(2)---C3P0>. Apache Tomcat作为一款JavaWeb服务器,内置了DBCP数据源连接池.在使用中,只要进行相应配置即可. 首先,确保Web ...
- JDBC数据源连接池(2)---C3P0
我们接着<JDBC数据源连接池(1)---DBCP>继续介绍数据源连接池. 首先,在Web项目的WebContent--->WEB-INF--->lib文件夹中添加C3P0的j ...
- DBCP,C3P0与Tomcat jdbc pool 连接池的比较
hibernate开发组推荐使用c3p0; spring开发组推荐使用dbcp(dbcp连接池有weblogic连接池同样的问题,就是强行关闭连接或数据库重启后,无法reconnect,告诉连接被重置 ...
- JDBC数据源连接池的配置和使用实例
个人学习参考所用,勿喷! 使用JDBC建立数据库连接的两种方式: 1.在代码中使用DriverManager获得数据库连接.这种方式效率低,并且其性能.可靠性和稳定性随着用户访问量得增加逐渐下降. 2 ...
- mysql,jdbc、连接池
show processlist; select * from information_schema.processlist; Command: The type of command the thr ...
随机推荐
- jQuery下ajax事件的简单分析
昨天写了一篇关于监视页面动态生成元素问题的文章,引起了一些小小的争议,不过我从中学到了很多.文章在这,<jQuery下实现等待指定元素加载完毕>当然 动态生成的节点元素 分很多种情况,这里 ...
- 也谈创业企业CEO该拿多少工资
网上看到一篇文章,关于创业公司CEO要给自己开多少工资. 当然,原文中的一些创业公司例子都过于高大上,譬如一创业就拿到A轮B轮的融资.对于这样的案例我想说的是:“太脱离人民大众创业者”. 纵观我国的I ...
- es6笔记(5)Map数据结构
概要 字典是用来存储不重复key的Hash结构.不同于集合(Set)的一点,字典使用的是[key,value]的形式来存储数据. JavaScript的对象(Object:{})只能用字符串当做key ...
- shell 判断路径
判断路径 ];then echo "找到了123" if [ -d /root/Desktop/text ] then echo "找到了text" else ...
- 【API】网络编程模型、多线程
1.网络通信编程 1)网络通信模型基础知识 TCP Server: WSAStartup() socket() bind() linsten() accept() send/recv() closes ...
- linux根据端口查找进程【原创】
如转载请注明地址 1.利用lsof -i:端口号 lsof -i:端口号 [root@01 ~]# lsof -i:8097COMMAND PID USER FD TYPE DEVICE SIZE/O ...
- nginx 反向代理Apache
2014年1月13日 18:15:25 同一个域名下,不同uri走不同的应用(不同的web根目录) 实现方式: Apache分别监听两个端口,对应不同的应用 nginx监听80端口,利用locatio ...
- 测试开发之Django——No2.Django的安装以及项目创建
开发平台:Mac Python版本:3.7 Django版本:2.0.5 一.Django的安装 1.pip安装 输入命令pip install Django==2.0.5 说明:不指定版本,则安装的 ...
- Flask 目录
flask入门 flask 源码剖析 Flask session Flask form
- Java 组合
组合: 在新类中产生现有类的对象,由于新的类是由现有类的对象所组成,所以这种方法称为组合 组合和继承都允许在新的类中放置对象,组合时显示的这样做,而继承则是隐式的这样做 组合技术通常用于想在新类中使用 ...