前言java
关于Apache的DbUtils中间件或许了解的人并很少,大部分开发人员在生成环境中更多的是依靠Hibernate、Ibatis、Spring JDBC、JPA等大厂提供的持久层技术解决方案,或者是企业内部本身研发的持久层技术。但不管如何,使用这些技术的初衷和本质都是为了可以减小企业开发成本,提升生产效率,下降耦合。mysql
放眼企业级项目,Hibernate等ORM产品是首选,而互联网领域,大部分开发人员每每并不会在生产环境中上这些ORM技术,缘由很简单,要的就是效率,其次都不重要。对于刚接触SQL和JDBC的开发人员,最引觉得傲的就是但愿可以在往后编写复杂的SQL语句,以及会使用诸如Hibernate、Ibatis等第三方持久层技术,而且极力的撇清与传统JDBC技术的关系,但笔者不得不认为,这是一种广泛业界存在的“病态”!sql
若是是企业级的项目,尤为是跟金融相关的业务,SQL语句或许会很是复杂,而且关联着事物。但互联网项目却并不是如此,在互联网项目中,看你牛不牛逼并非取决于你可否写出一条复杂的SQL语句,而是看你可否将本来一条复杂的SQL语句拆散成单条SQL,一句一句的执行;而且脱离Hibernate等ORM产品后,可否使用传统的JDBC技术完成一条简单的CRUD操做,这才是牛逼!是的,你没有听错,互联网确确实实就是这么玩,还原最本质的东西,才是追求性能的不二选择。数据库
笔者本章不会说起垂直分库、水平分区等数据库概念,以及数据路由中间件等技术(请阅读笔者博文《剖析淘宝TDDL—Matrix层分库分表实现》),由于这些内容与本章内容无关,但间接来看,笔者以前说起的单条SQL、使用JDBC完成基本的CRUD操做就能够在最大程度上知足一个互联网场景的持久层操做。以Hibernate为例,简单来讲须要经历HQL->SQL->DBMS等编译过程,中间还冗余着缓存、对象等开销,但愿你们记住,封装层次越高,性能越低!这个是无可争议的事实。笔者但愿你们接下来,暂时“忘记”掉你所会的持久层技术,耐心的听笔者为你介绍Apache的DbUtils技术,或许你会有意想不到的收获。apache
目录数组
1、Apache Commons DbUtils简介;缓存
2、下载与安装DbUtils;并发
3、使用DbUtils完成CRUD操做;app
4、C3P0链接池集成DbUtils;高并发
5、经常使用包、类讲解;
6、自动封装结果集;
7、事物管理;
1、Apache Commons DbUtils简介;
Apache的DbUtils工具是一个轻量级的持久层解决方案,天生为性能而生,它简单的对JDBC进行了必要的操做封装,让开发人员可以以一种高级API的方式使用JDBC技术完成本来复杂的CRUD操做。换句话说,DbUtils天生就不是一个复杂的技术,它只是一个简单的JDBC上层封装,对开发人员而言,大概只需半小时就可以彻底掌握DbUtils技术的使用,是的,它就是这么简单与方便,它是互联网项目的宠儿,选择DbUtils技术做为持久层的解决方案,或许可以让你从本来复杂的Hibernate操做中解脱出来,或者是你以为Ibatis不够好用,DbUtils也是你选择的理由之一。总之,使用它,你将会感到惊艳,它是如此的简单和干净,如此的纯粹和高效!而且DbUtils是采用商业友好的开源协议,你们甚至能够下载它的源码,进行二次开发,以此知足企业自身的须要。
2、下载与安装DbUtils;
当你们对DbUtils的项目背景有所了解后,接下来本节内容笔者将会告诉你它的下载和安装。你们能够登陆http://commons.apache.org/站点下载DbUtils工具的最新版本,笔者使用的版本为1.6.0,在此你们须要注意,为了不在开发过程当中出现异常,建议你们下载、使用与笔者本篇博文一致的版本。
当你们成功下载好DbUtils相关的构件后,咱们能够将其添加到项目中的ClassPath目录下,固然笔者后续小节会说起DbUtils与C3P0链接池的集成,所以,你们最好将C3P0所需的构件以及数据库驱动(笔者使用Mysql)一块儿添加到项目中。使用DbUtils时关联的构件,以下所示:
三、使用DbUtils完成CRUD操做;
废话很少说,使用DbUtils操做数据库以前,首先要作的事情就是获取Connection。那么为了方便,笔者使用硬编码的方式将数据源的配置信息coding在代码中(生产环境中,有多是配置在项目的配置文件中、数据库中、Diamond中等),以下所示:
Java代码 /** * 数据源信息 * * @author gaoxianglong */ public class ConnectionManager { public static Connection getConnection() { Connection conn = null; try { Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection( "jdbc:mysql://ip:port/dbName", "userName", "passWord"); } catch (Exception e) { e.printStackTrace(); } return conn; } }
当编写好ConnectionManager以后,接下来要作的事情就是获取Connection,而后就可以使用DbUtils进行CRUD操做了。或许细心的读者已经发现,使用DbUtils实际上是很是简单的,须要会的很少,仅仅只须要掌握JDBC操做以及简单的CRUD操做便可(互联网场景下一样也是这么要求)。
Java代码 @Test public void testInsert() { final String SQL = "insert into test_1 values(?, ?)"; try { if (null == conn || conn.isClosed()) conn = ConnectionManager.getConnection2(); int result = new QueryRunner().update(conn, SQL, new Object[] { "JohnGao1", "123" }); if (0 < result) System.out.println("数据插入成功..."); } catch (Exception e) { e.printStackTrace(); } finally { close(conn); } } @Test public void testUpdate() { final String SQL = "update test_1 set password= ? where username = ?"; try { if (null == conn || conn.isClosed()) conn = ConnectionManager.getConnection(); int result = new QueryRunner().update(conn, SQL, new Object[] { "321", "JohnGao1" }); if (0 < result) System.out.println("数据更新成功..."); } catch (Exception e) { e.printStackTrace(); } finally { close(conn); } } @Test public void testDelete() { final String SQL = "delete from test_1 where username like ?"; try { if (null == conn || conn.isClosed()) conn = ConnectionManager.getConnection(); int result = new QueryRunner().update(conn, SQL, "%JohnGao%"); if (0 < result) System.out.println("数据删除成功..."); } catch (Exception e) { e.printStackTrace(); } finally { close(conn); } } @Test public void testQuery() { final String SQL = "select * from test_1"; try { if (null == conn || conn.isClosed()) conn = ConnectionManager.getConnection(); Test_1Bean test1Bean = new QueryRunner().query(conn, SQL, new BeanHandler(Test_1Bean.class)); if (null != test1Bean) { System.out.println(test1Bean.getUsername()); System.out.println(test1Bean.getPassword()); } } catch (Exception e) { e.printStackTrace(); } finally { close(conn); } }
4、C3P0链接池集成DbUtils;
在生产环境中,开发人员在对数据库进行CRUD操做的时候,因为数据库的连接是有限的,所以不得不使用链接池来实现资源复用,以此下降数据库的性能瓶颈(尽管这么说有些不太友好,由于并发环境下,单靠链接池是不可以解决问题的,而经常使用的方案更可能是诸如Redis之类的内存数据库抗住70%传统DBMS数据的受访压力、数据库先作垂直分库,再作水平分区,固然Master/Sleave是必不可少的,通过这些步骤以后,才可以说基本上解决了理论上可能出现的数据库在高并发环境下的瓶颈)。
废话很少说,在以前的ConnectionManager中添加进链接池相关的代码,固然为了方便,笔者一样仍是使用硬编码的方式,以下所示:
Java代码 public static ComboPooledDataSource dataSource; static { try { dataSource = new ComboPooledDataSource(); dataSource.setUser("userName"); dataSource.setPassword("passWord"); dataSource.setJdbcUrl("jdbc:mysql://ip:port/dbName"); dataSource.setDriverClass("com.mysql.jdbc.Driver"); dataSource.setInitialPoolSize(10); dataSource.setMinPoolSize(5); dataSource.setMaxPoolSize(50); dataSource.setMaxStatements(100); dataSource.setMaxIdleTime(60); } catch (Exception e) { e.printStackTrace(); } } /** * 从链接池中获取数据源连接 * * @author gaoxianglong * * @return Connection 数据源连接 */ public static Connection getConnection2() { Connection conn = null; if (null != dataSource) { try { conn = dataSource.getConnection(); } catch (SQLException e) { e.printStackTrace(); } } return conn; }
当成功在ConnectionManager中添加好所需的C3P0链接池配置后,接下来要作的事情就是考虑如何使用C3P0与DbUtils之间的集成。其实最简单的作法就是直接将以前获取Connection的getConnection()方法更换为上述代码中的getConnection2()便可,一样可使用在建立QueryRunner实例时,将数据源的DataSource传递过去,这样便可避免在执行CRUD操做时,还须要在方法中指明Connection。
固然究竟应该怎么作,彻底取决于你本身,若是但愿方便,那么笔者建议你在建立QueryRunner实例时,直接将C3P0的DataSource传递过去,但这样作的弊端很明显,若是在特殊的场景下,须要手动控制事物时,那么这种操做是极其不便的,由于Connection并不可控。那么为了解决事物控制的问题,固然是Connection可控最好。
5、经常使用包、类讲解;
相信你们已经从上述DbUtils的CRUD示例中发现了QueryRunner的身影,那么笔者接下来就将会针对DbUtils中诸如QueryRunner等经常使用类型进行深刻讲解。
在DbUtils中,最经常使用的3个包为org.apache.commons.dbutils、org.apache.commons.dbutils.handlers以及org.apache.commons.dbutils.wrappers。
org.apache.commons.dbutils包下的经常使用类,以下所示:
一、DbUtils : 提供如关闭链接、装载 JDBC 驱动程序等常规工做的工具类;
二、QueryRunner : 该类简单化了 SQL 查询,它常与与 ResultSetHandler 组合在一块儿使用;
org.apache.commons.dbutils.handlers包下的经常使用类,以下所示:
一、ArrayHandler :将ResultSet中第一行的数据转化成对象数组;
二、ArrayListHandler:将ResultSet中全部的数据转化成List,List中存放的是Object[];
三、BeanHandler :将ResultSet中第一行的数据转化成类对象;
四、BeanListHandler :将ResultSet中全部的数据转化成List,List中存放的是类对象;
五、ColumnListHandler :将ResultSet中某一列的数据存成List,List中存放的是Object对象;
六、KeyedHandler :将ResultSet中存成映射,key为某一列对应为Map。Map中存放的是数据;
七、MapHandler :将ResultSet中第一行的数据存成Map映射;
八、MapListHandler :将ResultSet中全部的数据存成List。List中存放的是Map;
九、ScalarHandler :将ResultSet中一条记录的其中某一列的数据存成Object;
org.apache.commons.dbutils.wrappers包下的经常使用类,以下所示:
一、SqlNullCheckedResultSet :该类是用来对sql语句执行完成以后的的数值进行null的替换;
二、StringTrimmedResultSet :去除ResultSet中中字段的左右空格;
6、自动封装结果集;
在org.apache.commons.dbutils.handlers包下的类型,大部分都是与查询结果集相关的。试想一下,利用传统的JDBC进行查询时,返回的数据咱们须要对ResultSet进行迭代,这是至关麻烦的,且不利于维护,由于咱们须要手动编写与之相关的数据封装。可是使用DbUtils以后,咱们要作的事情仅仅只是告诉DbUtils咱们须要什么样的数据便可,关于数据封装这种通用的控制逻辑,则无需开发人员参与,这极大的节省了开发人员的时间,提高了生产效率。
简单来讲,笔者在开发过程当中使用最普遍的就是BeanListHandler以及MapListHandler 封装的结果集。简单来讲,BeanListHandler将会查询后的数据封装到一个对应的POJO中(能够看作是一个无状态的实体Bean),MapListHandler 会将查询后的数据封装为一个List,List中存储的就是一个个的Map集合,经过key-value的方式获取封装后的数据集。先来看看MapListHandler 的使用,以下所示:
Java代码 @Test public void testQuery4() { final String SQL = "select * from test_1 where username like ?"; try { if (null == conn || conn.isClosed()) conn = ConnectionManager.getConnection2(); List<Map<String, Object>> values = new QueryRunner().query(conn, SQL, new Object[] { "%JohnGao%" }, new MapListHandler()); if (null != values) { for (int i = 0; i < values.size(); i++) { Map<String, Object> map = values.get(i); System.out.println(map.get("username")); System.out.println(map.get("password")); } } } catch (Exception e) { e.printStackTrace(); } finally { close(conn); } }
若是你喜欢相似于实体Bean的操做方式,那么BeanListHandler无疑使最好的选择。一旦咱们使用BeanListHandler做为数据返回后的结果集封装,那么DbUtils便会将查询后的结果集一个字段一个字段的映射到指定的POJO中,固然前提就是字段名称是必须一致的,不然DbUtils将没法完成数据封装。BeanListHandler的使用示例,以下所示:
Java代码 @Test public void testQuery3() { final String SQL = "select * from test_1 where username like ?"; try { if (null == conn || conn.isClosed()) conn = ConnectionManager.getConnection(); List<Test_1Bean> test1Beans = new QueryRunner().query(conn, SQL, new Object[] { "%JohnGao%" }, new BeanListHandler( Test_1Bean.class)); if (null != test1Beans) { for (Test_1Bean test1Bean : test1Beans) { System.out.println(test1Bean.getUsername()); System.out.println(test1Bean.getPassword()); } } } catch (Exception e) { e.printStackTrace(); } finally { close(conn); } }
在此你们须要注意,为了方便演示,笔者在此并无提供对应的POJO。若是有须要,你们能够编写一个与数据库表字段相同的POJO来完成查询结果集的字段映射封装操做。
7、事物管理;
提及事物管理,这实际上是一个很是复杂与繁琐,且是最容易出错的场景,尤为是在手动管理事物操做上。固然本节所说起的事物管理仍然是创建在基于手动管理的事物操做上。对于JDBC操做,若是但愿事物不要手动提交,那么在获取Connection的时候,必定须要将设置conn.setAutoCommit(false);这样一来事物就不会自动进行提交,当咱们手动执行conn.commit()方法的时候,事物才会进行提交。这种方式对于DbUtils实际上是同样的,以前也说过,DbUtils仅仅只是对JDBC作了一个轻量级的上层封装,那么必然能够和JDBC进行混用,一旦咱们在程序中设定了事物后,接下来的事物管理操做就依赖与开发人员自身了,DbUtils将不会再参与事物的管理。
对于大多数开发人员而言,事物控制的很差,将会致使业务出现问题,脏数据等状况是很是常见的,但从另外一个层面来讲,手动的事物管理实际上是最灵活和方便的。在此须要提醒你们,若是是使用Mysql数据库,只有将数据库引擎设置为InnoDB后,才会支持事物!
最后笔者在啰嗦一下,使用完资源后,咱们必定要记得及时释放掉资源,以此避免无用资源长时间挂起。那么在DbUtils中,你将有2种方式结束掉Connection,第一个是使用DbUtils.close()方法。其次,你将能够直接使用close()方法关闭Connection的连接。