一起來看MyBatis(一)

憶說就懂發表於2017-07-07
老規矩,在正式開始之前,我們們閒扯一會。一提到MyBatis,得先知道是啥,用來做什麼的等一系列問題,然後呢?然後就用起來咯,不知道大家有時候會不會有這樣一種感覺,比如談到某種技術,像多執行緒,集合,Redis,Docker,儲存過程等等,即使之前聽過或者用過,突然腦子會一片空白,一臉懵逼,緊接著會問自己,我現在在做什麼?算了,還是打遊戲去吧。
1.MyBatis是什麼
那今天我們要聊的MyBatis是什麼呢?我們不妨來看下度娘上的解釋


看完這段話,有些人更加懵逼了,比沒看之前更煩躁,都是一些專業化的術語。不過我們至少能知道的是,MyBatis是有一個叫"iBatis"的傢伙演變過來的,soga,原來它還有前生。並且還能夠知道,原先它是apache下的一個開源專案,後來呢,它經過兩次搬家,第一次是搬到了google code上,後來又搬到了Github上。

第二段話我們知道它命名的由來,並且能夠知道它是一個"持久層"的框架,說到持久層的框架,大家可能聽過hibernate,沒聽過也沒關係,只是順便提一下,大家都屬於持久層框架,認個親戚。
好了,廢話了這麼多,其實還是沒明白MyBatis是幹嘛的,我們就假裝明白了它大概的做什麼的,具體我們們把它用起來就會有體會了,目前最缺的是一個demo,有demo什麼都好說,具體形象,文字遊戲太抽象了,不易於理解。

2.引入MyBatis
既然MyBatis是一個持久層的框架,那麼肯定和資料庫的操作相關,這是我們的一個直觀感受。說到運算元據庫,大家肯定能夠聯想到JDBC技術,那MyBatis和JDBC的關係是怎樣的呢?先給大家丟擲一個概念,MyBatis實際上就是對JDBC的封裝,也就是把JDBC中一些複雜的不方便的過程給封裝起來,提供給開發者的是方便靈活的操作方式,這樣說還是有點抽象,我們們繼續往下看。

之前給大家寫過一份《聊聊JDBC》,如果大家對JDBC不熟悉的話,可以先去看下那份文章。
這裡預設為大家都熟悉JDBC或者已經看過之前寫的那份文章,那我們知道,JDBC是Java程式運算元據庫的一個媒介,通過JDBC,可以通過寫Java程式碼對資料表中的資料進行CRUD操作,為了說明問題,我拿一張查詢的截圖過來。

上面的圖片展現的過程比較容易理解,是通過JDBC技術,查詢某個資料表中的資料,然後將查詢的結果迴圈遍歷出來賦值給實體類中的屬性。我們可以把過程來寫一下。
(1)載入資料庫驅動類
(2)根據url,user,pass得到一個資料庫連線物件
(3)構建一條sql語句
(4)根據連線物件和sql語句得到一個PreparedStatement物件,並給其中的引數賦值
(5)執行PreparedStatement物件的executeQuery()方法得到一個ResultSet結果集
(6)迴圈遍歷ResultSet結果集中的內容賦值給Admin物件的各個屬性
當然,這個過程不是很嚴謹,比如還需要關閉資源,不過這是一條主線。
既然說MyBatis框架是對JDBC的封裝,那麼哪裡可以進行封裝,哪裡可以進行簡化,我們看接下里的一個小節。

3.我們理解的MyBatis
MyBatis到底對JDBC進行了哪些封裝與簡化?我們從上面的過程可以看出,其中(1),(2)這兩步是不是可以簡化,看過我們的《資料庫連線池》的同學應該可以想到,得到資料庫連線物件可以交給資料庫連線池去管理;第(3)步構建出一個sql語句,這是最核心的,不能簡化,所以這一塊在MyBatis框架中保留出來給開發者自己去寫;第(4),(5)這兩步在MyBatis中可以進行更好的優化管理;第(6)步這種迴圈遍歷顯然程式碼量比較大,也沒啥意思,所以也交給MyBatis來管理。

綜上所述,我們可以看出來,對於一個查詢操作,以前的JDBC方式,需要進行6步,而在MyBatis中更加關心的只有第(3)步,把精力集中在sql語句的編寫上,但是要注意,MyBatis框架呼叫的底層還是JDBC的那幾個過程。這裡順便提一下,hibernate對JDBC的封裝就更深了,完全不需要程式設計師關心sql語句的編寫。

