博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【很全很新】C3P0 连接池和 DBUtils 配合事务使用总结
阅读量:6001 次
发布时间:2019-06-20

本文共 9066 字,大约阅读时间需要 30 分钟。

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

01 总结全文

从以下来总结

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

02 分析

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

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

public class CustomConnectionUtils {    private static LinkedList
pool = new LinkedList
(); /** * 初始化连接池 添加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。

第一步 添加依赖

c3p0
c3p0
0.9.1.2
复制代码

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

  • 默认不设定名字
  • 设定名字 首先来看默认不设定名字,比较简单,配置比较简单。代码中使用也可以直接使用。
com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/mylink2mv?characterEncoding=utf8
root
root
5
10
5
20
0
5
复制代码

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

com.mysql.jdbc.Driver
jdbc:mysql://localhost:3306/test1
root
root
10
30
100
10
200
复制代码

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

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

//加载默认配置文件    //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.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
movieList = queryRunner.query(sql, new BeanListHandler
(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
movieList = queryRunner.query(connection,sql02, new BeanListHandler
(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 事务 所有问题都搞清楚了。以后可能也用不着这些框架,但是希望能帮到一些用到了朋友吧。

转载地址:http://qtcmx.baihongyu.com/

你可能感兴趣的文章
WARN hdfs.DFSClient: Caught exception java.lang.InterruptedException
查看>>
移动硬盘文件或目录损坏且无法读取怎么解决
查看>>
在shell中使用sed命令替换/为\/
查看>>
JavaSe: 不要小看了 Serializable
查看>>
Node.js 抓取电影天堂新上电影节目单及ftp链接
查看>>
linux popen函数
查看>>
[游戏开发]关于手游客户端网络带宽压力的一点思考
查看>>
如何成为强大的程序员?
查看>>
How To: 用 SharePoint 计算列做出你自己的KPI列表
查看>>
Visual Studio下使用jQuery的10个技巧
查看>>
数据库查询某个字段值的位数 语法
查看>>
java file 文件操作 operate file of java
查看>>
WPF获取路径解读
查看>>
【实战HTML5与CSS3】用HTML5和CSS3制作页面(上)
查看>>
Android : 如何在WebView显示的页面中查找内容
查看>>
数字信号处理 基础知识 对比回顾
查看>>
分享个人Vim型材
查看>>
配置算法(第4版)的Java编译环境
查看>>
本学习笔记TCP/IP传输协议
查看>>
荣耀10GT升级EMUI 9.0体验分享:这可能是最好用的手机操作系统
查看>>