10、Java Web之JDBC连接池和DbUtils工具

一、什么是连接池:

    1. JDBC访问数据库的步骤: 
        1) 创建连接 --> 2) 访问数据库 --> 3) 关闭连接
   
    2. 使用连接池解决
        1) 由服务器在启动的时候,就创建好一定数据的连接对象。
        2) 用户从创建好的连接池中,直接取出一个连接对象即可

        3) 如果一个连接对象使用完毕,则将连接对象放回到连接池中。


二、数据库连接池API:

    1. 数据源接口
        javax.sql.DataSource 数据源(连接池),所有的连接池都必须要实现这个接口。
        
    2. 得到连接的方法:
        Connection getConnection() 不指定参数,得到一个连接对象
    
    3. 连接池常用的初始参数:(所有的连接池都有默认参数)
        1) 初始连接数,连接池创建的时候,创建的连接对象
        2) 最大连接数,连接池中连接数上限。
        3) 最大等待时间,如果超时,抛出异常。如果连接池中没有连接对象,等待多久。
        4) 最大空闲等待时间,如果一个用户得到了连接对象,多久没有发送SQL语句,就会被连接池回收。

(1)自定义连接池

 自定义连接池实现的几个步骤: 

1. 创建数据库连接类 JdbcUtils 。 

2. 创建类MyDataSource类 实现DataSource接口,该类下的步骤主要有: 

1) 批量的实现数据库的连接,并把创建的连接放入到LinkList中(因为要进行增和删操作,Linklist比ArrayList性能 高上许多); 

2)实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户, 3)在该类中创建方法addBackToPool(Connection con),当用户使用完后,重新将连接 放回到连接池中。 

3.编写测试类 进行测试 

具体的代码如下:

 数据库连接类

     —通过读取配置文件properties文件中的配置信息,配置文件就不列举了

package com.mystore.utils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ResourceBundle;

/**
 * 提供数据库连接池 和数据库连接
 * 方法为静态的 通过类型访问
 * @author huhongda
 * 
 */
public class JdbcUtils {

    private static String driverName = null;
    private static String url=null;
    private static String userName = null;
    private static String userPwd =null;
    //读取配置文件中的内容 
    //静态方法块
    static{
        ResourceBundle bundle = ResourceBundle.getBundle("dataBase");
         driverName = bundle.getString("driverName");
         url = bundle.getString("url");
         userName= bundle.getString("userName");
         userPwd = bundle.getString("userPwd");
    }
    //建立连接
    public static Connection getConnection(){
        try {
            //注册驱动
            loadDriver();
            //System.out.println("创建连接成功");
            //建立连接 并返回
            return DriverManager.getConnection(url, userName, userPwd);

        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
        return null;
    }
    //注册驱动
    public static void loadDriver(){
        try {
            Class.forName(driverName);
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
    //释放资源
    public static void release(ResultSet rs,Statement stat,Connection con){
        //存在连接或结果集的时候 释放
        if(rs!=null){
            try {
                rs.close();
            } catch (Exception e) {
                // TODO: handle exception
            }
            rs= null;
        }
        if(stat!=null){
            try {
                stat.close();
            } catch (Exception e) {
                // TODO: handle exception
            }
            stat = null;
        }
        if(con!=null){
            try {
                con.close();
            } catch (Exception e) {
                // TODO: handle exception
            }
            con = null;
        }
    }
}

连接池类

package DataSource;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.LinkedList;

import javax.sql.DataSource;

import com.mystore.utils.JdbcUtils;
//简单的实现自定义 连接池
public class MyDataSource implements DataSource {
    //通过 linkList充当 池
    private LinkedList<Connection> pool = new LinkedList<Connection>();
    //构造 函数 初始化 连接 数目
    public MyDataSource(){
        System.out.println("构造函数 创建 20个连接");
        for (int i = 0; i < 20; i++) {
            //创建 连接
            Connection connection = JdbcUtils.getConnection();
            //把 创建的连接 放入池子中
            pool.add(connection);
        }
    }
    //用完 之后  将传递的连接放回  池中
    public void addBackToPool(Connection connection){
        pool.add(connection);
    }
    @Override
    public PrintWriter getLogWriter() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }
    //从线程池 中 取得 第一个 
    @Override
    public Connection getConnection() throws SQLException {
        // TODO Auto-generated method stub
        //首先 判断 是否为空
                if(pool.isEmpty()){
                    //为空的话  继续创建 5个连接
                    for (int i = 0; i < 5; i++) {
                        Connection connection = JdbcUtils.getConnection();
                        pool.add(connection);
                    }
                }
                //有连接的话  就取出第一个
                Connection con = pool.removeFirst();
                System.out.println("取得一个连接 使用");
                return con;
    }

    @Override
    public Connection getConnection(String username, String password)
            throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

}

简单的测试类

package DataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import com.mystore.utils.JdbcUtils;

//对自定义 连接池的 测试
public class TestDataSource {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Connection connection = null;
        PreparedStatement stmt = null;
        MyDataSource ds = null;
        ResultSet rs =null;
        try {
            ds = new MyDataSource();
            //从连接池 中 取得 连接
            connection = ds.getConnection();
            stmt = connection.prepareStatement("update users set passward ='123' where userName=?");
            //给占位符 设值
            stmt.setString(1,"huhongda");
            //执行
            stmt.executeUpdate();

        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }finally{
            //最终  将 连接 放回到 连接池中
            ds.addBackToPool(connection);
            System.out.println("使用完 后将连接 放回 连接池中");
            //释放 资源 并 不将连接 释放
            JdbcUtils.release(rs, stmt, null);
        }

    }

}
(2)C3P0连接池:

