1. 程式人生 > >一文搞定 Mybatis 的應用

一文搞定 Mybatis 的應用

函數 public nec HERE 使用方式 nat 大量 exec 不完全

Mybatis 介紹

Mybatis 是一個開源的持久層框架,原來叫 ibatis ,它對 jdbc 操作數據庫的過程進行了封裝,使開發者只需要關註 SQL 本身,而不需要花費精力去處理例如註冊驅動、創建 connection、創建 statement、手動設置參數、結果集檢索等 jdbc 繁雜的過程代碼。

Mybatis 通過 xml 或註解的方式將要執行的各種 statement(statement、preparedStatemnt、CallableStatement)配置起來,並通過 Java 對象和 statement 中的 sql 進行映射生成最終執行的 sql 語句,最後由 mybatis 框架執行 sql 並將結果映射成 Java 對象並返回。

Mybatis 系統架構

下面好好看一下 Mybatis 的系統架構圖,核心配置文件是 sqlMapConfig.xml,通過 Mappers 映射器來連接主配置和各個實體類操作所用到的 SQL 語句。有了配置和 SQL 語句 ,使用 SqlSession 來執行 SQL,在執行的時候會涉及到入參和出參。

而 SqlSession 的生成則是由 SqlSessionFactory 通過加載主配置文件來創建。表面上使用 SqlSession 來執行 SQL ,實際上是 SqlSession 所封裝的 Executor ,Executor 又封裝了 MapperedStatement 在工作。

技術分享圖片

搭建 Mybatis 開發環境

1 創建 Java 項目。 2 加入 jar 包(核心包,驅動包,依賴包)。 3 創建主配置文件 sqlMapConfig.xml,日誌格式配置文件 log4j.properties 。 4 創建 pojo ,註意這裏的 pojo 要和數據庫字段相對應。 5 書寫實體類對應的 mapper 文件,該文件主要用來寫 SQL 。 6 加載 mapper 文件,在 sqlMapConfig.xml 中使用 Mappers 標簽進行關聯 。

測試

@Test
public void test1() throws Exception {
    String resource = "sqlMapConfig.xml";

    // 加載配置文件
    InputStream in = Resources.getResourceAsStream(resource);

    // 綁定配置文件,生成 SqlSession
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
    SqlSession session = sqlSessionFactory.openSession();
    User user = session.selectOne("namespace.findUserById",10);
    System.out.println(user);
}

在項目中比較常用的是 mapper 動態代理開發,為什麽會出現這個東西呢? Mybatis 主要為操作數據庫服務,主要用在 DAO 層,具體的 SQL 都在實體類對應的 mapper 文件中了,一般來說,對於一個 DAO 層的業務,我們需要編寫接口,實現類,還有 mapper 文件,至少需要這 3 個東西,而實現類和接口中的代碼會存在大量的重復。

為了簡化開發,Mybatis 的開發人員就想到使用 mapper 動態代理開發,其結果就是,我們只需要為 DAO 層的業務編寫一個接口和 mapper 文件即可,既然把實現類給省略了,那肯定需要以某種形式體現出具體的實現在哪裏。

我們要保證以下幾個方面

1 命名空間命名為接口的全類名,這樣接口和 SQL 就綁定在一起了。

2 方法名和 statement 的 id 一致,這樣接口中的方法和具體的 SQL 就綁定在一起了。

3 方法的入參和 parameterType 一致

4 方法的返回值和 resultType 一致

技術分享圖片

保證了上面 4 點的匹配即可將接口和 mapper 文件綁定,這裏沒有實現類,mybatis 會自動給我們生成實現類。上面的 mapper 中入參使用 Map 是為了方便傳參,不僅僅是 Integer ,其它類型的參數也可以接收。

在入參或是出參選擇類型的時候,parameterType 和 parameterMap 的區別主要是 Type 是已經存在的類型,可以直接配置使用,若是使用 Map 則需要我們在 mapper 文件中自定義輸入集或是結果集。

