mybatis由JDBC的演化過程分析

咱們知道,mybatis是對JDBC的封裝,那麼他是如何演變過來的呢?

摘自傳智傳媒Java培訓資料程序員

關於mybatis的演化原理,咱們先看看咱們最熟悉也是最基礎的經過JDBC查詢數據庫數據,通常須要如下七個步驟:

(1)  加載JDBC驅動sql

(2)  創建並獲取數據庫鏈接數據庫

(3)  建立 JDBC Statements 對象緩存

(4)  設置SQL語句的傳入參數數據結構

(5)  執行SQL語句並得到查詢結果mybatis

(6)  對查詢結果進行轉換處理並將處理結果返回架構

(7)  釋放相關資源(關閉Connection,關閉Statement,關閉ResultSet)oracle

如下是具體的實現代碼:模塊化

  1 public static List<Map<String,Object>> queryForList(){ 
  2 
  3         Connection connection = null; 
  4 
  5         ResultSet rs = null; 
  6 
  7         PreparedStatement stmt = null; 
  8 
  9         List<Map<String,Object>> resultList = new ArrayList<Map<String,Object>>();           
 10 
 11         try { 
 12 
 13             //加載JDBC驅動 
 14 
 15             Class.forName("oracle.jdbc.driver.OracleDriver").newInstance(); 
 16 
 17             String url = "jdbc:oracle:thin:@localhost:1521:ORACLEDB";               
 18 
 19             String user = "trainer";  
 20 
 21             String password = "trainer";                
 22 
 23             //獲取數據庫鏈接 
 24 
 25             connection = DriverManager.getConnection(url,user,password);                
 26 
 27             String sql = "select * from userinfo where user_id = ? "; 
 28 
 29             //建立Statement對象(每個Statement爲一次數據庫執行請求) 
 30 
 31 stmt = connection.prepareStatement(sql);               
 32 
 33             //設置傳入參數 
 34 
 35             stmt.setString(1, "zhangsan");               
 36 
 37             //執行SQL語句 
 38 
 39             rs = stmt.executeQuery();               
 40 
 41             //處理查詢結果(將查詢結果轉換成List<Map>格式) 
 42 
 43             ResultSetMetaData rsmd = rs.getMetaData(); 
 44 
 45             int num = rsmd.getColumnCount();               
 46 
 47             while(rs.next()){ 
 48 
 49                 Map map = new HashMap(); 
 50 
 51                 for(int i = 0;i < num;i++){ 
 52 
 53                     String columnName = rsmd.getColumnName(i+1); 
 54 
 55                     map.put(columnName,rs.getString(columnName)); 
 56 
 57                 } 
 58 
 59                 resultList.add(map); 
 60 
 61             }  
 62 
 63         } catch (Exception e) { 
 64 
 65             e.printStackTrace(); 
 66 
 67         } finally { 
 68 
 69             try { 
 70 
 71                    //關閉結果集 
 72 
 73                 if (rs != null) { 
 74 
 75                     rs.close(); 
 76 
 77                     rs = null; 
 78 
 79                 } 
 80 
 81                    //關閉執行 
 82 
 83                 if (stmt != null) { 
 84 
 85                     stmt.close(); 
 86 
 87                     stmt = null; 
 88 
 89                 } 
 90 
 91                 if (connection != null) { 
 92 
 93                     connection.close(); 
 94 
 95                     connection = null; 
 96 
 97                 } 
 98 
 99             } catch (SQLException e) { 
100 
101                 e.printStackTrace(); 
102 
103             } 
104 
105         } 
106   return resultList; 
110 
111 } 

Jdbc開始演變到mybatis性能

上面咱們看到了實現JDBC有七個步驟,哪些步驟是能夠進一步封裝的,減小咱們開發的代碼量

第一步優化:鏈接獲取和釋放

問題描述:

數據庫鏈接頻繁的開啓和關閉自己就形成了資源的浪費,影響系統的性能。

解決問題:

數據庫鏈接的獲取和關閉咱們可使用數據庫鏈接池來解決資源浪費的問題。經過鏈接池就能夠反覆利用已經創建的鏈接去訪問數據庫了。減小鏈接的開啓和關閉的時間。

問題描述:

可是如今鏈接池多種多樣,可能存在變化,有可能採用DBCP的鏈接池,也有可能採用容器自己的JNDI數據庫鏈接池。

解決問題:

咱們能夠經過DataSource進行隔離解耦,咱們統一從DataSource裏面獲取數據庫鏈接,DataSource具體由DBCP實現仍是由容器的JNDI實現均可以,因此咱們將DataSource的具體實現經過讓用戶配置來應對變化。

第二步優化:SQL統一存取

問題描述:

咱們使用JDBC進行操做數據庫時,SQL語句基本都散落在各個JAVA類中,這樣有三個不足之處:

第一,可讀性不好,不利於維護以及作性能調優。

第二,改動Java代碼須要從新編譯、打包部署。

第三,不利於取出SQL在數據庫客戶端執行(取出後還得刪掉中間的Java代碼,編寫好的SQL語句寫好後還得經過+號在Java進行拼湊)。

解決問題:

咱們能夠考慮不把SQL語句寫到Java代碼中,那麼把SQL語句放到哪裏呢?首先須要有一個統一存放的地方,咱們能夠將這些SQL語句統一集中放到配置文件或者數據庫裏面(以key-value的格式存放)。而後經過SQL語句的key值去獲取對應的SQL語句。

既然咱們將SQL語句都統一放在配置文件或者數據庫中,那麼這裏就涉及一個SQL語句的加載問題。

 第三步優化:傳入參數映射和動態SQL

問題描述:

