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

【转】https://blog.csdn.net/guozhaohui628/article/details/84793028

很久没用原生连接池,最近想写个小功能,结果发现很多地方不太懂,然后网上搜了半天的 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();
        }
    }

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

原文地址:https://www.cnblogs.com/sungong1987/p/11704765.html

时间: 2024-08-05 11:40:57

【转】【很全很新】C3P0 连接池和 DBUtils 配合事务使用总结的相关文章

C3P0连接池配置方式

c3p0的配置方式分为三种,分别是 1.setters一个个地设置各个配置项 2.类路径下提供一个c3p0.properties文件 3.类路径下提供一个c3p0-config.xml文件 1.setters一个个地设置各个配置项 这种方式最繁琐,形式一般是这样: Properties props = new Properties(); InputStream in = ConnectionManager.class.getResourceAsStream("/c3p0.properties&q

Java Web(十) JDBC的增删改查,C3P0等连接池,dbutils框架的使用

前面做了一个非常垃圾的小demo,真的无法直面它,菜的抠脚啊,真的菜,好好努力把.菜鸡. --WH 一.JDBC是什么? Java Data Base Connectivity,java数据库连接,在需要存储一些数据,或者拿到一些数据的时候,就需要往数据库里存取数据,那么java如何连接数据库呢?需要哪些步骤? 1.注册驱动 什么是驱动? 驱动就是JDBC实现类,通俗点讲,就是能够连接到数据库功能的东西就是驱动,由于市面上有很多数据库,Oracle.MySql等等,所以java就有一个连接数据库

c3p0连接池的学习

c3p0的学习 学习 学习 ! 首先c3p0的基本了解,在之前的web项目学习中都是直接使用jdbc的 DriverManager进行oracle数据库的连接,每次执行操作都会建立一个新的连接,在操作完成后,通过判断释放连接,但是如果处于高并发的情况下,就可能会造成服务器崩溃的后果,因为大量的资源同一时间得不到释放:以上是我自己对普通连接的学习和看法,采用c3p0连接池后,连接池会控制连接池内的连接对象数,以下是c3p0的xml配置文件: 1 <?xml version="1.0"

C3P0连接池一些基本配置

1 C3P0连接池配置 2 数据库连接是一个耗费大量资源且相当慢的操作,所以为了提高性能和连接速度,诞生了连接池这样的概念. 3 在多用户并发操作过程中,连接池尤为重要. 4 它是将那些已连接的数据库连接存放在一个容器里(连接池),这样以后别人要连接数据库的时候,将不会重新建立数据库连接,会直接从连接池里取出可用的连接,用户使用完毕后,连接又重新还回到连接池中. 5 注意:连接池里的连接将会一直保存在内存里,即使你没用也是一样.所以这个时候你得权衡一下连接池的连接数量了. 6 7 <c3p0-c

Maven 工程下 Spring MVC 站点配置 (三) C3P0连接池与@Autowired的应用

Maven 工程下 Spring MVC 站点配置 (一) Maven 工程下 Spring MVC 站点配置 (二) Mybatis数据操作 前两篇文章主要是对站点和数据库操作配置进行了演示,如果单单实现这两个需求的话,那么基本足够,但是很多时候一个网站除了仅仅能够访问数据库是不够的,它还需要对性能以及更简化的步骤有着更多的要求,这一篇重点就是帮助我们如何去实现数据连接池管理与更简化便利的开发步骤. 如果你觉得自己能写出更高效率的连接池,那你可以不需要这篇文章了,我更建议你可以去开源组织毛遂自

[转]C3P0连接池详细配置

<c3p0-config>   <default-config>  <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数.Default: 3 -->  <property name="acquireIncrement">3</property>    <!--定义在从数据库获取新连接失败后重复尝试的次数.Default: 30 -->  <property name="acquireRe

java学习笔记—c3p0连接池与元数据分析(42)

第一步:导入c3p0包 第二步:在classpath目录下,创建一个c3p0-config.xml <?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <!-- 默认配置,只可以出现一次 --> <default-config> <!-- 连接超时设置30秒 --> <property name="checkoutTimeout"

使用 Tomcat 7 新的连接池 —— Tomcat jdbc pool

Tomcat 在 7.0 以前的版本都是使用 commons-dbcp 做为连接池的实现,但是 dbcp 饱受诟病,原因有: dbcp 是单线程的,为了保证线程安全会锁整个连接池 dbcp 性能不佳 dbcp 太复杂,超过 60 个类 dbcp 使用静态接口,在 JDK 1.6 编译有问题 dbcp 发展滞后 因此很多人会选择一些第三方的连接池组件,例如 c3p0 , bonecp, druid (@wenshao ) 等. 为此,Tomcat 从 7.0 开始引入一个新的模块:Tomcat j

C3P0连接池配置

C3P0是一个开源的JDBC连接池,详情请google. 在spring中,C3P0的一些配置,介绍如下(只列了一部分,不是全部) C3P0更详细的配置项及其含义,请参考:http://www.mchange.com/projects/c3p0/index.html <!-- c3p0连接池配置 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"&g