註意看有個小細節,接口文件和 mapper 文件在同一目錄下,且同名。為什麽要這麽做呢 ?因為在主配置文件中關聯 mapper 文件的時候,若是使用如下圖方式,就需要 mapper 文件和接口名稱相同且在同一目錄下。Mybatis 直接對包進行掃描,不用一個一個的配置 mapper 文件。

技術分享圖片

有了上面的接口,實現類,主配置文件,關於主配置文件文件,還會涉及到一些標簽的使用,比方 properties 屬性,別名等。其中配置別名的時候也可以使用包掃描的方式進行配置,配置之後的別名即為實體類名,且不區分大小寫。還要註意使用標簽的時候不能亂序。

我們在開發的時候,可能會遇到這麽一個需求,在插入一個記錄之後,就要對該記錄進行操作,這就需要知道剛剛插入記錄的 ID ,以下展示在 MySQL 中獲取 ID 的方式。

技術分享圖片

配置並使用 select LAST_INSERT_ID( ) 函數即可,這裏 order 為主鍵生成的順序,在主鍵自增的時候使用 AFTER,在主鍵為 UUID 的時候配置為 BRFORE 。

以上既是在單表操作時 Mybatis 的應用了。在應用的時候可能會有這樣幾個問題。

Mybatis 中 #{ } 和 ${ } 區別 ?

#{} 表示一個占位符號,通過 #{} 可以實現 preparedStatement 向占位符中設置值,自動進行 Java 類型和 jdbc 類型轉換。防止 sql 註入。 #{} 可以接收簡單類型值或 pojo 屬性值。 如果 parameterType 傳輸單個簡單類型值,#{} 括號中可以是 value 或其它名稱。會給值自動加上引號。

${} 表示拼接 sql 串,通過 ${} 可以將 parameterType 傳入的內容拼接在 sql 中且不進行 jdbc 類型轉換,${} 可以接收簡單類型值或 pojo 屬性值,如果 parameterType 傳輸單個簡單類型值,${} 括號中只能是 value。

Mybatis 和 Hibernate 的區別 ?

Mybatis 和 Hibernate 不同,它不完全是一個 ORM 框架,因為 MyBatis 需要程序員自己編寫 SQL 語句。

Mybatis 學習門檻低,簡單易學,程序員直接編寫原生態 sql,可嚴格控制 sql 執行性能,靈活度高,但是靈活的前提是 Mybatis 無法做到數據庫無關性,如果需要實現支持多種數據庫的軟件則需要自定義多套 sql 映射文件,工作量大。

Hibernate 對象/關系映射能力強,數據庫無關性好,對於關系模型要求高的軟件(例如需求固定的定制化軟件)如果用 Hibernate 開發可以節省很多代碼,提高效率。但是 Hibernate 的學習門檻高。

下面就開始 Mybtis 的進階篇了!

輸入映射和輸出映射

輸入映射類型用 parameterType 來表示,輸入類型可以是簡單類型,比方說 Integer、String。也可以是 pojo 類型,今天再說一個輸入類型,還可以是 pojo 的包裝類型,就是說我們的入參是對實體類的封裝,取值的時候就需要註意了,假如我們使用 QueryUser 對 User 進行封裝。

而我們需要的參數是 User 對象的 username,而入參設置的又是 QueryUser ,那我們取值的時候該怎麽取呢?註意下圖中的連續調用,入參設置為 QueryUser 是為了方便接收參數。

技術分享圖片

技術分享圖片

為什麽會出現這種現象呢 ?我們需要調用 user 再調用 username 是因為 Mybatis 在處理參數的時候,使用的是 Map 的形式,我們傳入的是一個對象,實際上被分解為一個一個的屬性 put 進集合,而集合的鍵就是屬性名稱,值才是我們需要的值。所以在傳入實體類的時候我們可以直接使用 #{username} 得到值,實際上就是對 Map 進行 get("username") 操作。而我們傳入 VO 對象時,多了一層封裝,所以就需要多一層調用。

parameterType 是 Mybatis 自動給我們映射,parameterMap 是手動映射入參,基本不使用。

同理 resultType 也是 Mybatis 對結果集自動映射,我們可以為出參配置簡單類型,實體類型,這裏 Mybatis 可以自動映射有個前提條件就是數據庫的字段必須要實體中的屬性一致。

