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

栏目: Java · 发布时间: 6年前

内容简介:很久没用原生连接池,最近想写个小功能,结果发现很多地方不太懂,然后网上搜了半天的 c3p0 相关内容,全不符合我想要的。相同内容太多 而且没什么,所以我自己来总结下吧。从以下来总结首先我们操作数据库都需要连接,平时获取连接、关闭连接如果频繁,就会浪费资源,占用CPU。所以这里我们用一个池子来存放连接。 先自定义一个自己的连接池,这些内容太简单,我直接上代码。相信大家很容易看懂。

很久没用原生连接池,最近想写个小功能,结果发现很多地方不太懂,然后网上搜了半天的 c3p0 相关内容,全不符合我想要的。相同内容太多 而且没什么,所以我自己来总结下吧。

01 总结全文

从以下来总结

  • 连接池的作用,为什么要使用连接池
  • 书写自己的连接池,用于理解框架 c3p0 等连接池
  • 连接池框架 c3p0 使用
  • 连接池框架 c3p0 和 DBUtils 的配合使用
  • 配合事务的使用(重点,这块很多人都说不明白)

02 分析

0201 连接池的作用,为什么要使用连接池

首先我们操作数据库都需要连接,平时获取连接、关闭连接如果频繁,就会浪费资源,占用CPU。所以这里我们用一个池子来存放连接。 先自定义一个自己的连接池,这些内容太简单,我直接上代码。相信大家很容易看懂。

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。

第一步添加依赖

<!-- 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 然后就是它的配置,有两种情况

  • 默认不设定名字
  • 设定名字 首先来看默认不设定名字,比较简单,配置比较简单。代码中使用也可以直接使用。
<!-- 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>
复制代码

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

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

//加载默认配置文件
    //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();
        }
    }
复制代码
【很全很新】C3P0 连接池和 DBUtils 配合事务使用总结

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 事务 所有问题都搞清楚了。以后可能也用不着这些框架,但是希望能帮到一些用到了朋友吧。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Twenty Lectures on Algorithmic Game Theory

Twenty Lectures on Algorithmic Game Theory

Tim Roughgarden / Cambridge University Press / 2016-8-31 / USD 34.99

Computer science and economics have engaged in a lively interaction over the past fifteen years, resulting in the new field of algorithmic game theory. Many problems that are central to modern compute......一起来看看 《Twenty Lectures on Algorithmic Game Theory》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

html转js在线工具
html转js在线工具

html转js在线工具