實際上,大家有沒有看出來,MyBatis框架做的事情就是把資料表中的資料和實體類物件進行一個對映,只不過這裡需要我們關注sql語句的編寫,而hibernate框架也是做這樣一件事,不過不需要關心sql語句的編寫。所以hibernate是一個orm框架,MyBatis更像是一個半orm框架。
好了,說了這麼多,似乎比之前直接看度娘要懂得多一點了,說來說去,MyBatis就是對JDBC程式碼進行了一些封裝與簡化唄,更加方便我們開發者使用了,沒錯,可以先暫時這麼理解。為啥是說暫時呢?因為MyBatis除了這些,還對JDBC程式碼進行了很多優化,這些細節我們們先不討論。

4.用一用MyBatis
既然說MyBatis是對JDBC程式碼的封裝,也就是把那6步進行了一個簡化,那它是如何簡化的?原來每一步對應在MyBatis中又是如何的?這裡我們還是以一個demo為例,好了,我要發大招咯。

(1)匯入mybatis相關的jar包
這一點大家沒疑問吧?在使用一門新技術時,肯定要想想把人家的jar包拿過來,說白了就是把人家寫好的那些類拿過來,然後才能使用人家的技術。因為Java是物件導向的開發語言,即使前面把MyBatis說的那麼神奇,歸根結底還是要使用哪些類,哪些方法等等。對了,在次之前不要忘了新建工程,Java工程或者Java Web工程都可以。
那這裡我就先新建一個Java Web工程,名稱為"ES9D_MyBatis1",啥意思?字首"ES9D"含義為"憶說就懂",字尾"MyBatis1"含義為...這就不說了。我們們命名也稍微正規化一點,不能瞎來。

然後就匯入mybatis相關的jar包,我在網上找了一個版本3.1.1,大家自己可以去下載。另外需要說明的一點是,除了mybatis jar包,大家覺得還需要其他jar包嗎?想想我們整個程式,是不是要對資料庫進行操作?比如我還是用的mysql資料庫,那是不是還是需要mysql資料庫的jar包?沒毛病吧?之前我們們使用JDBC去操作mysql資料庫的時候,也用到了mysql的jar包,前面也解釋過原因,這裡就不贅述咯,那我就把這兩個jar包匯入到工程中,如下圖所示。



(2)準備資料庫和資料表
既然是對資料庫的操作,總不能沒有資料庫和資料表吧?這裡我使用的是mysql資料庫
資料庫db_es9d_mybatis
資料表t_users(id,username,password,tel)


(3)SqlMapConfig配置檔案
之前如果有學過struts,hibernate,spring等框架的時候,一般都會有一個核心配置檔案,說白了,學習框架,主要學習的就是如何進行一些相關的配置。我們們的MyBatis框架也不例外,它有一個核心配置檔案,名稱預設為"SqlMapConfig.xml",一般放到src目錄下。注意,這裡是預設,而不是必須,之所以我把名稱寫成這個,是為了給大家一種feeling,以後看到這個檔案就能聯想到是MyBatis框架。那這個檔案作用是什麼呢?怎麼用呢?這個配置檔案不用我們自己寫,一般在網上可以找到模板,或者在mybatis的原始碼專案中也可以找到,無論通過哪種方式,格式還是比較簡單的,我先拿一份通用的格式過來,然後我們們一起聊聊。

開頭是xml檔案的固定寫法,沒什麼好說的。然後就是dtd檔案的約束,也就是該xml檔案中可以包含哪些標籤,有一份dtd檔案進行約束。然後configuration標籤就是開始進行配置了。
environments:顧名思義,環境的配置,當前我們是開發環境,所以寫成development
transaction:事務的配置,這裡採用JDBC的事務管理方式
datasource:資料來源的配置,也稱為資料庫連線池的配置,前面的《資料庫連線池》文章中我們提到過這個概念,這裡就不贅述咯
mappers:是對mapper配置檔案的引入,先不說,接下來聊到mapper檔案時再說
到這裡為止,似乎有點明白了,這份SqlMapConfig檔案主要做了哪些事情,其中比較明顯的一個就是配置資料來源,也就是說在JDBC的6個步驟中的(1),(2)兩步是在這邊進行管理的,從而能夠得到資料庫連線物件。那還有其他步驟呢?怎麼做?繼續往下看。

(4)編寫實體類
既然MyBatis是對JDBC的封裝,那它需要將資料表中的資料和程式中的實體類對應起來,所以我們需要準備一個與資料表t_users對應的實體類User,這點能夠想通吧?如果不明白,可以看下我們的《聊聊JDBC》。


