【转】【很全很新】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 事务 所有问题都搞清楚了。以后可能也用不着这些框架,但是希望能帮到一些用到了朋友吧。加粗样式
最新文章
- [Word]中批量修改图片大小和缩放比例方法
- Elasticsearch 聚合
- 第六章 第一个Linux驱动程序:统计单词个数
- Android开发--Socket通信
- 在VS2010下编译和使用tesseract_ocr识别验证码
- Mysql_mysql force Index 强制索引
- Android:Fragment+ViewPager实现Tab滑动
- XML和对象属性互转的基类
- mysql用户权限分配及主从同步复制
- python常用类型的内置函数列表
- python2.7 使用super关键词 报错 TypeError: must be type, not&;n
- LeetCode之旅(15)-Odd Even Linked List
- GNSS相关网站汇总
- typescript的函数
- CTex+WinEdt10.2安装详细教程与破解
- Python_datetime模块
- 使用dom4j中SAXReader解析xml数据
- 细胞迁移 | cell migration
- 第一篇 入门必备 (Android学习笔记)
- 使用WPF制作视频监控多画面切换