前言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的連接。