不少狀況下,咱們均可以經過在SQL語句中設置佔位符來達到使用傳入參數的目的,這種方式自己就有必定侷限性,它是按照必定順序傳入參數的,要與佔位符一一匹配。可是,若是咱們傳入的參數是不肯定的(好比列表查詢,根據用戶填寫的查詢條件不一樣,傳入查詢的參數也是不一樣的,有時是一個參數、有時多是三個參數),那麼咱們就得在後臺代碼中本身根據請求的傳入參數去拼湊相應的SQL語句,這樣的話仍是避免不了在Java代碼裏面寫SQL語句的命運。既然咱們已經把SQL語句統一存放在配置文件或者數據庫中了,怎麼作到可以根據前臺傳入參數的不一樣,動態生成對應的SQL語句呢?

解決問題:

第一,咱們先解決這個動態問題,按照咱們正常的程序員思惟是,經過if和else這類的判斷來進行是最直觀的,這個時候咱們想到了JSTL中的<if test=」」></if>這樣的標籤,那麼,能不能將這類的標籤引入到SQL語句中呢?假設能夠,那麼咱們這裏就須要一個專門的SQL解析器來解析這樣的SQL語句,可是,if判斷的變量來自於哪裏呢?傳入的值自己是可變的,那麼咱們得爲這個值定義一個不變的變量名稱,並且這個變量名稱必須和對應的值要有對應關係,能夠經過這個變量名稱找到對應的值,這個時候咱們想到了key-value的Map。解析的時候根據變量名的具體值來判斷。

假如前面能夠判斷沒有問題,那麼假如判斷的結果是true,那麼就須要輸出的標籤裏面的SQL片斷,可是怎麼解決在標籤裏面使用變量名稱的問題呢?這裏咱們須要使用一種有別於SQL的語法來嵌入變量(好比使用#變量名#)。這樣,SQL語句通過解析後就能夠動態的生成符合上下文的SQL語句。

還有,怎麼區分開佔位符變量和非佔位變量?有時候咱們單單使用佔位符是知足不了的,佔位符只能爲查詢條件佔位,SQL語句其餘地方使用不了。這裏咱們可使用#變量名#表示佔位符變量,使用$變量名$表示非佔位符變量。

第四步優化:結果映射和結果緩存

問題描述:

執行SQL語句、獲取執行結果、對執行結果進行轉換處理、釋放相關資源是一整套下來的。假如是執行查詢語句,那麼執行SQL語句後,返回的是一個ResultSet結果集,這個時候咱們就須要將ResultSet對象的數據取出來,否則等到釋放資源時就取不到這些結果信息了。咱們從前面的優化來看,以及將獲取鏈接、設置傳入參數、執行SQL語句、釋放資源這些都封裝起來了,只剩下結果處理這塊尚未進行封裝,若是能封裝起來,每一個數據庫操做都不用本身寫那麼一大堆Java代碼,直接調用一個封裝的方法就能夠搞定了。

解決問題:

咱們分析一下,通常對執行結果的有哪些處理,有可能將結果不作任何處理就直接返回,也有可能將結果轉換成一個JavaBean對象返回、一個Map返回、一個List返回等等,結果處理多是多種多樣的。從這裏看,咱們必須告訴SQL處理器兩點:第一,須要返回什麼類型的對象;第二,須要返回的對象的數據結構怎麼跟執行的結果映射,這樣才能將具體的值copy到對應的數據結構上。

    接下來,咱們能夠進而考慮對SQL執行結果的緩存來提高性能。緩存數據都是key-value的格式,那麼這個key怎麼來呢?怎麼保證惟一呢?即便同一條SQL語句幾回訪問的過程當中因爲傳入參數的不一樣,獲得的執行SQL語句也是不一樣的。那麼緩存起來的時候是多對。可是SQL語句和傳入參數兩部分合起來能夠做爲數據緩存的key值。

 第五步優化:解決重複SQL語句問題

問題描述:

因爲咱們將全部SQL語句都放到配置文件中,這個時候會遇到一個SQL重複的問題,幾個功能的SQL語句其實都差很少,有些多是SELECT後面那段不一樣、有些多是WHERE語句不一樣。有時候表結構改了,那麼咱們就須要改多個地方,不利於維護。

解決問題:

當咱們的代碼程序出現重複代碼時怎麼辦?將重複的代碼抽離出來成爲獨立的一個類,而後在各個須要使用的地方進行引用。對於SQL重複的問題,咱們也能夠採用這種方式,經過將SQL片斷模塊化,將重複的SQL片斷獨立成一個SQL塊,而後在各個SQL語句引用重複的SQL塊,這樣須要修改時只須要修改一處便可。

優化總結:

咱們總結一下上面對JDBC的優化和封裝:

(1) 使用數據庫鏈接池對鏈接進行管理

(2) SQL語句統一存放到配置文件

(3) SQL語句變量和傳入參數的映射以及動態SQL

(4) 動態SQL語句的處理

(5) 對數據庫操做結果的映射和結果緩存

(6) SQL語句的重複

2.3.mybatis的架構設計

 

功能架構講解:

咱們把Mybatis的功能架構分爲三層:

(1)API接口層:提供給外部使用的接口API,開發人員經過這些本地API來操縱數據庫。接口層一接收到調用請求就會調用數據處理層來完成具體的數據處理。

(2)數據處理層:負責具體的SQL查找、SQL解析、SQL執行和執行結果映射處理等。它主要的目的是根據調用的請求完成一次數據庫操做。

(3)基礎支撐層:負責最基礎的功能支撐,包括鏈接管理、事務管理、配置加載和緩存處理,這些都是共用的東西,將他們抽取出來做爲最基礎的組件。爲上層的數據處理層提供最基礎的支撐。

相關文章
相關標籤/搜索