很久没用原生连接池, 最近想写个小功能, 结果发现很多地方不太懂, 然后网上搜了半天的 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);
- }
- /**
- * 获取插入成功后的主键值
- */
- 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());
- }
- /**
- * 事务
- */
- @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();
- }
- }
- }
来源: https://juejin.im/post/5c08e0686fb9a049a62c60f2