        1) 开放源代码,免费使用
        2) Hibernate框架(访问数据库)中默认使用的连接池

    1. 使用步骤:

        1) 导包 c3p0-0.9.5.2.jar  、mchange-commons-java-0.2.12.jar
        2) 设置参数(通过源代码或通过配置文件)
            要设置两类参数:
            2.1) 数据库连接参数(用户名,密码,URL,驱动)
            2.2) 数据库连接池的参数
        3) 得到连接池(数据源)
        4) 从连接池中得到连接对象

复制代码
  1     public static void main(String[] args) throws Exception {
  2         // 1.创建数据源对象
  3         ComboPooledDataSource ds = new ComboPooledDataSource();
  4         // 2. 设置数据库的连接参数
  5         // 用户名,密码,URL,驱动
  6         ds.setUser("root");
  7         ds.setPassword("root");
  8         ds.setJdbcUrl("jdbc:mysql://localhost:3306/day25");
  9         ds.setDriverClass("com.mysql.jdbc.Driver");
 10         // 3. 设置连接池的参数
 11         ds.setInitialPoolSize(5); // 初始连接数
 12         ds.setMaxPoolSize(10); // 最大连接数
 13         ds.setCheckoutTimeout(2000); // 最长等待时间,单位:毫秒
 14         ds.setMaxIdleTime(1000); // 最长空闲等待时间
 15         // 4. 得到连接对象
 16         for (int i = 1; i <= 11; i++) {
 17             Connection conn = ds.getConnection();
 18             System.out.println("" + i + "个连接:" + conn);
 19             //第5个连接释放
 20             if (i ==5) {
 21                 conn.close();
 22             }
 23         }
 24     }
复制代码

使用配置文件配置参数:    
    1. 使用配置文件的好处:
        1) 因为所有的这些配置参数都写在源码中,以后修改不方便。
        2) 配置与源码是耦合在一起
    
    2. 配置文件的要求:
        1) 文件名:c3p0-config.xml
        2) 位置:放在类路径下,放在src目录下
        3) 在一个配置文件中,可以有多个配置:
            3.1) 默认配置:如果没有指定配置名,则使用默认配置
            3.2) 命名配置:如果指定了配置名,则使用指定的配置
    
    3. 多个不同配置的好处:
        1) 可以使用不同的连接池的配置参数
        2) 可以连接不同的数据库。如:db1, db2
        3) 可以使用不同厂商的数据库,如:MySQL,Oracle

  2. C3P0连接池的工具类:
      1).    创建私有静态数据源成员变量
      2).    创建公有的得到数据源的方法
      3).    创建得到连接对象的方法

复制代码
  1 /**
  2  * 连接池的工具类
  3  *
  4  * @author NewBoy
  5  *
  6  */
  7 public class C3P0Util {
  8     /**
  9      * 1. 创建私有静态数据源成员变量
 10      */
 11     private static ComboPooledDataSource ds = new ComboPooledDataSource();
 12 
 13     /**
 14      * 2. 创建公有的得到数据源的方法
 15      * @return
 16      */
 17     public static DataSource getDataSource() {
 18         return ds;
 19     }
 20 
 21     /**
 22      * 3. 创建得到连接对象的方法
 23      * @return
 24      */
 25     public static Connection getConnection() {
 26         try {
 27             return ds.getConnection();
 28         } catch (SQLException e) {
 29             e.printStackTrace();
 30         }
 31         return null;
 32     }
 33 
 34 }
复制代码

 

