JDBC连接池

什么是数据库连接池

在 JDBC 编程中,每次创建和断开 Connection 对象都会消耗一定的时间和 IO 资源,为了避 免频繁的创建数据库连接,工程师就提出了数据库连接池技术,数据库连接池主要负责分配、 管理、释放数据库连接,它允许重复使用现有的数据库连接,而不是重新创建。

数据库连接池的概念

数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,当应用程序访问 数据库时并不是直接创建 Connection,而是向连接池“申请”一个 Connection。

约定

实现连接池的方式不固定,所以 Java 为了扩展性,提供了 javax.sql.DataSource 接口,开发者 只需要实现接口里面的约定即可,这样也方便我们切换不同的连接池。

自定义数据库连接池

如需要自定义数据库连接池,需要完成以下步骤

  1. 创建类,实现 DataSource 接口
  2. 使用集合来存储创建的连接
  3. 使用静态代码块初始化连接池
  4. 一个连接只能在于一个线程中使用,获取连接后需要进行移除,以免多次获取到相同的连接使用完后需要进行归还
代码示例
public class Demo {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Datepool dp = new Datepool();
        System.out.println("连接池的连接数量:"+dp.getPoolSize());
        Connection con;
        try {
            con = dp.getConnection();
            System.out.println("使用一个连接后连接池的连接数量:"+dp.getPoolSize());
//          dp.release(con);
            con.close();
            System.out.println("使用一个连接后又释放了连接池的连接数量:"+dp.getPoolSize());
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}
//自定义池
public class Datepool implements DataSource{
    static LinkedList<Connection> pool = new LinkedList<>();

    static{
        init();
    }

