14.数据库连接池【C3P0、阿里Druid、Spring JDBC】

数据库连接池

概念: 一个容器(集合),存放数据库连接的容器。当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,当用户访问完之后,会将连接对象归还给容器。

好处:

  1. 节约资源
  2. 用户访问高效

技术支持:

  1. C3P0:数据库连接池技术
  2. Druid:【很牛逼】数据库连接池实现技术,阿里巴巴提供

一、标准接口【javax.sql.DataSource】【具体实现由数据库厂商去完成】

javax.sql.DataSource接口中的方法:

  • Connection getConnection():获取连接
  • Connection.close():如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不再会关闭连接了。

二、C3P0数据库连接池技术

1.使用步骤

  1. 导包jar:
    • 数据库驱动包【前提】
    • c3p0-0.9.5.2.jar
    • mchange-commons-java-0.2.12.jar
  2. 定义配置文件
    • 名称:c3p0.properties 或者 c3p0-config.xml
    • 路径:src目录下
  3. 创建核心对象 数据库连接池对象 ComboPooledDataSource
  4. 获取连接:getConnection(),执行业务逻辑
  5. 归还连接:Connection.close()

2.代码实现

// 对应的包
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

# ---------------------------------------- #

// 1.创建数据库连接池对象【使用默认配置,传入参数则使用对应的配置】
DataSource ds = new ComboPooledDataSource();

// 2.获取连接对象
Connection conn = ds.getConnection();

// 3.业务逻辑
String sql = "create table student(" +
        "id int primary key auto_increment," +
        "name varchar(10)," +
        "age int," +
        "gender char(3)," +
        "birthday Date)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.executeUpdate();
ps.close();

// 4. 归还连接对象
conn.close();

四、Druid数据库连接池技术,【阿里巴巴提供】

1.使用步骤

  1. 导入 druid-1.0.9.jar 包【也得导入数据库驱动包】
  2. 定义配置文件
    • 可以是 properties 形式
    • 可以叫任意名称,可以放在任意目录下
  3. 加载配置文件:推荐 properties
  4. 获取数据库连接池对象:通过工厂类来获取
    • DruidDataSourceFactory
  5. 获取连接: getConnection

2. 代码实现

import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;

public class DruidLearn {

    public static void main(String[] args) throws Exception {
        // 1.加载配置文件
        Properties pro = new Properties();
        InputStream in = DruidLearn.class
                         .getClassLoader()
                         .getResourceAsStream("druid.properties");
        pro.load(in);

        // 2.获取连接池对象
        DataSource ds = DruidDataSourceFactory.createDataSource(pro);

        // 3.获取连接
        Connection conn = ds.getConnection();

        // 4.执行力业务逻辑
        String sql1 = "create table user(id int primary key auto_increment,name varchar(10),password varchar(16))";
        PreparedStatement ps = conn.prepareStatement(sql1);
        int res = ps.executeUpdate();
        String sql2 = "insert into user values(null,'晓庆','abc123'),(null,'小竹','123abc')";
        ps = conn.prepareStatement(sql2);
        int res2 = ps.executeUpdate();
        String sql3 = "select * from user";
        ps = conn.prepareStatement(sql3);
        ResultSet rs = ps.executeQuery();
        while (rs.next()) { int id = rs.getInt("id");String name = rs.getString("name");String password = rs.getString("password");System.out.println("编号:" + id + " 姓名:" + name + " 密码:" + password); }

        // 5.释放资源
        rs.close();
        ps.close();
        // 6.归还资源
        conn.close();
    }
}

五、封装一个Druid工具类JDBCUtils

  1. 提供静态代码块加载排至文件,初始化连接池对象
  2. 提供的方法:
    • 获取连接池的方法
    • 通过数据库连接池获取连接
    • 释放资源
package JDBCUtils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JDBCUtils {
    /**
     * 1.定义成员变量 DateSource 连接池
     */
    private static DataSource ds;

    /**
     * 2.加载配置文件
     */
    static {
        try{
            Properties pro = new Properties();
            pro.load(JDBCUtils.class.getClassLoader()
                    .getResourceAsStream("druid.properties"));

            // 根据工厂类获取数据库连接池对象 SataSource
            ds = DruidDataSourceFactory.createDataSource(pro);

        } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); }
    }

    /**
     * 获取连接池
     */
    public static DataSource getDataSource() {
        return ds;
    }

    /**
     * 获取连接
     */
    public static Connection getConnection() {
        try { return ds.getConnection(); } catch (Exception e) { e.printStackTrace();return null; }
    }

    /**
     * 释放资源
     */

    public static void close(Statement stmt, Connection conn) {
        if(stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } }
        if(conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } }
    }

    /**
     * 释放资源; 重载
     */
    public static void close(ResultSet rs, Statement stmt, Connection conn) {
        if(rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } }
        close(stmt, conn);
    }
}

六、Spring JDBC

Spring 框架对JDBC的简单封装,提供了一个JdbcTemplate对象简化JDBC的开发

