天天看點

Mybatis原理分析一 從JDBC到Mybaits

本文主要講解JDBC怎麼演變到Mybatis的漸變過程,重點講解了為什麼要将JDBC封裝成Mybaits這樣一個持久層架構。再而論述Mybatis作為一個資料持久層架構本身有待改進之處。

我們先看看我們最熟悉也是最基礎的通過JDBC查詢資料庫資料,一般需要以下七個步驟:

(1)  加載JDBC驅動

(2)  建立并擷取資料庫連接配接

(3)  建立 JDBC Statements 對象

(4)  設定SQL語句的傳入參數

(5)  執行SQL語句并獲得查詢結果

(6)  對查詢結果進行轉換處理并将處理結果傳回

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

以下是具體的實作代碼:

   上面我們看到了實作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語句的重複

Mybaits所有的資料庫操作都是基于SQL語句,導緻什麼樣的資料庫操作都要寫SQL語句。一個應用系統要寫的SQL語句實在太多了。

改進方法:

我們對資料庫進行的操作大部分都是對表資料的增删改查,很多都是對單表的資料進行操作,由這點我們可以想到一個問題:單表操作可不可以不寫SQL語句,通過JavaBean的預設映射器生成對應的SQL語句,比如:一個類UserInfo對應于USER_INFO表, userId屬性對應于USER_ID字段。這樣我們就可以通過反射可以擷取到對應的表結構了,拼湊成對應的SQL語句顯然不是問題。

本文轉自二郎三郎部落格園部落格,原文連結:http://www.cnblogs.com/haore147/p/5219774.html,如需轉載請自行聯系原作者