    public static void init(){
        for (int i = 0; i < 2; i++) {
            try {
                Class.forName("com.mysql.jdbc.Driver");
                Connection con = DriverManager.getConnection("jdbc:mysql:///stdb", "root", "root");
                pool.add(con);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    public int getPoolSize(){
        return pool.size();

    }

    public void release(Connection con){
        if(con !=null){
            pool.addFirst(con);
        }
    }

    @Override
    public Connection getConnection() throws SQLException {
        // TODO Auto-generated method stub
        if(pool.isEmpty()){
            init();
        }
        Connection con = pool.removeFirst();
        MyConnection mycon = new MyConnection(pool, con); 
        return mycon;
    }
    //省略一堆重写的方法


}
//增强close方法
public class MyConnection implements Connection{

    private LinkedList<Connection> pool = new LinkedList<>();
    private Connection con;
    public MyConnection(LinkedList<Connection> pool, Connection con) {
        super();
        this.pool = pool;
        this.con = con;
    }
    @Override
    public void close() throws SQLException {
        // TODO Auto-generated method stub
        this.pool.addFirst(con);
    }
    //省略重写的方法
}

DBCP 连接池

什么是 DBCP

DBCP 是数据库连接池(DataBaseConnectionPool)的简称,Apache 提供的一款开源免费的 数据库连接池

使用 DBCP 需要导入的包

  • commons-dbcp.jar:是 DBCP 数据源包,包含所有操作数据库连接信息和数据库连接池 初始化信息的方法。
  • commons-pool.jar 包:DBCP 数据库连接池实现包的依赖包,为 commons-dbcp.jar 包中 的方法提供支持。
  • common-logging.jar:apache 提供的一个通用的日志接口。用户可以自由选择第三方的 日志组件作为具体实现。
  • commons-dbcp.jar 包中主要包含两个核心类,分别是 BasicDataSource、 BasicDataSourceFactory

- BasicDataSource 是 DataSource 接口的实现类,主要包含设置数据源对象的方法

setDriverClassName(StringdriverClassName) 设置连接数据库的驱动名称
setUrl(Stringurl) 设置连接数据库的路径
setUsername(Stringusername) 设置数据库的登陆账号
setPassword(Stringpassword) 设置数据库的登陆密码
setInitialSize(intinitialSize) 设置数据库连接池初始化大小的连接数目
setMaxActive(intmaxIdle) 设置数据库连接池最大活跃的连接数目
setMinIdle(intminIdle) 设置数据库连接池最小闲置的连接数目
getConnection() 从连接池中获取一个数据库连接

  • BasicDataSourceFactoryshi 是创建 BasicDataSource 对象的工厂类,它包含一个返回值为 BasicDataSource 对象的方法 createDataSource(),该方法通过读取配置文件的信息生成数 据源对象并返回给调用者。
示例代码
public class Demo {

//  public static void main(String[] args) throws SQLException {
//      // TODO Auto-generated method stub
//
//      BasicDataSource bds = new BasicDataSource();
//      bds.setDriverClassName("com.mysql.jdbc.Driver");
//      bds.setUrl("jdbc:mysql:///stdb");
//      bds.setUsername("root");
//      bds.setPassword("root");
//      bds.setInitialSize(10);
//      bds.setMaxIdle(5);
//      Connection con = bds.getConnection();
//      PreparedStatement ps = con.prepareStatement("select * from userinfo");
//      ResultSet rs = ps.executeQuery();
//      while(rs.next()){
//          System.out.println(rs.getString(2));
//      }
//      bds.close();
//  }
    public static void main(String[] args) throws Exception{
        Properties p = new Properties();
        FileReader fr = new FileReader("JDBC.properties");
        p.load(fr);
        System.out.println(p);
        BasicDataSource bds = BasicDataSourceFactory.createDataSource(p);
        bds.setInitialSize(10);
        bds.setMaxIdle(5);
        Connection con = bds.getConnection();
        PreparedStatement ps = con.prepareStatement("select * from userinfo");
        ResultSet rs = ps.executeQuery();
        while(rs.next()){
            System.out.println(rs.getString(2));
        }
    }

}

C3P0 连接池

什么是 C3P0

C3P0 是目前较为流行的开源数据库连接池,支持 JDBC2 和 JDBC3 规的标准规范,易于扩展 并且性能优越,目前被 Hibernate、Spring 等框架使用。

使用 C3P0 需要导入的包

  • c3p0-0.9.5.2.jar
  • mchange-commons-java-0.2.11.jar
  • ComboPooedDataSource 是 DataSource 接口的实现类,主要包含设置数据源对象的方法

setDriverClass() 设置连接数据库的驱动名称
setJdbcUrl 设置连接数据库的路径
setUser() 设置数据库的登陆账号
setPassword() 设置数据库的登陆密码
setMaxPoolSize() 设置数据库连接池最大的连接数目
setMinPoolSize() 设置数据库连接池最小的连接数目
setInitialPoolSize() 设置数据库连接池初始化的连接数目
getConnection() 从连接池中获取一个数据库连接
想要使用 C3P0,首先需要创建数据源对象,创建数据源对象使用 ComboPooledDataSource 类

示例代码
public class Demo {
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        ComboPooledDataSource cpds = new ComboPooledDataSource();
        cpds.setDriverClass("com.mysql.jdbc.Driver");
        cpds.setJdbcUrl("jdbc:mysql:///stdb");
        cpds.setUser("root");
        cpds.setPassword("root");
        cpds.setMaxPoolSize(20);
        cpds.setInitialPoolSize(10);
        Connection con = cpds.getConnection();
        PreparedStatement ps = con.prepareStatement("select * from userinfo");
        ResultSet rs = ps.executeQuery();
        while(rs.next()){
            System.out.println(rs.getString(2));
        }
    }
}
public class Demo2 {

    public static void main(String[] args) throws SQLException {
        // TODO Auto-generated method stub

        DataSource cpds = new ComboPooledDataSource(); 
        Connection con = cpds.getConnection();
        PreparedStatement ps = con.prepareStatement("select * from userinfo");
        ResultSet rs = ps.executeQuery();
        while(rs.next()){
            System.out.println(rs.getString(2));
        }
    }

}

####c3p0-config.xml 配置文件

DBUtils

DBUtils 介绍

DBUtils 是对 JDBC 的简单封装,可在不影响性能的情况下简化 JDBC 的编码工作量。

使用 DBUtils 需要导入的包

commons-dbutils-1.7.jar

DBUtils 主要类

DBUtils

DBUtils 类主要为如何关闭连接、装载 JDBC 驱动程序之类的常规工作提供方法,它提供的都 是静态的方法。

  • close()方法

此方法有三个重载,可以关闭 Connection、Statement 和 ResultSet 这三个对象

  • closeQuietly()方法

可以关闭 Connection、Statement 和 ResultSet 这三个对象,和 close()相比,此方法 此方法内部会进行对象的非空判断,还会隐藏一些数据库抛出的异常

  • commitAndCloseQuietly()

用来提交连接,然后关闭连接,并且在关闭连接是不抛出 SQL 异常

  • loadDriver

用于装载并注册 JDBC 驱动,如果成功就返回 true,使用该方法不需要捕获 ClassNotFoundException 异常。

QueryRunner

QueryRunner 类简化了 SQL 语句的代码,与 ResultSetHandler 配合可以完成大部分数据库操 作,减少编码量。
- query(Connectionconn,Stringsql,ResultSetH:andlerrsh,Object[]params)

该方法用于查询操作
params 该数字中的每一个元素都用来作为查询语句的置换参数
该方法会自动处理 PreparedStatement 和 ResultSet 的创建和关闭

  • query(Stringsql,ResultSetHandlerrsh,Object…params)

该方法用于执行查询操作

  • query(Connectionconn,Stringsql,ResultSetHandlerrsh)

该方法用于执行一个不需要参数的查询。

  • update(Connectionconn,Stringsql,Object[]params)

该方法用来执行插入、更新或者删除等操作,其中参数 params 表示 SQL 语句中的 置换参数

  • update(Connectionconn,Stringsql)

该方法用于执行插入、更新或者删除等操作,不需要参数

ResultSetHandler 接口

ArrayHandler 将结果集中的第一条记录封装到一个 Object[]数组中,并返回
ArrayListHandler 将结果集中的每一条记录都封装到一个 Object[]数组中,再将数组存放到 List 集 合中。
BeanHandler 将结果集中第一条记录封装到一个指定的 JavaBean 中。
BeanListHandler 将结果集中每一条记录封装到指定的JavaBean中,再将这些JavaBean存放到List 集合中
ColumnListHandler 将结果集中指定的列的字段值,封装到一个 List 集合中
KeyedHandler 将结果集中每一条记录封装到 Map

示例代码
//c3p0工具类
public class C3p0Utils {
    static ComboPooledDataSource cpds;
    static {
        cpds = new ComboPooledDataSource();
    }

    public static DataSource getDataSource() {
        return cpds;
    }

}
//bean类
public class User {
    private int id;
    private String name;
    private String password;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public User(int id, String name, String password) {
        super();
        this.id = id;
        this.name = name;
        this.password = password;
    }

    public User() {
        super();
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", password=" + password + "]";
    }
}
//测试类
public class Demo {
    /**
     * 向表中添加数据
     */
    @Test
    public void add() {
        QueryRunner qr = new QueryRunner(C3p0Utils.getDataSource());
        String sql = "INSERT INTO user VALUES (?, ?, ?)";
        try {
            int y = qr.update(sql, 7, "小蓝", "123456");
            System.out.println(y);

        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /**
     * 更新表中数据
     */
    @Test
    public void update() {
        QueryRunner qr = new QueryRunner(C3p0Utils.getDataSource());
        String sql = "update user set name = ? where id = ?";
        try {
            int y = qr.update(sql, "小红", 7);
            System.out.println(y);

        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /**
     * 删除表中数据
     */
    @Test
    public void delete() {
        QueryRunner qr = new QueryRunner(C3p0Utils.getDataSource());
        String sql = "delete from user where id = ?";
        try {
            int y = qr.update(sql, 7);
            System.out.println(y);

        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    /**
     * 查询表中数据 ArrayHandler 将结果集中的第一条记录封装到一个 Object[]数组中,并返回
     */
    @Test
    public void select1() {
        QueryRunner qr = new QueryRunner(C3p0Utils.getDataSource());
        String sql = "select * from user";
        try {
            Object[] arr = qr.query(sql, new ArrayHandler());
            System.out.println(Arrays.toString(arr));

        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * 查询表中数据 ArrayListHandler 将结果集中的每一条记录都封装到一个 Object[]数组中,再将数组存放到 List 集合中。
     */
    @Test
    public void select2() {
        QueryRunner qr = new QueryRunner(C3p0Utils.getDataSource());
        String sql = "select * from user";
        try {
            List<Object[]> list = qr.query(sql, new ArrayListHandler());
            for (Object[] arr : list) {
                System.out.println(Arrays.toString(arr));
            }

        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * BeanHandler 将结果集中第一条记录封装到一个指定的 JavaBean 中。
     */
    @Test
    public void select3() {
        QueryRunner qr = new QueryRunner(C3p0Utils.getDataSource());
        String sql = "select * from user";
        try {
            User u = qr.query(sql, new BeanHandler<>(User.class));
            System.out.println(u);

        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * BeanListHandler 将结果集中每一条记录封装到指定的 JavaBean 中,再将这些 JavaBean 存放到 List 集合中
     */
    @Test
    public void select4() {
        QueryRunner qr = new QueryRunner(C3p0Utils.getDataSource());
        String sql = "select * from user";
        try {
            List<User> list = qr.query(sql, new BeanListHandler<>(User.class));
            for (User u : list) {
                System.out.println(u);
            }

        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * MapHandler 将结果集中第一行记录封装到了 Map<String,Object>集合中,key 是列名称,value 是对应字 段的值
     */
    @Test
    public void select5() {
        QueryRunner qr = new QueryRunner(C3p0Utils.getDataSource());
        String sql = "select * from user";
        try {
            Map<String, Object> map = qr.query(sql, new MapHandler());
            System.out.println(map);

        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * MapListHandler 将结果集中每一行记录封装到 Map<String,Object>集合中,key 就是字段名称,value
     * 就是字段值,再将每个 Map 存到 List 集合中。
     * 
     */
    @Test
    public void select6() {
        QueryRunner qr = new QueryRunner(C3p0Utils.getDataSource());
        String sql = "select * from user";
        try {
            List<Map<String, Object>> list = qr.query(sql, new MapListHandler());
            for (Map<String, Object> map : list) {

                System.out.println(map);
            }

        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * ScalarHandler 将结果集中的某一条记录的其中某一列数据存储成 Object 对象,一般用于单数 据。例如 select
     * count(*) from 表操作。
     * 
     */
    @Test
    public void select7() {
        QueryRunner qr = new QueryRunner(C3p0Utils.getDataSource());
        String sql = "select count(*) from user";
        try {
            Object obj = qr.query(sql, new ScalarHandler<>());
            System.out.println(obj);

        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * ColumnListHandler 将结果集中指定的列的字段值,封装到一个 List 集合中
     * 
     */
    @Test
    public void select8() {
        QueryRunner qr = new QueryRunner(C3p0Utils.getDataSource());
        String sql = "select * from user";
        try {
            List<Object> list = qr.query(sql, new ColumnListHandler<>());
            for (Object object : list) {
                System.out.println(object);
            }

        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * KeyedHandler 
     * 将结果集中每一条记录封装到 Map<String,Object>,在将这个 map 集合做为另一 个 Map 的value,另一个 Map 集合的 key 是指定的字段的值。
     * 
     */
    @Test
    public void select9() {
        QueryRunner qr = new QueryRunner(C3p0Utils.getDataSource());
        String sql = "select * from user";
        try {
            Map<Object, Map<String, Object>> query = qr.query(sql, new KeyedHandler<>());
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}