【很全很新】C3P0 链接池和 DBUtils 配合事务使用总结

好久没用原生链接池,最近想写个小功能,结果发现不少地方不太懂,而后网上搜了半天的 c3p0 相关内容,全不符合我想要的。相同内容太多 并且没什么,因此我本身来总结下吧。mysql

01 总结全文

从如下来总结spring

  • 链接池的做用,为何要使用链接池
  • 书写本身的链接池,用于理解框架 c3p0 等链接池
  • 链接池框架 c3p0 使用
  • 链接池框架 c3p0 和 DBUtils 的配合使用
  • 配合事务的使用(重点,这块不少人都说不明白)

02 分析

0201 链接池的做用,为何要使用链接池

首先咱们操做数据库都须要链接,平时获取链接、关闭链接若是频繁,就会浪费资源,占用CPU。因此这里咱们用一个池子来存放链接。 先自定义一个本身的链接池,这些内容太简单,我直接上代码。相信你们很容易看懂。sql

public class CustomConnectionUtils {

    private static LinkedList<Connection>  pool = new LinkedList<Connection>();

    /**
     * 初始化链接池 添加3个链接到池子中去
     */
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            for(int i=0;i<3;i++){
                Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test1", "root", "root");
                pool.add(connection);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取链接
     * @return
     */
    public static Connection getConnection(){
        try {
            if(!pool.isEmpty()){
                Connection connection = pool.removeFirst();
                return connection;
            }
            //若是没有链接 等待 100 毫秒 而后继续
            Thread.sleep(100);
            return getConnection();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 归还链接 其实就是从新将链接添加到池子中去
     * @param connection
     */
    public static void release(Connection connection){
        if(connection != null){
            pool.add(connection);
        }
    }
}
复制代码

0202 c3p0链接池使用

免费的链接池有不少,若是不配合 spring 。单独使用,我我的仍是喜欢使用 c3p0。数据库

第一步 添加依赖api

<!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
    <dependency>
      <groupId>c3p0</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.1.2</version>
    </dependency>
复制代码

第二步 配置文件 配置文件命名只能 c3p0-config.xml ,位置放在 idea-->src-->resources-->c3p0-config.xml 而后就是它的配置,有两种状况bash

  • 默认不设定名字
  • 设定名字 首先来看默认不设定名字,比较简单,配置比较简单。代码中使用也能够直接使用。
<!-- This is default config! -->
	<default-config>
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql://localhost:3306/mylink2mv?characterEncoding=utf8</property>
		<property name="user">root</property>
		<property name="password">root</property>
		<property name="acquireIncrement">5</property>
		<property name="initialPoolSize">10</property>
		<property name="minPoolSize">5</property>
		<property name="maxPoolSize">20</property>
		<property name="maxStatements">0</property>
		<property name="maxStatementsPerConnection">5</property>
	</default-config>
复制代码

还有种方式就是设定配置名字,以下 test1框架

<!-- This is my config for mysql-->
	<named-config name="test1">
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="jdbcUrl">jdbc:mysql://localhost:3306/test1</property>
		<property name="user">root</property>
		<property name="password">root</property>
		<property name="initialPoolSize">10</property>
		<property name="maxIdleTime">30</property>
		<property name="maxPoolSize">100</property>
		<property name="minPoolSize">10</property>
		<property name="maxStatements">200</property>
	</named-config>
复制代码

剩下就是代码中的事情了。ide

第三步代码中初始化配置文件工具

//加载默认配置文件
    //private static final ComboPooledDataSource DATA_SOURCE_DEFAULT =new ComboPooledDataSource();
    
    //加载命名配置文件
    private static final ComboPooledDataSource DATA_SOURCE_TEST1 =new ComboPooledDataSource("test1");

复制代码

两种加载方式,都比较简单。稍微注意,就是这一行代码就已经读取配置文件了,不用作其余操做。这里已经获得的就是链接池了。这里咱们写个方法来获取,显得专业点,否则直接经过类名获取也没事。通常大部分举例都是采用读取默认名字的配置,这里我读取自定义的,将上面的代码注释掉。测试

第四步 获取链接

这里我写个简单的从链接池获取链接的方法。

public static ComboPooledDataSource getDataSource(){
        return pooledDataSource;
    }

    public static Connection getCoonection(){
        try {
            Connection connection = getDataSource().getConnection();
            return connection;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
复制代码

第五步 测试

这里由于还没开始用 DBUtils ,因此都是原生的 jdbc 方法。写了个简单的查询语句演示。

public static void main(String[] args) {
        try {
            Connection connection = C3P0Utils.getCoonection();
            PreparedStatement preparedStatement = null;
            ResultSet rs = null;
            String sql = "select * from my_movie;";
            preparedStatement = connection.prepareStatement(sql);
            rs = preparedStatement.executeQuery();
            while(rs.next()){
                String mName = rs.getString("mName");
                System.out.println(mName);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
复制代码

0203 DBUtils

如今开始使用 DBUtils ,注意它并非链接池 ,最开始学的时候二者老是分不清,它是结合链接池 C3P0 使用的工具类。如上面代码,发现没,就算不使用 DBUtils 也是能够的,只不过操做数据库那一块,增删改查太过于复杂,特别对于「查」来讲。对于返回的数据,处理很原生。可是有了 DBUtils 就不同了。它有三个核心功能,恰好用于解决项目实践中很容易碰到的问题。

  • QueryRunner 中提供对 sql 语句操做的 api
  • ResultSetHandler 接口,用于定义 select 操做后,怎样封装结果集
  • 定义了关闭资源与事务处理的方法

如上所说,这三个核心功能都是解决「痛点」问题的。

**QueryRunner **

一个个来看。首先 QueryRunner 类的构造方法有好三个,可是经常使用的能够分红两类

  • 带 connection 的
  • 不带 connection

首先来看不带 connection 的,也就是说直接传入链接池的。DBUtils 底层自动维护链接 connection 。

QueryRunner queryRunner  = new QueryRunner(C3P0Utils.getDataSource());
复制代码

这种是你们用的最多的,还有种就是默认的不带参数。

QueryRunner queryRunner  = new QueryRunner();
复制代码

这两种有什么区别呢? 为何涉及 connection 呢? 主要仍是事务!

这里先将结果丢出来,若是涉及事务,不能使用第一种构造。

再来看 QueryRunner 的方法 api 。两个操做数据库的 api 。

  • update(String sql, Object ... params) ,用于执行 增删改 sql 语句
  • query(String sql , ResutlSetHandler rsh , Object ... params) 执行 查询 sql 语句。

**ResultSetHandler **

第二部分中的查询语句中能够看出,对于查询语句要解析到实体类,特别麻烦,特别结果集是集合时,更加麻烦。可是实践中查询用的最多。因此 DBUtils 有专门的类来处理结果集。很是多,可是经常使用三个。

  • 结果集是单 bean ----> BeanHanler
  • 结果集是集合 ---- BeanListHandler
  • 结果集是个单数据。(我通常专用来获取 主键 id) ---- ScalarHandler

增删改操做都比较简单,比较特殊一点就是 **获取插入成功后的主键值 **,这点后再说,先将简单的写完。

/**
     * 添加
     */
    public void A() throws SQLException {
        QueryRunner queryRunner  = new QueryRunner(C3P0Utils.getDataSource());
        String sql = "insert into my_movie (mId,mName,mPrice,mDirector,mShowDate,cId) values (?,?,?,?,?,?)";
        Object params[] = {null, "邪恶力量", 20, "Haha", "2017-08-23", 2};
        int i = queryRunner.update(sql,params);
        System.out.println(i);
    }
复制代码
/**
     * 删除
     */
    public void B() throws SQLException {
        QueryRunner queryRunner  = new QueryRunner(C3P0Utils.getDataSource());
        String sql = "delete from my_movie where mId = ?";
        Object params[] = {7};
        int i = queryRunner.update(sql,params);
        System.out.println(i);
    }
复制代码
/**
     * 改
     */
    public void C() throws SQLException {
        QueryRunner queryRunner  = new QueryRunner(C3P0Utils.getDataSource());
        String sql = "update my_movie set mName = ? where mId = ?";
        Object params[] = {"绿箭侠",1};
        int i = queryRunner.update(sql,params);
        System.out.println(i);
    }
复制代码

上面三个方法我都试过,运行是没问题的。接下来就是 获取插入成功后的主键值。由于我碰到过这个需求,结果搜索 c3p0 dbutils 插入成功主键值 找到的相关文章都没解决个人问题。查了好久 踩了不少坑,这里总结下。

/**
     * 获取插入成功后的主键值
     */
   public void D() throws SQLException {
        QueryRunner queryRunner  = new QueryRunner(C3P0Utils.getDataSource());
        String sql = "insert into my_movie (mId,mName,mPrice,mDirector,mShowDate,cId) values (?,?,?,?,?,?)";
        Object params[] = {null, "邪恶力量", 20, "Haha", "2017-08-23", 2};
        //这里不能使用 update 而是 insert 使用以下参数
        Long i = (Long) queryRunner.insert(sql,new ScalarHandler(),params);
        System.out.println(i);
    }
复制代码

接下来是查询

@Test
    /**
     * 查询 获取单个结果集
     */
    public void E() throws SQLException {
        QueryRunner queryRunner  = new QueryRunner(C3P0Utils.getDataSource());
        String sql = "select * from my_movie where mId = ? ";
        Object params[] = {1};
        Movie movie = queryRunner.query(sql, new BeanHandler<Movie>(Movie.class), params);
        System.out.println(movie);
    }

    @Test
    /**
     * 查询 获取单个结果集
     */
    public void F() throws SQLException {
        QueryRunner queryRunner  = new QueryRunner(C3P0Utils.getDataSource());
        String sql = "select * from my_movie ";
        Object params[] = {};
        List<Movie> movieList = queryRunner.query(sql, new BeanListHandler<Movie>(Movie.class), params);
        System.out.println(movieList.toString());
    }
复制代码

0204 配合事务

到这里为止,一切都很美好,没啥问题。可是若是涉及 事务 呢。

若是一个方法中不止一个 sql 操做,而都在一个事务中。采用如上办法是不行的。由于这些 sql 语句的操做前提须要保证使用的是同一个链接

可是使用 QueryRunner 如上的构造方法是不行的。每个操做queryRunner.query可能都使用的不是同一个链接。因此咱们的作法是本身获取链接,而后做为参数传入。

/**
     * 事务
     */
    @Test
    public void G() {
        Connection connection = null;
        try {
            QueryRunner queryRunner  = new QueryRunner();
            connection = C3P0Utils.getCoonection();

            //开启事务
            connection.setAutoCommit(false);

            //全部的操做都带上参数 connection
            String sql01 = "insert into my_movie (mId,mName,mPrice,mDirector,mShowDate,cId) values (?,?,?,?,?,?)";
            Object params01[] = {null, "邪恶力量", 20, "Haha", "2017-08-23", 2};
            int i = queryRunner.update(connection,sql01,params01);

            String sql02 = "select * from my_movie ";
            Object params02[] = {};
            List<Movie> movieList = queryRunner.query(connection,sql02, new BeanListHandler<Movie>(Movie.class), params02);

            System.out.println(movieList);
        } catch (SQLException e) {

            //若是报错 回滚
            try {
                connection.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }finally {
            try {
                //无论成功 失败 都提交
                connection.commit();
                //关闭链接
                DbUtils.closeQuietly(connection);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
复制代码

上面代码我只是举例了插入和查询。其余都同样,带了 connection 参数便可。

03 尾声

基本 c3p0 dbutils 事务 全部问题都搞清楚了。之后可能也用不着这些框架,可是但愿能帮到一些用到了朋友吧。

相关文章
相关标签/搜索