(5)編寫userMapper.xml檔案
咦?怎麼又來一個配置檔案?之前SqlMapConfig配置檔案我們已經瞭解了它的作用,而這個配置檔案又是做什麼的?既然使用JDBC對資料表進行查詢操作需要6個步驟,而目前我們MyBatis看上去已經做了(1),(2)兩步,那其他幾個步驟呢?顯然如果沒有其他幾個步驟,何來的查詢,何來的把查詢結果賦值到某個實體類物件的屬性中?對不對?所以該配置檔案就是針對某張表和某個具體的實體類而產生的,如果資料庫中有5張表,顯然我們需要準備5個實體類,同時需要準備5個這樣的mapper檔案,當然,也可以使用註解的方式,不絕對,這裡只是為了說明問題。
所以這個userMapper.xml檔案的作用你可以把它理解為,資料表和實體類的關係怎樣對應?JDBC中其他幾個步驟在MyBatis技術中如何提現等等。重點是,你不會突然覺得這個配置檔案可有可無,一定要知道它存在的意義,存在即合理。
另外,需要注意的是,一般這個配置檔案的命名為實體類的名稱+Mapper.xml,當然,不絕對,命名還是可以變化的,只是說這樣一個約定俗成的概念。該配置檔案的位置一般放到一個專門的目錄中,比如"com.es9d.mapper",這樣方便管理,不然配置檔案一多就會顯得比較凌亂。
下面我們就來看看這個配置檔案中有哪些值得說的東西,同樣,我截一張模板圖過來。

前面的部分就不多說了,從mapper標籤開始說起,發現有一個namespace屬性,含義為名稱空間,也就是說,該userMapper.xml檔案需要一個對應的命名,而且這個命名要唯一。一般情況下,通常是包名+mapper檔名,mapper檔案去除字尾即可,比如我們的userMapper.xml檔案在com.es9d.mapper中,namespace的值為"com.es9d.mapper.userMapper"
select標籤:說明要進行查詢操作,其他先不看,看標籤對中的sql語句,這裡就相當於JDBC的第(3)步,構建sql語句。然後給select標籤id取個值,方便程式中使用,具體怎麼使用,我們到程式中再看,這裡先明確一點,該名稱要唯一,可讀性要好,比如getUser,說明就是得到一個User物件
parameterType="int",要說這個,我們們還是回到sql語句"select * from users where id=#{id}",這句話的含義不用多說,相當於之前JDBC中的"select * from users where id=?",能理解吧?也就是#{id}表示一個佔位符,具體值是什麼,由程式程式碼中決定,而parameterType的含義就是指定該佔位符的值型別,int就表示是整型。另外,這個步驟是不是相當於做了JDBC中的(4)根據連線物件和sql語句得到一個PreparedStatement物件,並給其中的引數賦值。
resultType="com.es9d.domain.User",表示從返回的結果集中迴圈遍歷出一個User物件,也就是resultType的含義是指定返回的型別,我們select目的是得到一個User物件,所以要寫成"com.es9d.domain.User",注意是類的全路徑。

(6)將userMapper.xml檔案配置到SqlMapConfig檔案中

如圖所示,將userMapper.xml檔案在核心配置檔案SqlMapConfig檔案中配置一下,交給核心配置檔案管理。這裡實際上比較容易理解,既然SqlMapConfig檔案是MyBatis的核心配置檔案,那一切就以它為中心唄,你寫好一個userMapper.xml檔案要想生效用到,就交給它管理。

(7)根據id查詢得到一個User物件
好了,上面寫了幾個步驟,那我們們就通過程式來看看能夠根據id查詢成功並且得到一個User物件,具體步驟是什麼呢?

a.得到核心配置檔案SqlMapConfig檔案的內容
既然SqlMapConfig是核心配置檔案,一切都要以它為中心,那我們們是不是先要取到其中的內容?怎麼取呢?取完之後放到哪裡?

寫法比較固定,如果不能理解,記住也可以,這樣就可以把SqlMapConfig.xml檔案的內容載入到一個InputStream輸入流中,也就是在InputStream輸入流中就可以拿到核心配置檔案的所有內容。

b.根據核心配置檔案的輸入流得到一個工廠

至於為什麼會要有這樣一個工廠,實際上這和設計模式相關。就算我們不知道mybatis該如何使用,但是最起碼我們要有一個判斷,就是要找到mybatis中某個某些類來呼叫這些類的方法屬性,因為這是物件導向的開發思想決定的。
而在mybatis框架中,核心的一個類就是sqlsession,而要想得到這樣一個sqlsession類,就得需要一個sqlsessionfactory,用的是工廠設計模式,這裡我們就不展開說咯。