1.操作步骤【使用上方实现的JDBCUtils工具箱类】

  1. 导入jar包,例如:
    • commons-logging-1.2.jar;
    • spring-beans-5.0.0.RELEASE.jar;
    • spring-core-5.0.0.RELEASE.jar;
    • spring-jdbc-5.0.0.RELEASE.jar;
    • spring-tx-5.0.0.RELEASE.jar;
  2. 创建JdbcTemplate对象。依赖于数据源 DataSource【从JDBCUtils获得】
    • JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource())
  3. 调用JdbcTemplate的方法来完成CRUD的操作

2.JdbcTemplate类中的方法

  1. int update(String sql, ...): 执行DML语句,增、删、改语句。
  2. Map<String,Object> QueryForMap(String sql, ...): 查询结果将结果集封装成map集合,将列名作为key,将值作为value,将这条记录封装为一个map集合。
    • 注意:此方法查询的结果集长度只能是【1】,也就是只能是一条记录。
  3. List<Map<String,Object>> queryForList(String sql, ...): 查询结果将结果集封装为list集合
    • 注意:将每一条记录封装为一个Map集合,再将Map集合在到List集合中
  4. List<?> query(String sql, RowMapper<?> rm):查询结果,将结果封装为JavaBean对象。
    • 参数:RowMapper接口:一般使用 BeanPropertyRowMapper实现类。当然可以重写内部的mapRow抽象方法.完成数据到JavaBean的自动封装

    new BeanPropertyMapper<E>(E.class):【E必须是JavaBean规范类,内部的反射机制才好工作!

  5. E queryForObject(String sql, E.class): 查询结果,将结果封装为对象,一般为基本数据类型对象
    • 注意: 一般用于聚合函数的查询

使用JdbcTemplate中的方法,得到JdbcTemplate对象后,直接进行SQL操作,不需要任何额外申请资源或者释放资源的操作!资源用完了就释放,结果集也是Map,List或者基本数据类型的包装类

3.案例展示

代码中使用JDBCUtils工具类是上面所描述的,Student类包含属性【int id,String name,int age,String gender】,JavaBean规范类,代码略!

package jdbcTemplate;

import JDBCUtils.JDBCUtils;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

public class JDBCTemplate {

    // 获取JDBCTemplate对象
    private static JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());

    // 创建一张表格
    public static void create() {
        String sql = "create table student(" +
                "id int primary key auto_increment," +
                "name varchar(10)," +
                "age int," +
                "gender varchar(6))";
        int count = template.update(sql);
        System.out.println(count);
    }

    // 添加一些记录
    public static void insert() {
        String sql = "insert into student values " +
                "(null,?,?,?)," +
                "(null,?,?,?);";
        int count = template.update(sql, "晓庆",18,"女","洪祥",20,"男");
        System.out.println(count);
    }

    // 修改记录
    public static void update() {
        String sql = "update student set age = ? where name = ?";
        int res = template.update(sql, 22, "小竹");
        System.out.println(res);
    }

    // 删除一些记录
    public static void delect() {
        String sql = "delete from student where name in (?, ?)";
        int res = template.update(sql,"晓庆", "洪祥");
        System.out.println(res);
    }

    /**
     * 查询操作
     */
    // queryForMap - 查询的结果记录值允许一条!
    public static void queryForMap() {
        String sql = "select * from student where name=?";
        Map<String, Object> map = template.queryForMap(sql, "晓庆");
        System.out.println(map);
    }

    // 5.查询所有记录,将其封装为List
    public static void queryForList() {
        String sql = "select * from student";
        List<Map<String, Object>> list = template.queryForList(sql);
        for (Map<String, Object> map : list) {
            System.out.println(map);
        }
    }

    // 6.查询所有记录,将其封装为Student对象的List集合【接口方法重写】
    public static void queryOne() {
        String sql = "select * from student";
        List<Student> list = template.query(sql,new RowMapper<Student>() {

            /**
             * mapRow的返回值是一个对象而不是List集合,
             * 说明参数ResultSet是遍历查询的每个结果
             * */
            @Override
            public Student mapRow(ResultSet rs, int i) throws SQLException {
                Student student = new Student();
                student.setId(rs.getInt("id"));
                student.setName(rs.getString("name"));
                student.setAge(rs.getInt("age"));
                student.setGender(rs.getString("gender"));
                return student;
            }
        });

        for (Student student : list) {
            System.out.println(student);
        }
    }

    // 7.查询所有记录,将其封装为Student对象的List集合
    public static void queryTwo() {
        String sql = "select * from student;";
        List<Student> list = template.query(sql, new BeanPropertyRowMapper<>(Student.class));
        for (Student student : list) {
            System.out.println(student);
        }
    }


    // 8.查询总记录数
    public static void queryAgg() {
        String sql = "select count(*) number from student";
        Long total = template.queryForObject(sql,Long.class);
        System.out.println(total);
    }

    public static void main(String[] args) {
        // create();
        // insert();
        // update();
        // delect();

        /*查询操作!*/
        // queryForMap();
        // queryForList();
        // queryOne();
        // queryTwo();
        // queryAgg();

    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容