论数据库连接池的必要性及c3p0的详解

1.JDBC数据库连接池的必要性

在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤:  

在主程序(如servlet、beans)中建立数据库连接。

进行sql操作

断开数据库连接。

这种模式开发,存在的问题:

普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码(得花费0.05s~1s的时间)。需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接。这样的方式将会消耗大量的资源和时间。数据库的连接资源并没有得到很好的重复利用.若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。

对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。

这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃.

2.数据库连接池(connection pool)

为解决传统开发中的数据库连接问题,可以采用数据库连接池技术。

数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。

数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。

3.数据库连接池技术的优点

(1)资源重用:由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。

(2)更快的系统反应速度:数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间

(3)新的资源分配手段对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置实现某一应用最大可用数据库连接数的限制避免某一应用独占所有的数据库资源.

(4)统一的连接管理,避免数据库连接泄露在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露。

4.c3p0数据库连接池

据说c3p0数据库连接池是最优秀的,下面我就来介绍如何使用c3p0连接池。

(1)下载jar包

(2)配置文件

作为一个数据库连接池自然有很多参数要设置,当然就算你不设置也有默认的,不过那不一定能满足你的要求。这里的配置文件没有什么特别的要求,可以是xml也可以是properties甚至与txt都行,当然如果你愿意也可以写死在程序中。

下面是我的配置文件,我把它放在了src目录下,名字叫“c3p0.properties”

#jdbc基本信息
driverClass=oracle.jdbc.driver.OracleDriver
jdbcUrl=jdbc:oracle:thin:@192.168.61.250:1521:SXPPMS
user=qzp
password=111111

#c3p0连接池信息
c3p0.minPoolSize=3
c3p0.maxPoolSize=25

#当连接池中的连接耗尽的时候c3p0一次同时获取的连接数
c3p0.acquireIncrement=3
#定义在从数据库获取新连接失败后重复尝试的次数
c3p0.acquireRetryAttempts=60
#两次连接中间隔时间,单位毫秒
c3p0.acquireRetryDelay=1000
#连接关闭时默认将所有未提交的操作回滚
c3p0.autoCommitOnClose=false
#当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒
c3p0.checkoutTimeout=3000
#每120秒检查所有连接池中的空闲连接。Default: 0
c3p0.idleConnectionTestPeriod=120
#最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0
c3p0.maxIdleTime=600
#如果设为true那么在取得连接的同时将校验连接的有效性。Default: false
c3p0.testConnectionOnCheckin=true
#c3p0将建一张名为c3p0TestTable的空表,并使用其自带的查询语句进行测试。
jdbc.automaticTestTable = c3p0TestTable

然后是C3P0ConnentionProvider,它是一个c3p0连接池管理类

package com.qzp.util;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.DataSources;

/**
 * c3p0连接池管理类
 * @author ICE
 *
 */
public class C3P0ConnentionProvider {

	private static final String JDBC_DRIVER = "driverClass";
	private static final String JDBC_URL = "jdbcUrl";
	
	private static DataSource ds;
	/**
	 * 初始化连接池代码块
	 */
	static{
		initDBSource();
	}
	