那總會出現一個特立獨行的不一致的情況,這時就需要我們使用 resultMap 進行手動的為出參進行配置。例如,我們的 User 實體中有個字段為 username,而數據庫中為 user_name,此時就需要我們自行配置出參。

技術分享圖片

動態 SQL

動態 SQL 指的就是使用 Mybatis 提供的標簽來拼接 SQL ,所以在 SQL 會寫的情況下,就是在學習使用 Mybatis 的標簽。吐槽一句,計算機的世界裏,高大上的名詞太多,實際上……,嗯,接著看動態 SQL 。

學的標簽有這麽幾個 if,where,foreach ,include 。直接看具體的每個標簽具體的應用場景。

在多條件查詢中,我們希望可以隨機的搭配輸入的條件,例如下面這條 SQL 我們不需要寫成按 sex、username、或兩個條件一起查詢這樣三條 SQL,只需要使用 IF 標簽進行改造即可。

select id, username, birthday, sex, address from user where sex = #{sex} and username = #{username}

技術分享圖片

上面的 SQL 改造還是不到位,還要添加一個雞肋的語句 1 = 1 ,怎麽才能去掉這個呢,這就需要使用 where 標簽了,我們只需要這樣即可

技術分享圖片

這裏有個註意點,where 標簽會自動去掉其後面的第一個前 and 。這也就意味著,我們可以寫 and sex ,不能寫成 sex and 。

看看 SQL 你會發現,“select * from user” 經常出現,我們可以抽取出來,這樣就需要用到 include 和 sql 標簽。如下圖

技術分享圖片

最後一個 foreach 標簽,主要用在向 SQL 中傳入 list 或是數組時。類如我們有如下 SQL

select * from user where id in (1,10,24)

我們可以使用最常規的一種做法來實現,註意看這裏的實現步驟和 foreach 標簽的使用方式。

技術分享圖片

這裏可以看到 foreach 中的 collection 是對象中的屬性 ids ,我們再來看看其它的實現方式。

技術分享圖片

第一種方式已經說過了,後面兩種的區別主要在於foreach 標簽中的 collection 值不一樣了,若是使用 list 集合作為入參,則遍歷的類型為 list ,若是使用數組作為入參,則遍歷的元素則是 array 。

為什麽這樣呢?上面已經說過,在 Mybatis 中,參數的傳遞使用 Map ,所以我們要填寫的東西就是 Map 中的主鍵,而記錄 List 和數組的 Map 默認主鍵就是 list 和 array 。

Mybatis 中的一對一、一對多的表示

在用戶和訂單關系中,一個訂單只能對應一個用戶,而一個用戶可以對應多個訂單,所以,站在訂單的角度,訂單和用戶的關系就是一對一,而站在用戶的角度,一個用戶可以對應多個訂單,是一對多的關系。

在代碼中的體現就是,我們在訂單實體中添加一個用戶對象,表示這個訂單屬於哪個用戶。而在用戶對象中添加一個訂單的 List,來表示一個用戶對應多個訂單。

在 SQL 語句的體現就是兩張表的連接查詢,當 order left join user 時,表示訂單全查,並查出關聯的用戶信息。而 user left join order 時,表示用戶全查,並查出關聯的訂單信息。

那我們如何使用 Mybatis 將 SQL 結果與對象綁定呢,這裏我們必須要使用 resultMap 來重新定義結果集,在定義結果集的時候,我們需要將關聯的對象一並表示出來。在一對一的關系中,我們可以使用 association 標簽,具體如下圖所示

技術分享圖片

而在一對多的關系中,我們可以使用 collection 標簽進行表示,如下圖所示

技術分享圖片

有個需要註意的地方,若是進行單表的操作,resultMap 中的 result 可以只配置實體類中和數據庫不一致的列,但是涉及到兩個表時就需要將用到的列和主鍵全部列出來,不然沒有值。

到這裏 Mybatis 的應用就說完了,並沒有對 Mybatis 的內部邏輯做過多的介紹,目前還只是會用,後面還有與 Spring 的整合,等其它框架的應用說的差不多再整理。

一文搞定 Mybatis 的應用