(3)DBCP连接池:  (DataBase Connection Pool 数据库连接池)

    1) 厂商:apache基金组织
    2) 开放源代码,免费
    3) Tomcat(Web服务器),默认使用的就是DBCP连接池

3. 使用步骤:

    1) 下载包
        commons-dbcp-1.4.jar   数据库连接池的核心包
        commons-pool-1.5.6.jar  辅导支持包
    2) 编写代码,创建连接池
        public class BasicDataSource implements DataSource 
    3) 设置参数
        3.1) 数据库的连接参数
        3.2) 连接池的参数
    4) 通过数据源得到连接对象

复制代码
  1         public class Demo1 {
  2 
  3             public static void main(String[] args) throws SQLException {
  4                 // 1.导包commons-dbcp-1.4.jar 数据库连接池的核心包 commons-pool-1.5.6.jar 辅导支持包
  5                 // 2) 编写代码,创建连接池
  6                 BasicDataSource ds = new BasicDataSource();
  7                 // 3) 设置参数
  8                 // 3.1) 数据库的连接参数(用户名,密码,URL,驱动)
  9                 ds.setUsername("root");
 10                 ds.setPassword("root");
 11                 ds.setUrl("jdbc:mysql://localhost:3306/day25");
 12                 ds.setDriverClassName("com.mysql.jdbc.Driver");
 13                 // 3.2) 连接池的参数(初始连接数,最大连接数,最长等待时间,最大等待个数)
 14                 ds.setInitialSize(5);
 15                 ds.setMaxActive(10);
 16                 ds.setMaxWait(2000);
 17                 ds.setMaxIdle(3);
 18                 // 4) 通过数据源得到连接对象
 19                 for (int i = 1; i <=11; i++) {
 20                     //Cannot get a connection, pool error Timeout waiting for idle object
 21                     Connection conn = ds.getConnection();
 22                     System.out.println("" + i + "个连接:" + conn.hashCode());
 23                     if (i == 5) {
 24                         conn.close();  //关闭了一个连接
 25                     }
 26                 }
 27             }
 28         }
复制代码

DBCP使用配置文件加载
    1. 配置文件
        文件名:xxx.properties
        内容:属性名=属性值
            
        1) 属性名:与set方法的名字相同,去掉set,首字母小写,如:setInitialSize 写成 initialSize
        2) 位置:建议放在src目录下,使用类加载器得到资源文件 

复制代码
  1 # database connection information
  2 username=root
  3 password=root
  4 url=jdbc:mysql://localhost:3306/day25
  5 driverClassName=com.mysql.jdbc.Driver
  6 # database connect pool information
  7 initialSize=5
  8 maxActive=10
  9 maxWait=2000
 10 maxIdle=3
复制代码
  1 代码要点:
  2         1) 使用Properties类加载属性文件
  3         2) 通过类路径加载输入流
  4         3) 通过工厂对象的静态方法,得到DataSource连接池对象
  5         4) 通过BasicDataSource类得到连接对象
  6 
  7 代码:
  8     public static void main(String[] args) throws Exception {
  9         // 1) 使用Properties类加载属性文件
 10         Properties info = new Properties();
 11         // 2) 通过类路径加载输入流
 12         info.load(Demo2.class.getClassLoader().getResourceAsStream("dbcp.properties"));
 13         // 3) 通过工厂对象的静态方法,得到DataSource连接池对象
 14         BasicDataSource ds = (BasicDataSource) BasicDataSourceFactory.createDataSource(info);
 15         // 4) 通过BasicDataSource类得到连接对象
 16         for (int i = 1; i <= 11; i++) {
 17             Connection conn = ds.getConnection();
 18             System.out.println(conn.hashCode());
 19             //释放一个
 20             if (i==3) {
 21                 conn.close();
 22             }
 23         }
 24     }


三.DbUtils工具

    1. 组织:    apache基金会
    2. 特点:
        1) 对JDBC进行封装,学习成本低,容易上手。
        2) 效率接近于直接使用JDBC的代码
        3) 几乎所有增删改查操作,都可以一句话搞定

 

一、主要类介绍
    1. 导入jar包:commons-dbutils-1.6.jar
    
    2. DbUtils类:
        1) 所有的方法都是静态方法
        2) 一组释放资源的方法
        static void    close(Connection conn)
        static void    close(ResultSet rs)
        static void    close(Statement stmt)
        static void    closeQuietly(Connection conn)
        static void    closeQuietly(Connection conn, Statement stmt, ResultSet rs)
    
    3. QueryRunner类(重点):
        进行增删改查的类
        
    4.    ResultSetHandler接口:
        用于处理查询的结果集,并且对结果集再次进行封装。
        ResultSet  ->  List<Student>

