MyBatis中通過package標(biāo)簽加載mapper映射文件的方式分析

本文作者:孔維勝,叩丁狼高級講師辽装。原創(chuàng)文章帮碰,轉(zhuǎn)載請注明出處。

MyBatis中通過package標(biāo)簽加載mapper映射文件的方式分析

看文章前的要求

在學(xué)習(xí)MyBatis的初級篇之前拾积,有兩個前提要求殉挽,第一.必須學(xué)會使用IDEA丰涉,因為在文章中,使用的工具為IDEA斯碌,文章中的案例也都是基于IDEA的一死。第二.必須學(xué)會使用MAVEN,因為在案例中需要的jar包傻唾,都是通過MAVEN來管理的投慈。

文章中的案例的開發(fā)環(huán)境

JDK 1.8

IDEA 2017.3

MySQL 5.1.38

Apache Maven 3.5.0

Tomcat 9.0.6

MyBatis 3.4.6

案例需要的表和數(shù)據(jù)

我們使用MyBatis的目的最終是訪問數(shù)據(jù)庫,所以在數(shù)據(jù)庫方面冠骄,我們先創(chuàng)建相應(yīng)的數(shù)據(jù)庫伪煤,表,導(dǎo)入相關(guān)的數(shù)據(jù)凛辣。如:

1.創(chuàng)建mybatis數(shù)據(jù)庫抱既。

2.在mybatis數(shù)據(jù)庫中創(chuàng)建department(部門表)。

DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
  `id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '部門ID',
  `name` varchar(20) DEFAULT NULL COMMENT '部門名稱',
  `sn` varchar(20) DEFAULT NULL COMMENT '部門縮寫',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

3.準(zhǔn)備department(部門表相關(guān)的數(shù)據(jù))

INSERT INTO `department` VALUES (1, '人力資源', 'HR_DEPT');
INSERT INTO `department` VALUES (2, '銷售部', 'SALE_DEPT');
INSERT INTO `department` VALUES (3, '開發(fā)部', 'DEVELOP_DEPT');
INSERT INTO `department` VALUES (4, '財務(wù)部', 'FINANCE_DEPT');

案例需求

需求:使用Mapper動態(tài)代理的方式完成所有數(shù)據(jù)的查詢操作扁誓。

需求分析

    1. 導(dǎo)入相關(guān)jar依賴

要使用MyBatis框架防泵,首先需要導(dǎo)入mybatis的核心包,MyBatis主要是操作數(shù)據(jù)庫跋理,替換掉傳統(tǒng)的JDBC方式訪問數(shù)據(jù)庫择克,所以需要導(dǎo)入mysql的驅(qū)動包。我們要在項目中使用單元測試進行測試前普,所以需要導(dǎo)入junit包肚邢,我們不想寫javaBean的setter和getter方法,可以導(dǎo)入lombok的包拭卿。

  • 2.添加配置文件骡湖。

我們使用MyBatis框架,需要兩個配置文件峻厚,一個是MyBatis的主配置文件响蕴,主要用來配置事務(wù)管理器和數(shù)據(jù)庫的連接信息,一個是封裝SQL語句Mapper映射文件惠桃。我們?yōu)榱藬?shù)據(jù)庫的連接信息不寫死在主配置文件中浦夷,所以我們采用抽取的方式,把連接數(shù)據(jù)庫的信息抽取到db.properties文件中辜王,進行管理劈狐。通過package掃描的方式在主配置文件中掛載mapper的文件。如:

   <package name="cn.wolfcode.mapper"/> 
  • 3.添加實體類和接口呐馆。

可能查詢數(shù)據(jù)需要查詢條件有很多肥缔,查詢數(shù)據(jù)需要封裝到對象中,所以我們可以定義一個JavaBean汹来,來封裝條件和查詢的數(shù)據(jù)续膳。

定義一個接口改艇,編寫操作數(shù)據(jù)庫方法。方法的名字保持和sql映射文件中的標(biāo)簽的id一一對應(yīng)坟岔。

  • 4.增加工具類谒兄。

通過加載主配置文件來獲取SqlSessionFactory工廠對象,一般工廠對象都是單例模式的炮车,所以這個操作只需要做一次即可舵变。比如:我們不能每吃一次飯,都去建一所餐廳瘦穆。兩者的道理是一樣的纪隙。

而在MyBatis的官網(wǎng)給出的建議是SqlSessionFactory 一旦被創(chuàng)建就應(yīng)該在應(yīng)用的運行期間一直存在,沒有任何理由對它進行清除或重建扛或。因此 SqlSessionFactory 應(yīng)該使用其單例模式绵咱,只創(chuàng)建一次在整個應(yīng)用中,都可以使用熙兔。

工廠對象的獲取思考:

那么把獲取工廠對象的操作放在哪里合適呢悲伶?如果在本類中進行抽取,放在一個方法中住涉,但是每個DAO的實現(xiàn)類都這樣處理麸锉,還是會出現(xiàn)代碼的冗余。所以最合適的方式定義一個MyBatisUtil工具類舆声,把獲取工廠對象的操作抽取到工具類中花沉,那么工廠對象的獲取只需要獲取一次即可,所以在工具類中媳握,定義在哪里碱屁,只會執(zhí)行一次呢?靜態(tài)代碼塊蛾找,我們都知道娩脾,類中的靜態(tài)代碼塊,只會隨著類的加載而加載打毛,并且只執(zhí)行一次柿赊。

MyBatis工具類設(shè)計思考:

何為工具類,一般我們在定義的工具類的時候幻枉,希望使用者只使用而不要修改此類碰声,所以我們會設(shè)置這個類使用final進行修飾,這樣這個類就是終結(jié)類展辞,不能被繼承。一般工具類不會讓使用者去創(chuàng)建對象万牺,而是采用提供靜態(tài)方法的方式共使用者調(diào)用罗珍。

SqlSession對象獲取思考:

定義一個方法供外部訪問洽腺,獲取SqlSession對象。這個方法設(shè)計成靜態(tài)的這樣覆旱,調(diào)用方法的時候不用再創(chuàng)建工具類對象蘸朋。

  • 5.添加測試類。

定義一個測試類扣唱,編寫一個測試方法藕坯,通過調(diào)用工具類中的方法獲取SqlSession對象,通過SqlSession對象調(diào)用getMapper方法獲取對應(yīng)的Mapper的代理對象噪沙,然后調(diào)用接口中的方法獲取所有數(shù)據(jù)炼彪。

案例代碼

pom.xml:

 <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.6</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.40</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.16.20</version>
    </dependency>

mybatis-config.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
   <properties  resource="db.properties"/>
   <typeAliases>
        <package name="cn.wolfcode.domain"/>
    </typeAliases>
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driverName}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${userName}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>    
    <mappers>
       <package name="cn.wolfcode.mapper"/>
    </mappers>

</configuration>

db.properties:

driverName=com.mysql.jdbc.Driver
url=jdbc:mysql:///mybatis
userName=root
password=root123

DepartmentMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.wolfcode.mapper.DepartmetMapper">

    <!--
     select 表示查詢語句的標(biāo)簽。標(biāo)簽體的內(nèi)容即是查詢的SQL語句
          id:SQL語句的唯一標(biāo)識
          parameterType:傳入這條SQL語句的參數(shù)的類的完全限定名或別名正歼,
                 因為 MyBatis 可以通過 TypeHandler 推斷出具體傳入語句的參數(shù)辐马,故可以省略
          resultType:返回期望的類型(類的完全限定名或別名),用來接收查詢的結(jié)果局义。

      -->
    <select id="selectAll" resultType="cn.wolfcode.domain.Department">
        SELECT id,name,sn FROM department
        WHERE id = #{id}
    </select>

</mapper>

Department:

@Getter
@Setter
@ToString
public class Department {

    // 主鍵id
    private Long id;
    // 部門名稱
    private String name;
    // 部門簡寫
    private String sn;
}

DepartmentMapper:

public interface DepartmentMapper {

   /**
     *  查詢所有部門信息
     * @return 返回所有部門信息的集合
     */
    List<Department> selectAll();
}

MyBatisUtil:

public final class MyBatisUtil {

    private static SqlSessionFactory factory = null;

    static {
        // 使用static靜態(tài)代碼塊喜爷,隨著類的加載而加載,只執(zhí)行一次
        try {
            // 加載MyBatis的主配置文件
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            // 通過構(gòu)建器(SqlSessionFactoryBuilder)構(gòu)建一個SqlSessionFactory工廠對象
            factory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 獲取sqlSession對象
    public static SqlSession openSession() {
        return factory.openSession();
    }
}

DepartmentMapperTest:

public class DepartmentMapperTest {
 @Test
    public void testQueryOne(){
        // 獲取sqlSession對象
        SqlSession sqlSession = MyBatisUtil.openSession();
        // 獲取Mapper對象
        DepartmentMapper departmentMapper = sqlSession.getMapper(DepartmentMapper.class);
        List<Department> departmentList = departmentMapper.selectAll();
        // 關(guān)閉資源
        sqlSession.close();
        // 遍歷結(jié)果
  departmentList.stream().forEach(System.out::println);
    }

DepartmentMapper文件加載流程分析

  • 1 . 加載主配置文件萄唇,通過build方法構(gòu)建工廠對象檩帐。如:
    MyBatisUtil.png
  • 2 . 創(chuàng)建XML配置構(gòu)建器的對象(XMLConfigBuilder)。底層使用的是XPath解析器另萤。 在這個方法的finally塊中湃密,對外部傳入的流,進行了關(guān)閉仲墨。所以外部不需要進行關(guān)閉了勾缭。如:


    SqlSessionFactoryBuilder.png
  • 3 . 通過構(gòu)建器對象調(diào)用parse方法,把解析的數(shù)據(jù)封裝到Configuration對象中目养。我們主要是關(guān)心mapper文件的加載俩由,所以繼續(xù)往下看。如:


    XMLConfigBuilder.png
  • 4 .方法中定義了一個while死循環(huán)癌蚁,主要是便利mappers節(jié)點下面的所有元素幻梯。因為我們采用的是在主配置文件中使用package掃描的方式掛載的mapper映射文件。所以跳入if代碼塊努释。在if塊中通過獲取name屬性的值碘梢,拿到了mapper文件的所屬的包名,通過configuration對象調(diào)用addMappers方法把mapper映射文件所在的包傳入伐蒂。如:
XMLConfigBuilder.png
  • 5 .調(diào)用Mapper注冊對象中(MapperRegistry)的addMappers方法煞躬,添加映射。如:
Configuration.png
  • 6 .創(chuàng)建ResolverUtil工具類,通過調(diào)用find方法把包下面的字節(jié)碼對象找出來恩沛,并存入到Set集合中在扰,通過調(diào)用getClasses方法取出,進行遍歷雷客。把每一個字節(jié)碼對象傳入addMapper方法芒珠。如:
MapperRegistry.png
  • 7 .在MapperRegistry(映射注冊類)中定義一個map容器(knowMappers),用來存入映射搅裙。在addMapper方法中皱卓,先通過調(diào)用isInterface方法看看mapper是不是接口,必須是接口部逮,才會添加娜汁。在通過調(diào)用hasMapper方法來判斷是否已經(jīng)添加過了,如果已經(jīng)添加甥啄,就拋出一個綁定異常存炮。通過標(biāo)記loadCompleted,來確保添加成功蜈漓。如果添加出現(xiàn)了異常穆桂,在finally塊中刪除map中存入的映射。把字節(jié)碼對象作為key融虽,創(chuàng)建該字節(jié)碼對象的代理對象作為value享完,存入knowMappers中。并創(chuàng)建MapperAnnotationBuilder對象如:
MapperRegistry.png
  • 8 .MapperAnnotationBuilder這個類總會優(yōu)先解析xml配置文件有额,并且這個xml配置文件必須與Class對象所在的包路徑一致般又,且文件名要與類名一致。在解析完xml配置文件后巍佑,才會開始解析Class對象中包含的注解茴迁。里面有個if判斷,如果在主配置對象(configuration)添加過接口標(biāo)記萤衰,表示解析過堕义,就不再進入if語句。首先調(diào)用loadXmlResource方法脆栋,解析指定的xml配置文件倦卖。如:
MapperAnnotationBuilder.png
  • 9 . 在這個方法中,先通過if判斷之前是否解析過椿争,如果沒有解析過怕膛,則進入if語句,把包名中的"."替換成"/"秦踪,這樣變成了文件夾褐捻,然后在后面追加".xml"后綴掸茅。這樣拼接成一個xml文件的資源路徑。然后加載到內(nèi)存柠逞。在通過調(diào)用parse方法進行解析xml文件倦蚪。
    所以這也是為何如果使用package掃描的方式,必須要保證接口和mapper映射文件必須在同一個包中边苹,名字也必須相同的原因。如:
MapperAnnotationBuilder.png
  • 10 . 繼續(xù)往下解析裁僧。如:
XMLMapperBuilder.png

DepartmentMapper文件加載整體流程圖

Mapper映射文件通過package方式解析流程圖.png

想獲取更多技術(shù)干貨个束,請前往叩丁狼官網(wǎng):http://www.wolfcode.cn/all_article.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市聊疲,隨后出現(xiàn)的幾起案子茬底,更是在濱河造成了極大的恐慌,老刑警劉巖获洲,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阱表,死亡現(xiàn)場離奇詭異,居然都是意外死亡贡珊,警方通過查閱死者的電腦和手機最爬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來门岔,“玉大人爱致,你說我怎么就攤上這事『妫” “怎么了糠悯?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長妻往。 經(jīng)常有香客問我互艾,道長,這世上最難降的妖魔是什么讯泣? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任纫普,我火速辦了婚禮,結(jié)果婚禮上判帮,老公的妹妹穿的比我還像新娘局嘁。我一直安慰自己,他們只是感情好晦墙,可當(dāng)我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布悦昵。 她就那樣靜靜地躺著,像睡著了一般晌畅。 火紅的嫁衣襯著肌膚如雪但指。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音棋凳,去河邊找鬼拦坠。 笑死,一個胖子當(dāng)著我的面吹牛剩岳,可吹牛的內(nèi)容都是我干的贞滨。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼拍棕,長吁一口氣:“原來是場噩夢啊……” “哼晓铆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起绰播,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤骄噪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蠢箩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體链蕊,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年谬泌,在試婚紗的時候發(fā)現(xiàn)自己被綠了滔韵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡掌实,死狀恐怖奏属,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情潮峦,我是刑警寧澤囱皿,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站忱嘹,受9級特大地震影響嘱腥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拘悦,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一齿兔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧础米,春花似錦分苇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蘑斧,卻和暖如春靖秩,著一層夾襖步出監(jiān)牢的瞬間须眷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工沟突, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留花颗,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓惠拭,卻偏偏與公主長得像扩劝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子职辅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容