	/**
	 * 初始化c3p0连接池
	 */
	private static final void initDBSource(){
		Properties c3p0Pro = new Properties();
		try {
			//加载配置文件
			c3p0Pro.load(C3P0ConnentionProvider.class.getResourceAsStream("/c3p0.properties"));
		} catch (Exception e) {
			e.printStackTrace();
		} 
		
		String drverClass = c3p0Pro.getProperty(JDBC_DRIVER);
		if(drverClass != null){
			try {
				//加载驱动类
				Class.forName(drverClass);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
			
		}
		
		Properties jdbcpropes = new Properties();
		Properties c3propes = new Properties();
		for(Object key:c3p0Pro.keySet()){
			String skey = (String)key;
			if(skey.startsWith("c3p0.")){
				c3propes.put(skey, c3p0Pro.getProperty(skey));
			}else{
				jdbcpropes.put(skey, c3p0Pro.getProperty(skey));
			}
		}
		
		try {
			//建立连接池
			DataSource unPooled = DataSources.unpooledDataSource(c3p0Pro.getProperty(JDBC_URL),jdbcpropes);
			ds = DataSources.pooledDataSource(unPooled,c3propes);
			
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 获取数据库连接对象
	 * @return 数据连接对象
	 * @throws SQLException 
	 */
	public static synchronized Connection getConnection() throws SQLException{
		final Connection conn = ds.getConnection();
		conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
		return conn;
	}
}

SQLHelper.java文件内容如下:用于数据库的查询及增删操作。

package com.qzp.util;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

public class SQLHelper {

	private Connection conn = null;
    private PreparedStatement ps = null;
    private ResultSet rs = null;
 
    /**
     * 数据查询
     * @param sql语句
     * @return 返回结果集List<Object>
     */
    public List<Object> query(String sql) {
        if(sql.equals("") || sql == null){
            return null;
        }
        List<Object> list = new ArrayList<Object>();
        try {
            conn = C3P0ConnentionProvider.getConnection();
            ps = conn.prepareStatement(sql);
            rs = ps.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            // 可以得到有多少列
            int columnNum = rsmd.getColumnCount();
            // 将数据封装到list中
            while (rs.next()) {
                Object[] objects = new Object[columnNum];
                for (int i = 0; i < objects.length; i++) {
                    objects[i] = rs.getObject(i + 1);
                }
                list.add(objects);
            }
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        return list;
    }
 
    /**
     * 插入、修改数据操作
     * @param sql语句
     * @return boolean 成功返回true,失败返回false
     */
    public boolean update(String sql) {
        boolean b = false;
        if(sql.equals("") || sql == null){
            return b;
        }
        try {
            conn = C3P0ConnentionProvider.getConnection();
            ps = conn.prepareStatement(sql);
            int i = ps.executeUpdate();
            if (i == 1) {
                b = true;
            }
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        return b;
    }
 
}

最后是测试程序

由于此处调用了user表,我们对表中的数据进行了封装,后台的数据库表也是此类结构,此处省略。

    private int no;
    private String id;
    private String name;
    private String password;
    private int departNo;

    方法省略...

test程序

package com.qzp.util;

import java.util.ArrayList;
import java.util.List;

import com.qzp.model.User;

public class Test {

    private SQLHelper sqlHelper = new SQLHelper();
    
    /**
     * 测试query
     */
    @SuppressWarnings("rawtypes")
    public List<User> testQuery(){
        String sql = "select * from t_user ";
        //调用sqlHelper()返回一个结果集
        List list = sqlHelper.query(sql);
        List<User> userList = new ArrayList<User>();
        //对查询结果进行封装
        for (int i = 0; i < list.size(); i++) {
            Object object[] = (Object[]) list.get(i);
            User user=new User();
            user.setNo(Integer.parseInt(object[0].toString()));
            user.setId(object[1].toString());
            user.setName(object[2].toString());
            user.setPassword(object[3].toString());
            user.setDepartNo(Integer.parseInt(object[4].toString()));
            userList.add(user);
        }
        return userList;
    }
     
    /**
     * 测试insert、update、delete
     */
    public void testInsertOrUpdate(){
        String sql = "delete from user where userName ='qqqq'";
        boolean b = sqlHelper.update(sql);
        if(b){//b为true则操作成功
            System.out.println("操作成功");
        }else{//b为false则操作失败
            System.out.println("操作失败");
        }
    }
    
    public static void main(String[] args) {
	Test test=new Test();
	System.out.println(test.testQuery().get(1).getPassword());
		
	}
}


执行会打印输出结果:

111111(这个就是数据库中第2个元素所对应的密码的值)

PS:学习小结

1.连接池的好处是不必每次连接都新建一个连接,可以直接取池中取,节约内存资源;

2.使用配置文件,代替原来的jdbc硬编码的形式,复用性更强,而且可以在其中配置连接池的相关信息

3.获取当前路径下的文件,可以使用

            Properties c3p0Pro = new Properties();
            //加载配置文件,注意此处是以流的形式返回,C3P0ConnentionProvider是类名
            c3p0Pro.load(C3P0ConnentionProvider.class.getResourceAsStream("/c3p0.properties"));

4.刚开始的时候,不是很深刻理解arraylist的操作,注意直接打印输出arraylist的值,它只会返回他所在的包及路径,如果要取到其中的值,必须遍历

如:

System.out.println(test.testQuery().get(1).getPassword());

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值