二. QueryRunner类的主要方法
    1. 方式一:
        1) 传入数据源的构造方法:
            QueryRunner(DataSource ds)
            
        2) 增删改的方法
            int    update(String sql) SQL语句
            int    update(String sql, Object... params)  SQL语句中有多个占位符,参数用来替换占位符的真实的值
            int    update(String sql, Object param) SQL语句中只有一个占位符,参数用来替换占位符的真实的值
            返回影响的行数
            
        3) 无需关闭连接
            因为update方法的内部已经关闭了连接对象

复制代码
  1         public class Demo1 {
  2             // 1.创建QueryRunner类,在构造方法中传入了数据源
  3             QueryRunner runner = new QueryRunner(C3P0Util.getDataSource());
  4 
  5             @Test
  6             public  void testAdd() throws SQLException {
  7                 // 2. 向数据库中添加一条记录
  8                 runner.update("insert into student (name,gender,birthday) values (?,?,?)", "唐僧", "",
  9                         java.sql.Date.valueOf("2000-11-11"));
 10             }
 11 
 12             @Test
 13             public void testUpdate() throws SQLException {
 14                 //将id为2的学生,性别改成女
 15                 runner.update("update student set gender=? where id=?", "", 2);
 16             }
 17 
 18             @Test
 19             public void testDelete() throws SQLException {
 20                 //删除姓名为:龟仙人记录
 21                 runner.update("delete from student where name=?", "龟仙人");
 22             }
 23         }
复制代码

     2. 方式二:
        1) 无参的构造方法:new QueryRunner()
        
        2) 增删改的方法 
            传入了连接对象
            int    update(Connection conn, String sql)  只有SQL语句
            int    update(Connection conn, String sql, Object... params)  多个参数,有多个占位符
            int    update(Connection conn, String sql, Object param) 一个参数,一个占位符

        3) 关闭连接
            需求自己手动关闭连接

复制代码
  1         // 创建QueryRunner类
  2         QueryRunner runner = new QueryRunner();
  3 
  4         @Test
  5         public void testAdd() throws SQLException {
  6             // 得到连接对象
  7             Connection conn = C3P0Util.getConnection();
  8             // 添加1条记录
  9             int row = runner.update(conn, "insert into student(name,gender,birthday) values (?,?,?)", "小乔", "",
 10                     java.sql.Date.valueOf("2000-03-20"));
 11             //连接对象需要自己关闭
 12             DbUtils.close(conn);
 13             System.out.println(row);
 14         }
复制代码

    3. 查询有关的方法(需要时实现ResultSetHandler接口)
        1) 没有连接对象的
        <T> T    query(String sql, ResultSetHandler<T> rsh)
            传入SQL语句和结果集的处理类
            
        <T> T    query(String sql, ResultSetHandler<T> rsh, Object... params)
            sql: SQL语句
            rsh:结果集处理类
            params:替换占位符的参数
        
        2) 有连接对象的
        <T> T    query(Connection conn, String sql, ResultSetHandler<T> rsh)
        <T> T    query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params)

三. ResultSetHandler接口
    1. 方法:
        T    handle(ResultSet rs) 将结果集转成一个对象

复制代码
  1  实现类:
  2         示例:查询学生信息表中1条记录
  3 
  4         public class Demo3 {
  5             //创建QueryRunner对象
  6             QueryRunner runner = new QueryRunner(C3P0Util.getDataSource());
  7 
  8             @Test
  9             public void testQueryStudent() throws SQLException{
 10                 //通过id查询一个学生
 11                 Student student = runner.query("select * from student where id=?", new StudentHandler(), 3);
 12                 System.out.println(student);
 13             }
 14         }
 15 
 16         public class StudentHandler implements ResultSetHandler<Student> {
 17             @Override
 18             public Student handle(ResultSet rs) throws SQLException {
 19                 //如果有结果集
 20                 Student student = new Student();
 21                 if (rs.next()) {
 22                     student.setId(rs.getInt("id"));
 23                     student.setName(rs.getString("name"));
 24                     student.setGender(rs.getString("gender"));
 25                     student.setBirthday(rs.getDate("birthday"));
 26                 }
 27                 return student;
 28             }
 29         }
复制代码

     2. 常用的实现类(已经实现了ResultSetHandler接口):
        1) 封装成JavaBean:查询一条记录的时候
            前提:表的列名与类的属性名相同
            BeanHandler<>();
            
        2) 封装成List<JavaBean>
            BeanListHandler<>();
            查询多条记录的时候
            
        3) 单行单列:用于聚合函数查询
            ScalarHandler<>();

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值