《笔者带你剖析Apache Commons DbUtils 1.6》
前言
关于Apache的DbUtils中间件或许了解的人并不多,大部分开发人员在生成环境中更多的是依靠Hibernate、Ibatis、Spring JDBC、JPA等大厂提供的持久层技术解决方案,或者是企业内部自己研发的持久层技术。但无论如何,使用这些技术的初衷和本质都是为了能够减少企业开发成本,提高生产效率,降低耦合。
放眼企业级项目,Hibernate等ORM产品是首选,而互联网领域,大部分开发人员往往并不会在生产环境中上这些ORM技术,原因很简单,要的就是效率,其次都不重要。对于刚接触SQL和JDBC的开发人员,最引以为傲的就是希望能够在日后编写复杂的SQL语句,以及会使用诸如Hibernate、Ibatis等第三方持久层技术,并且极力的撇清与传统JDBC技术的关系,但笔者不得不认为,这是一种普遍业界存在的“病态”!
如果是企业级的项目,尤其是跟金融相关的业务,SQL语句或许会非常复杂,并且关联着事物。但互联网项目却并非如此,在互联网项目中,看你牛不牛逼并不是取决于你能否写出一条复杂的SQL语句,而是看你能否将原本一条复杂的SQL语句拆散成单条SQL,一句一句的执行;并且脱离Hibernate等ORM产品后,能否使用传统的JDBC技术完成一条简单的CRUD操作,这才是牛逼!是的,你没有听错,互联网确确实实就是这么玩,还原最本质的东西,才是追求性能的不二选择。
笔者本章不会提及垂直分库、水平分区等数据库概念,以及数据路由中间件等技术(请阅读笔者博文《剖析淘宝TDDL—Matrix层分库分表实现》),因为这些内容与本章内容无关,但间接来看,笔者之前提及的单条SQL、使用JDBC完成基本的CRUD操作就可以在最大程度上满足一个互联网场景的持久层操作。以Hibernate为例,简单来说需要经历HQL->SQL->DBMS等编译过程,中间还冗余着缓存、对象等开销,希望大家记住,封装层次越高,性能越低!这个是无可争议的事实。笔者希望大家接下来,暂时“忘记”掉你所会的持久层技术,耐心的听笔者为你介绍Apache的DbUtils技术,或许你会有意想不到的收获。
目录
一、Apache Commons DbUtils简介;
二、下载与安装DbUtils;
三、使用DbUtils完成CRUD操作;
四、C3P0连接池集成DbUtils;
五、常用包、类讲解;
六、自动封装结果集;
七、事物管理;
一、Apache Commons DbUtils简介;
Apache的DbUtils工具是一个轻量级的持久层解决方案,天生为性能而生,它简单的对JDBC进行了必要的操作封装,让开发人员能够以一种高级API的方式使用JDBC技术完成原本复杂的CRUD操作。换句话说,DbUtils天生就不是一个复杂的技术,它只是一个简单的JDBC上层封装,对开发人员而言,大概只需半小时就能够完全掌握DbUtils技术的使用,是的,它就是这么简单与方便,它是互联网项目的宠儿,选择DbUtils技术作为持久层的解决方案,或许能够让你从原本复杂的Hibernate操作中解脱出来,或者是你觉得Ibatis不够好用,DbUtils也是你选择的理由之一。总之,使用它,你将会感到惊艳,它是如此的简单和干净,如此的纯粹和高效!并且DbUtils是采用商业友好的开源协议,大家甚至可以下载它的源码,进行二次开发,以此满足企业自身的需要。
二、下载与安装DbUtils;
当大家对DbUtils的项目背景有所了解后,接下来本节内容笔者将会告诉你它的下载和安装。大家可以登录http://commons.apache.org/站点下载DbUtils工具的最新版本,笔者使用的版本为1.6.0,在此大家需要注意,为了避免在开发过程中出现异常,建议大家下载、使用与笔者本篇博文一致的版本。
当大家成功下载好DbUtils相关的构件后,我们可以将其添加到项目中的ClassPath目录下,当然笔者后续小节会提及DbUtils与C3P0连接池的集成,因此,大家最好将C3P0所需的构件以及数据库驱动(笔者使用Mysql)一起添加到项目中。使用DbUtils时关联的构件,如下所示:
3、使用DbUtils完成CRUD操作;
废话不多说,使用DbUtils操作数据库之前,首先要做的事情就是获取Connection。那么为了方便,笔者使用硬编码的方式将数据源的配置信息coding在代码中(生产环境中,有可能是配置在项目的配置文件中、数据库中、Diamond中等),如下所示:
/** * 数据源信息 * * @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操作即可(互联网场景下同样也是这么要求)。
@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); } }
四、C3P0连接池集成DbUtils;
在生产环境中,开发人员在对数据库进行CRUD操作的时候,由于数据库的链接是有限的,因此不得不使用连接池来实现资源复用,以此降低数据库的性能瓶颈(尽管这么说有些不太友好,因为并发环境下,单靠连接池是不能够解决问题的,而常用的方案更多是诸如Redis之类的内存数据库抗住70%传统DBMS数据的受访压力、数据库先做垂直分库,再做水平分区,当然Master/Sleave是必不可少的,经过这些步骤之后,才能够说基本上解决了理论上可能出现的数据库在高并发环境下的瓶颈)。
废话不多说,在之前的ConnectionManager中添加进连接池相关的代码,当然为了方便,笔者同样还是使用硬编码的方式,如下所示:
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可控最好。
五、常用包、类讲解;
相信大家已经从上述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包下的常用类,如下所示:
1、DbUtils : 提供如关闭连接、装载 JDBC 驱动程序等常规工作的工具类;
2、QueryRunner : 该类简单化了 SQL 查询,它常与与 ResultSetHandler 组合在一起使用;
org.apache.commons.dbutils.handlers包下的常用类,如下所示:
1、ArrayHandler :将ResultSet中第一行的数据转化成对象数组;
2、ArrayListHandler:将ResultSet中所有的数据转化成List,List中存放的是Object[];
3、BeanHandler :将ResultSet中第一行的数据转化成类对象;
4、BeanListHandler :将ResultSet中所有的数据转化成List,List中存放的是类对象;
5、ColumnListHandler :将ResultSet中某一列的数据存成List,List中存放的是Object对象;
6、KeyedHandler :将ResultSet中存成映射,key为某一列对应为Map。Map中存放的是数据;
7、MapHandler :将ResultSet中第一行的数据存成Map映射;
8、MapListHandler :将ResultSet中所有的数据存成List。List中存放的是Map;
9、ScalarHandler :将ResultSet中一条记录的其中某一列的数据存成Object;
org.apache.commons.dbutils.wrappers包下的常用类,如下所示:
1、SqlNullCheckedResultSet :该类是用来对sql语句执行完成之后的的数值进行null的替换;
2、StringTrimmedResultSet :去除ResultSet中中字段的左右空格;
六、自动封装结果集;
在org.apache.commons.dbutils.handlers包下的类型,大部分都是与查询结果集相关的。试想一下,利用传统的JDBC进行查询时,返回的数据我们需要对ResultSet进行迭代,这是相当麻烦的,且不利于维护,因为我们需要手动编写与之相关的数据封装。但是使用DbUtils之后,我们要做的事情仅仅只是告诉DbUtils我们需要什么样的数据即可,关于数据封装这种通用的控制逻辑,则无需开发人员参与,这极大的节省了开发人员的时间,提升了生产效率。
简单来说,笔者在开发过程中使用最广泛的就是BeanListHandler以及MapListHandler 封装的结果集。简单来说,BeanListHandler将会查询后的数据封装到一个对应的POJO中(可以看做是一个无状态的实体Bean),MapListHandler 会将查询后的数据封装为一个List,List中存储的就是一个个的Map集合,通过key-value的方式获取封装后的数据集。先来看看MapListHandler 的使用,如下所示:
@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的使用示例,如下所示:
@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来完成查询结果集的字段映射封装操作。
七、事物管理;
说起事物管理,这其实是一个非常复杂与繁琐,且是最容易出错的场景,尤其是在手动管理事物操作上。当然本节所提及的事物管理仍然是建立在基于手动管理的事物操作上。对于JDBC操作,如果希望事物不要手动提交,那么在获取Connection的时候,一定需要将设置conn.setAutoCommit(false);这样一来事物就不会自动进行提交,当我们手动执行conn.commit()方法的时候,事物才会进行提交。这种方式对于DbUtils其实是一样的,之前也说过,DbUtils仅仅只是对JDBC做了一个轻量级的上层封装,那么必然可以和JDBC进行混用,一旦我们在程序中设定了事物后,接下来的事物管理操作就依赖与开发人员自身了,DbUtils将不会再参与事物的管理。
对于大多数开发人员而言,事物控制的不好,将会导致业务出现问题,脏数据等情况是非常常见的,但从另一个层面来说,手动的事物管理其实是最灵活和方便的。在此需要提醒大家,如果是使用Mysql数据库,只有将数据库引擎设置为InnoDB后,才会支持事物!
最后笔者在啰嗦一下,使用完资源后,我们一定要记得及时释放掉资源,以此避免无用资源长时间挂起。那么在DbUtils中,你将有2种方式结束掉Connection,第一个是使用DbUtils.close()方法。其次,你将可以直接使用close()方法关闭Connection的链接。
小结
本章内容到此结束,由于时间仓库,本文或许有很多不尽人意的地方,希望各位能够理解和体谅。
相关推荐
win7修复本地系统工具
《自动化专业英语》04-Automatic-Detection-Block(自动检测模块).ppt
《计算机专业英语》chapter12-Intelligent-Transportation.ppt
内容概要:本文详细介绍了基于西门子S7-1200博图平台的3轴伺服螺丝机程序。该程序使用SCL语言编写,结合KTP700组态和TIA V14及以上版本,实现了对X、Y、Z三个轴的精密控制。文章首先概述了程序的整体架构,强调了其在自动化控制领域的高参考价值。接着深入探讨了关键代码片段,如轴初始化、运动控制以及主程序的设计思路。此外,还展示了如何通过KTP700组态实现人机交互,并分享了一些实用的操作技巧和技术细节,如状态机设计、HMI交互、异常处理等。 适用人群:从事自动化控制系统开发的技术人员,尤其是对西门子PLC编程感兴趣的工程师。 使用场景及目标:适用于希望深入了解西门子S7-1200博图平台及其SCL语言编程特点的学习者;旨在帮助读者掌握3轴伺服系统的具体实现方法,提高实际项目中的编程能力。 其他说明:文中提供的代码示例和设计理念不仅有助于理解和学习,还能直接应用于类似的实际工程项目中。
内容概要:本文详细探讨了五种非线性滤波器(卡尔曼滤波(KF)、扩展卡尔曼滤波(EKF)、无迹卡尔曼滤波(UKF)、粒子滤波(PF)和变维卡尔曼滤波(VDKF))在水下长基线定位(LBL)系统中的应用。通过对每种滤波器的具体实现进行MATLAB代码展示,分析了它们在不同条件下的优缺点。例如,KF适用于线性系统但在非线性环境中失效;EKF通过雅可比矩阵线性化处理非线性问题,但在剧烈机动时表现不佳;UKF利用sigma点处理非线性,精度较高但计算量大;PF采用蒙特卡罗方法,鲁棒性强但计算耗时;VDKF能够动态调整状态维度,适合信标数量变化的场景。 适合人群:从事水下机器人(AUV)导航研究的技术人员、研究生以及对非线性滤波感兴趣的科研工作者。 使用场景及目标:①理解各种非线性滤波器的工作原理及其在水下定位中的具体应用;②评估不同滤波器在特定条件下的性能,以便为实际项目选择合适的滤波器;③掌握MATLAB实现非线性滤波器的方法和技术。 其他说明:文中提供了详细的MATLAB代码片段,帮助读者更好地理解和实现这些滤波器。此外,还讨论了数值稳定性问题和一些实用技巧,如Cholesky分解失败的处理方法。
VMware-workstation-full-14.1.3-9474260
DeepSeek系列-提示词工程和落地场景.pdf
javaSE阶段面试题
《综合布线施工技术》第5章-综合布线工程测试.ppt
安川机器人NX100使用说明书.pdf
内容概要:本文详细介绍了将M7120型平面磨床的传统继电器控制系统升级为基于西门子S7-1200 PLC的自动化控制系统的过程。主要内容涵盖IO分配、梯形图设计和组态画面实现。通过合理的IO分配,确保了系统的可靠性和可维护性;梯形图设计实现了主控制逻辑、砂轮升降控制和报警逻辑等功能;组态画面则提供了友好的人机交互界面,便于操作和监控。此次改造显著提高了设备的自动化水平、运行效率和可靠性,降低了维护成本。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是熟悉PLC编程和控制系统设计的专业人士。 使用场景及目标:适用于需要进行老旧设备升级改造的企业,旨在提高生产设备的自动化水平和可靠性,降低故障率和维护成本。具体应用场景包括但不限于金属加工行业中的平面磨床等设备的控制系统改造。 其他说明:文中还分享了一些实际调试中的经验和技巧,如急停逻辑的设计、信号抖动的处理方法等,有助于读者在类似项目中借鉴和应用。
chromedriver-linux64-136.0.7103.48.zip
IMG_20250421_180507.jpg
《网络营销策划实务》项目一-网络营销策划认知.ppt
Lianantech_Security-Vulnerabil_1744433229
MybatisCodeHelperNew2019.1-2023.1-3.4.1
【深度学习部署】基于Docker的BERT模型训练与API服务部署:实现代码复用与模型共享
摘 要 传统办法管理信息首先需要花费的时间比较多,其次数据出错率比较高,而且对错误的数据进行更改也比较困难,最后,检索数据费事费力。因此,在计算机上安装火车票订票系统软件来发挥其高效地信息处理的作用,可以规范信息管理流程,让管理工作可以系统化和程序化,同时,火车票订票系统的有效运用可以帮助管理人员准确快速地处理信息。 火车票订票系统在对开发工具的选择上也很慎重,为了便于开发实现,选择的开发工具为Eclipse,选择的数据库工具为Mysql。以此搭建开发环境实现火车票订票系统的功能。其中管理员管理用户,新闻公告。 火车票订票系统是一款运用软件开发技术设计实现的应用系统,在信息处理上可以达到快速的目的,不管是针对数据添加,数据维护和统计,以及数据查询等处理要求,火车票订票系统都可以轻松应对。 关键词:火车票订票系统;SpringBoot框架,系统分析,数据库设计
【ABB机器人】-00标准保养简介.pdf
最新校园跑腿小程序源码 多校版本,多模块,适合跑腿,外卖,表白,二手,快递等校园服务 此版本为独立版本,不需要微擎 直接放入就可以 需要自己准备好后台的服务器,已认证的小程序,备案的域名!