c.根據sqlsessionfactory得到sqlsession

其中openSession這個方法是過載的,也就是可以傳入引數,一般常用的是傳入一個boolean值,主要是為了控制事務是否自動進行提交。先有這麼一個概念,後面這個知識點我們們會具體說。

d.定義一個sql識別符號
回想一下我們前面所做的事情,SqlMapConfig檔案中沒什麼好說的了,userMapper.xml檔案還是有一些可以說道說道的,想想看,我們要做什麼?我們需要根據id來查詢得到User物件,而在userMapper.xml檔案中是不是寫了一個select標籤,並且有一個id的值,這個標籤的作用就是根據id來進行查詢users表,忘記了的可以回頭看下userMapper.xml檔案。
那接下來的工作就是如何通過java程式碼使用這個select標籤進行查詢,肯定是先要定位到這個標籤,怎麼定位?就要找到userMapper.xml檔案,然後找到其中的select標籤。
userMapper.xml檔案有一個名稱空間還記得嗎?這就能對應到該配置檔案,com.es9d.mapper.userMapper。那怎麼定位到select標籤呢?很簡單,它有一個id,組合起來就是"com.es9d.mapper.userMapper.getUser",這樣就可以準確地定位到select標籤,然後使用select標籤去進行查詢操作。
這裡說定義一個sql識別符號也就是這個含義,如下圖所示


e.呼叫sqlsession的方法得到User物件
萬事俱備,只欠東風,想一想,目前是不是所有的準備工作都已經完成?那麼接下來就是關鍵的一步,呼叫sqlsession的某一個方法來得到我們所需要的結果。

如上圖所示,呼叫的是selectOne方法,為什麼會呼叫該方法?sqlsession中有很多方法,因為我們是根據id進行查詢,返回的結果只能是一條,也就是隻會對應一個User物件。第一個參數列明我們需要執行的是userMapper.xml檔案中哪個標籤的內容。第二個引數1,大家還記得嗎?在userMapper.xml檔案的select標籤中有一個id=#{id}表示佔位符,也就是具體查詢哪個id由程式中指定,這裡就表示查詢id為"1"的記錄。那返回值是什麼呢?細心的各位應該還記得在select標籤中有一個屬性resultType,我們將其定義成了com.es9d.domain.User,也就是說返回值為User型別。

f.執行看結果
到這裡,能否查詢成功,大家實際上會有一個半信半疑的態度,信的原因是JDBC的6個步驟在MyBatis中都有對應,感覺應該是可以成功的。不信的原因是好像有點顛覆之前JDBC程式碼的概念,感覺整個過程中也沒寫什麼太多程式碼,就是配置了一下。
最好的方式就是看看是否能夠取到值,並且封裝到了User物件中,我先把整個程式碼截圖一下

執行結果


5.小結
對比一下原來的JDBC查詢和用MyBatis查詢,可以發現,的確用MyBatis方便了很多,這裡我們還是把步驟寫一下
a.匯入mybatis的jar包
b.編寫SqlMapConfig.xml檔案
c.編寫xxxMapper.xml檔案
d.將xxxMapper.xml檔案交給SqlMapConfig.xml檔案管理
e.得到SqlSessionFactory物件
f.得到SqlSession物件
g.根據sql識別符號呼叫SqlSession物件的方法完成查詢操作

當然,步驟不一定是這樣,只是剛開始我給大家一個入門的流程。等大家用熟了之後,想先寫什麼就先寫什麼,就是這麼任性。從直觀的感受上來講,我們更多的是關心xxxMapper.xml檔案中sql語句的編寫,像從原來的ResultSet結果集遍歷資料封裝到實體類物件中這種繁瑣的操作再也不需要寫了。當然,優點還有很多。

大家可能會想,根據id查詢我們瞭解了,那比如要寫增加,修改和刪除該怎麼做呢?如果資料表欄位名和實體類屬性名不一致會有問題嗎?如果不用配置檔案而使用註解的方式該怎麼寫?等等一系列疑惑,沒關係,我們們先通過了一個簡單的查詢操作來感受了一下MyBatis框架的使用,先明白它和JDBC的一些差別,以及它的使用流程,其他問題我們們在接下來慢慢討論,我先把這份文章命名為《一起來看MyBatis(一)》。
欲知後事如何,且聽下回分解!

大家也可以關注下我們的微信公眾號“憶說就懂”,掃一掃或者長按圖片進行識別,平時有什麼問題可以一起探討與學習,謝謝!QQ交流群:574393683










相關文章