Mybatis從認識到了解

首發日期:2018-10-31python


MyBatis的介紹

介紹:

  • MyBatis是Apache旗下的開源項目,是一個持久層框架
  • 與Hibernate的全自動ORM框架能夠僅僅依靠映射關係進行SQL查詢不一樣,MyBatis是一個基於SQL語句的半自動ORM框架,它須要咱們手動去定義SQL語句。


爲何選擇MyBatis:

與JDBC的對比中,MyBatis是基於ORM的,自然要比JDBC強,基於ORM使得MyBatis具備查詢的數據能夠自動封裝到對象中的等好處,而JDBC須要手動編碼的位置就較多了(就算是使用DButils,你也須要使用定義不一樣的方法來給不一樣的持久類賦值。)。mysql

在互聯網應用程序開發中,對於存儲過程和複雜的SQL,Hibernate並無很好地去處理,因此它比較難提高性能(並非說不能,而是耗費心思較多),沒法知足高併發和高響應的需求(若是要強行優化,又破壞了Hibernate的全自動ORM設計)。git

與之對應的,Mybatis能夠說是基於SQL的,它須要咱們去自定義SQL,那麼咱們能夠根據須要去設計和優化SQL,這種可針對SQL的優化的持久層框架,恰巧符合高併發和高響應的互聯網應用程序開發開發需求。github


與Hibernate的對比:

  • ORM區別:
    • Hibernate是徹底面向POJO的,它基本不須要編寫SQL,能夠直接經過映射關係來操做數據。
    • 而MyBatis須要咱們自定義SQL語句。
  • 面向的需求:
    • 對於一些不須要過高性能的,可使用Hibernate去開發。【ERP,CRM,OA之類的】
    • 對於有性能要求的,一般須要使用MyBatis。


MyBatis的優勢:

  • 支持存儲過程
  • 因爲是自定義SQL,方便進行SQL優化
  • 能夠簡化開發,對比JDBC開發,省去了不少代碼;
  • 基於Mapper發送SQL的方式使得DAO層僅須要定義接口,而不須要DAO實現類了。



入門示例


1.首先要下載依賴包https://github.com/mybatis/mybatis-3/releasessql

  • 文件\文件夾詳情:
    • lib:包含MyBatis依賴包,MyBatis的功能須要這些依賴包。
    • mybatis-3.4.5.jar:mybatis的核心包。
    • mybatis-3.4.5.pdf:mybatis的官方文檔。


2.建立普通javase工程,導入依賴包(我這裏使用mybatis-3.4.5 ):數據庫

  • 導入MyBatis核心包:mybatis-3.4.5.jar
  • 導入數據庫驅動包:mysql-connector-java-5.1.7-bin.jar
  • 導入MyBatis依賴包:


3.建立持久類和建立數據表:持久類是用於封裝從數據庫中返回的結果,能夠說持久類的對象就是數據庫記錄在java中的實體(這裏約定數據表的列名與持久類的屬性名一致)。apache

  • 持久類
package work.pojo;
   
   public class User {
    private Integer id;
    private String name;
    private Integer age;
    //省略setter,getter,toString
   }
  • 數據表:
create table user(
id int primary key auto_increment,
name varchar(20),
age int
);


4.在持久類User.java同級目錄下建立映射文件user.xml:這個映射文件的意義是用來定義sql語句,使得咱們能夠在代碼中調用sql語句【不一樣類的標籤訂義不一樣類的SQL,id用於標識SQL語句,parameterType是傳入SQL語句的參數,resultType定義了返回結果的類型(這裏因爲表字段和類屬性名一致,查詢的記錄的各個值會自動賦給對象)】設計模式

<?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="user">
    <!--id用於標識SQL,parameterType是傳給SQL的參數的類型,resultType是返回結果的類型  -->
    <select id="getUserById" parameterType="int" resultType="work.pojo.User" >
    <!-- 使用#{}來傳入參數 -->
        SELECT * FROM USER WHERE id = #{id}
    </select>
</mapper>


5.配置mybatis配置文件,這裏命名爲mybatis-config.xml。配置文件的意義是配置MyBatis的運行設置,MyBatis是針對持久層的框架,因此它必需要有數據庫鏈接的配置。數組

<?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>
    <environments default="development">
        <!-- environment用來定義數據庫鏈接環境,environments用來定義多個environment -->
        <environment id="development">
            <!-- transactionManager配置事務管理,使用jdbc事務管理 -->
            <transactionManager type="JDBC" />
            <!-- dataSource配置數據庫鏈接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 導入映射文件,這樣MyBatis才能管理到SQL語句 -->
        <mapper resource="work/pojo/user.xml"/>
    </mappers>
</configuration>


6.編寫測試方法:

配置文件配完後,要想經過mybatis操做數據庫,那麼就須要使用SqlSessionFactoryBuilder、SqlSessionFactory和SqlSession幾個對象。

  • SqlSessionFactoryBuilder:用來讀取配置文件,獲得一個SqlSessionFactory
  • SqlSessionFactory:SqlSessionFactory至關於鏈接池工廠,能夠得到一個SqlSession對象
  • SqlSession:至關於JDBC的Connection對象,能夠調用方法來操做數據表
@Test
    public void test1() throws IOException {
        // 建立SqlSessionFactoryBuilder對象
        SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder();
        // 經過Mybatis包的Resources類來將配置文件轉成輸入流
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        // 加載配置文件輸入流,建立SqlSessionFactory對象
        SqlSessionFactory   sqlSessionFactory = sfb.build(inputStream);
        // sqlSessionFactory建立SqlSession對象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 執行查詢,第一個參數是映射文件中定義的SQL的id名,第二個參數是傳給SQL的參數。
        User user = sqlSession.selectOne("getUserById", 1);
        // 輸出查詢結果
        System.out.println(user);
        // 釋放資源
        sqlSession.close();
    }


這裏給一下文件結構、數據表數據和測試結果。

文件結構:

數據表數據:

測試結果:User [id=1, name=沙僧, age=18]



上面演示了MyBatis的使用,相信你對MyBatis已經有了一個初步的瞭解,下面將對各個部分的知識進行講解。

1.核心組件【關於SqlSessionFactoryBuilder這些對象,以及瞭解Mybatis的運行流程】
2.mybatis配置文件的配置【關於數據庫鏈接之類的配置】
3.映射文件的配置【關於SQL語句的定義方式】


Mybatis核心組件


四大核心組件

  • SqlSessionFactoryBuilder:負責根據配置文件來生成SqlSessionFactory
  • SqlSessionFactory:一個用來生成SqlSession的工廠(這個工廠由SqlSessionFactoryBuilder生成)
  • SqlSession:SqlSession是會話的意思,即與數據庫的會話,至關於JDBC中的Connection。它是一個既能夠既能夠發送SQL去執行並返回結果,也能夠獲取Mapper的接口。
  • SQL Mapper:它是Mybatis新設計的組件,它由一個Java接口和一個配置文件(XML式或註解式的)組成,它能夠獲取配置文件中的SQL語句和映射關係,它也能夠發生SQL去執行並返回結果。


SqlSessionFactoryBuilder

  • SqlSessionFactoryBuilder是一個基於建造者設計模式的接口,它能夠根據配置去生成SqlSessionFactory
  • SqlSessionFactoryBuilder生成SqlSessionFactory依靠build方法,build能夠傳入多種參數(下面是其中三種方式),常見狀況是傳入一個配置文件的字節輸入流對象。
    • build(InputStream inputStream)【傳入一個配置文件的字節輸入流對象】
    • build(Reader reader)【傳入一個配置文件的字符輸入流對象】
    • build(Reader reader, String environment)【傳入一個配置文件的字符輸入流對象,並根據指定的environment生成工廠,默認是使用environments中的默認值】
    • build(Configuration config)【傳入一個Configuration 對象,這裏不講它怎麼配置

以傳入一個配置文件的字節輸入流對象爲例,MyBatis提供了一個Resource類,它能夠很方便地將配置文件轉成輸入流:

  • // 建立SqlSessionFactoryBuilder對象
    SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder();
    // 經過Resources類來將配置文件轉成輸入流,Resources是mybatis提供的
    InputStream inputStream=Resources.getResourceAsStream("mybatis-config.xml");
    // 加載配置文件輸入流,建立SqlSessionFactory對象
    SqlSessionFactory sqlSessionFactory = sfb.build(inputStream);


SqlSessionFactory

  • SqlSessionFactory是SqlSessionFactoryBuilder讀取了配置文件以後生成的對象,它解析了配置文件的信息(mybatis的數據庫鏈接信息、緩存信息、事務管理等等)。
  • SqlSessionFactory能夠調用openSession方法來獲取一個SqlSession,openSession方法是能夠傳入參數的,若是你傳入一個布爾值那麼將影響事務管理,爲true時將自動提交,爲false將默認不提交,默認爲false,這時候須要你手動提交:sqlSession.commit();
  • openSession除了能夠傳入布爾值,還能夠傳入其餘類型的對象,這裏不講。
// sqlSessionFactory建立SqlSession對象
    SqlSession sqlSession = sqlSessionFactory.openSession();
    //開啓自動提交,默認增、刪、改是不自動提交的
    //SqlSession sqlSession = sqlSessionFactory.openSession(true);


SqlSession

  • SqlSession的做用相似於JDBC中的Connection對象。

  • 做用:
    • 管理事務:SqlSession能夠調用commit和rollback方法【要注意,MyBatis使用JDBC做爲事務管理器時,事務是默認不自動提交的】
    • 提交SQL:SqlSession內置了selectOne、insert、update、delete等超過20種操做數據庫的方法。
      • 調用的方法裏面一般的第一個參數都是字符串,這個字符串是映射文件中的SQL語句的id,本質上是調用映射文件中的SQL語句。
    • 釋放資源:SqlSession能夠調用close方法來關閉鏈接資源。
  • 使用示例:

SqlSession sqlSession = sqlSessionFactory.openSession();
    User user = new User();
    user.setName("鐵頭娃");
    user.setAge(4);
    //發送sql
    sqlSession.insert("insertUser", user);
    //提交事務
    sqlSession.commit();
    // 釋放資源
    sqlSession.close();


SQL Mapper

  • SQL Mapper是mybatis新設計的一個組件,也是一種新的執行SQL的模式,在ibatis時代,主要使用sqlSession來進行查詢,而有了SQL Mapper以後,只要遵循一種開發規範,那麼就能夠利用一個接口和一份映射文件
  • 若是這個接口遵循了Mapper的開發規範,而且有對應的映射文件,那麼sqlSession調用getMapper方法能夠獲取一個mapper對象,mapper對象能夠直接調用接口方法來執行映射文件中對應的SQL語句。

    • sqlSession.getMapper(某個接口.class);
  • Mapper的規範
    • 映射文件的namespace是接口的全限定名
    • 接口的方法名一般與映射文件的SQL id同名【使得Mapper的方法與SQL對應起來,這樣調用方法就是調用對應的SQL】【還有各類各樣的要求,因爲這涉及兩種開發方式,因此這將在後面講】
    • 映射文件就是配置SQL語句的文件
  • 功能:

    • Mapper也能夠發送SQL,調用Mapper接口中的方法就至關於sqlSession調用映射文件中的SQL。【這樣的調用是徹底面向對象的,如今廣泛用這種。】

      • SqlSession sqlSession = sqlSessionFactory.openSession();
        //發送sql
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.getUserById(1);
        //User user = sqlSession.selectOne("getUserById", 1); //原方式
        System.out.println(user);
        // 釋放資源
        sqlSession.close();


核心組件的生命週期

  • SqlSessionFactoryBuilder的主要做用是建立SqlSessionFactory,一旦建立了 SqlSessionFactory 後,這個類就不須要存在了。一般只讓它處在局部環境(本地方法變量)中。
  • SqlSessionFactory用於建立Session接口對象,因此SqlSessionFactory應該是長期有效的,但不該該重複建立,因此一般使用單例模式或靜態單例模式來獲取SqlSessionFactory對象。
  • SqlSession至關於一個鏈接對象,每次獲取它,都是用來處理業務的,因此每一個業務(線程)都應該有一個SqlSession對象,而它一般應該在業務結束後歸還給SqlSessionFactory,SqlSession的範圍爲一個業務的請求範圍。
  • Mapper是綁定了SQL的接口,在使用Mapper來操做的時候,很明顯的--Mapper的生命週期與SqlSession有關。Mapper的生命週期最大能夠與SqlSession等同,但若是把Mapper放到try-catch這樣的語句或方法中,那麼它的生命週期就會變小了。


總結

稍微瞭解了一下核心組件以後,再回頭看一下以前的代碼:

@Test
    public void test1() throws IOException {
        // 建立SqlSessionFactoryBuilder對象
        SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder();
        // 經過Mybatis包的Resources類來將配置文件轉成輸入流
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        // 加載配置文件輸入流,建立SqlSessionFactory對象
        SqlSessionFactory   sqlSessionFactory = sfb.build(inputStream);
        // sqlSessionFactory建立SqlSession對象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 執行查詢,第一個參數是映射文件中定義的SQL的id名,第二個參數是傳給SQL的參數。
        User user = sqlSession.selectOne("getUserById", 1);
        // 輸出查詢結果
        System.out.println(user);
        // 釋放資源
        sqlSession.close();
    }



映射文件的配置


咱們如今先來了解一些映射文件的配置,也就是映射文件裏面所謂的SQL語句之類的怎麼寫。這裏瞭解了映射文件怎麼配置以後,你能夠先使用sqlSession來執行SQL體驗一下其中的意義的。

SqlSession sqlSession = sqlSessionFactory.openSession();
    User user = new User();
    user.setName("鐵頭娃");
    user.setAge(4);
    //發送sql
    sqlSession.insert("insertUser", user);
    //提交事務
    sqlSession.commit();
    // 釋放資源
    sqlSession.close();


dtd約束:

映射文件須要dtd約束,在依賴包沒有包含dtd的配置方法,只能從官方文檔中獲取,這裏給一下。

<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">


可配置標籤:

基礎結構,下面是一個大體的映射文件結構,在mapper標籤下面配置其餘標籤:

<?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="user">
    <select id="getUserById" parameterType="int" resultType="work.pojo.User" >
        SELECT * FROM USER WHERE id = #{id}
    </select>
</mapper>


mapper標籤:

  • mapper標籤中的屬性namespace是命名空間的意思,它能夠將定義的SQL語句區分到不一樣的命名空間上,就好像把類區分到不一樣的包中。命名空間能夠解決SQL ID相同的問題,SQL ID惟一時,能夠調用SQL ID來調用對應的SQL語句;不惟一時,應該使用命名空間.SQL ID
  • mapper標籤是映射文件的頂級標籤,子標籤有:
    • select:用於定義查詢語句,能夠自定義入參類型和返回的結果集類型。
    • insert:用於定義插入語句,能夠自定義入參類型,返回結果是插入的記錄數。
    • update:用於定義更新語句,能夠自定義入參類型,返回結果是更新的記錄數。
    • delete:用於定義刪除語句,能夠自定義入參類型,返回結果是刪除的記錄數。
    • sql:用來封裝一部分常常出現的SQL,這樣能夠在別的地方進行引用,避免了總是重複。
    • resultMap:用來手動將數據庫返回的結果與Java中的數據創建上關係,使用了resultMap的地方,會根據resultMap的規則來封裝數據。
    • cache:給命名空間配置緩存。【配置緩存能夠影響CRUD語句是否保留緩存值】
    • cache-ref:引用其餘命名空間中的緩存配置。


select標籤:

  • select標籤用來定義查詢類的SQL語句。

  • 屬性:
    • id:與命名空間組合,用來惟一標識SQL語句。MyBatis能夠根據id來調用對應的SQL語句。【要注意惟一性,惟一的時候,能夠直接使用SQL ID;若是不是惟一,那麼要使用命名空間.SQL ID 來調用對應的SQL語句。】
    • parameterType:用來定義傳給SQL語句的參數的數據類型。能夠是各類數據類型,也能夠是POJO類對象(要求是類的全限定名),也能夠給出別名(別名是指數據類型的別名或類的全限定名的別名,因爲Mybatis提早定義了一堆別名,因此咱們直接使用int的時候,至關於使用了java.lang.Integer)。
    • resultType:用來定義SQL語句返回結果的參數類型。能夠是各類數據類型,也能夠是POJO類(要求是類的全限定名),也能夠給出別名(數據類型的別名或類的全限定名的別名)。【給出POJO類的時候,若是容許自動匹配,將自動根據映射關係自動將返回結果封裝到對象中】。【resultType不能與resultMap一塊兒用,他們的功能重複了
    • resultMap:resultMap標籤用來定義返回結果與類的映射關係,在select中使用resultMap屬性時,能夠將返回結果根據映射關係來賦值。【後面將具體介紹】
  • 示例:

    • <select id="getUserById" parameterType="int" resultType="user" >
          SELECT * FROM USER WHERE id = #{id1}
      </select>
  • 使用問題:
    • 傳參問題:
      • 可使用#{參數名}來向SQL語句傳遞參數。
        • 對於基本數據類型,因爲僅有一個參數(不像集合或類包含多個變量),參數名是能夠隨意取的。
          • <select id="getUserById" parameterType="int" resultType="work.pojo.User" >
                  SELECT * FROM USER WHERE id = #{id1}
            </select>
        • 對於對象類型,因爲對象中有屬性,因此要使用#{屬性名}來把對象中的數據傳遞給SQL。若是對象的屬性仍是一個對象那麼可使用#{內含對象名.屬性名}

          • <!-- 因爲傳參是同樣的用法,這裏用insert做爲例子,User中有屬性name和age -->
            <insert id="insertUser" parameterType="work.pojo.User" >
                  INSERT INTO USER(name,age)
                  VALUES 
                  (#{name},#{age});
            </insert>
      • 對於集合類的入參,這須要使用到動態SQL中的foreach標籤,這個留到後面再講。


  • 對於模糊查詢,在傳參的時候可能會有問題,你可能會思索%這個東西在哪裏存:
    • 方式一:在函數調用方法並傳參給SQL的時候,手動拼接;而後在SQL正常的使用#{參數名}來獲取參數。
      • 代碼中進行拼接:sqlSession.selectList("user.getUserByName","%唐%");,SQL中使用#{參數名}SELECT * FROM USER WHERE name LIKE #{name};
    • 方式二:${參數名}能夠用來字符串拼接,它能夠在將字符串包裹的${參數名}轉成對應的數據,但對於非POJO類型的參數名只能爲value,對於POJO類型的就只能使用POJO類型的屬性了。
      • SELECT * FROM USER WHERE name LIKE '%${value}%';
    • 方式三:調用SQL內置函數concat來拼接。【這個要注意數據庫有沒有這個函數】
      • SELECT * FROM USER WHERE name LIKE CONCAT('%',#{name},'%')


  • 返回結果問題:
    • 可使用resultType和resultMap來定義返回結果,因爲resultMap屬性藉助resultMap標籤,resultMap留到resultMap標籤再講
    • resultType的值能夠是各類數據類型,不過要符合數據的封裝要求,好比返回多列數據時resultType不該該設置成int,resultType的值是什麼要你本身去判斷,只要你符合封裝規則,那麼就能封裝成功。好比SELECT * FROM USER WHERE id = #{id}的返回值應該是一個User類。
    • 返回結果是多條記錄的時候,返回結果類型也能夠是POJO類,MyBatis會自動將返回結果轉成POJO類的數組。


  • 映射規則問題:
    • 若是傳入一個POJO類,默認是自動映射的,【若是屬性名不一致,可使用字段別名或者resultMap來進行強制映射】
      • 假設數據表中的字符名爲username,但對象中的屬性名爲name,那麼怎麼對應上呢?

        <!-- SELECT id,username ,age FROM USER WHERE id = #{id}  --><!--因爲字符名與屬性名不徹底相同,自動映射失敗-->
        <!-- 使用下面的語句,利用別名 -->
        SELECT id,username as name ,age FROM USER WHERE id = #{id}
      • 使用resultMap,顯式對應:property是類對象的屬性名,column是字段名

        <resultMap type="work.pojo.User" id="userMap">
                  <id property="id" column="id" />
                  <!-- 手動把字段名與屬性對應上 -->
                  <result property="name" column="username" />
                  <result property="age"  column="age"/>
        </resultMap>
    • 多表時必須使用resultMap。由於沒有自動跨表映射(除非你定義了返回結果是一個包含了多個表全部字段的類)。


resultMap

  • resultMap用來顯式定義數據封裝規則,resultMap把數據表的數據與對象的屬性一一對應起來,若是SQL語句的返回結果類型使用了resultMap,這樣MyBatis就會根據resultMap中的規則來封裝返回的數據。

  • 屬性
    • id:resultMap的id,有了id,就能夠在其餘標籤的resultMap屬性中引用了。
    • type:要創建映射關係的POJO類的全限定名。
  • id標籤:用來定義主鍵的映射關係
    • property:對象的屬性名,表明映射到對象的哪一個屬性中。
    • column:表明數據表中的列名。
  • result標籤:用來定義普通字段的映射關係。

  • <!-- 定義resultMap -->  
    <resultMap type="work.pojo.User" id="userMap">
              <!--id標籤把數據表中的id字段與User類中的id屬性對應起來 -->
              <id property="id" column="id" />
              <result property="name" column="name" />
              <result property="age"  column="age"/>
    </resultMap>
    <!-- 引用resultMap -->
    <select id="getUserById" parameterType="int"  resultMap="userMap">
          SELECT * FROM USER WHERE id = #{id1}
    </select>
  • resultMap還與關聯查詢有關係,關聯查詢依靠resultMap來定義映射關係。這留到後面講。


insert標籤:

  • insert用來定義插入語句

  • 屬性:

    • parameterType:定義傳入的參數類型,使用能夠參考select中的parameterType。
    • useGeneratedKeys:獲取插入記錄的主鍵(數據庫自動生成的)
    • keyProperty:用來標識主鍵返回結果賦給哪一個變量,複合主鍵時,使用逗號分隔變量名。【keyColumn】
  • 使用說明:增刪改三種語句默認是不提交,要commit

  • <insert id="insertUser" parameterType="work.pojo.User" >
          INSERT INTO USER(name,age)
          VALUES 
          (#{name},#{age});
    </insert>
  • 主鍵返回問題,有時候咱們但願插入了一條記錄後,獲取到它的ID來處理其餘業務,那麼這時候咱們怎麼獲取它的主鍵呢?下面列舉兩種方法:

JDBC式:在insert標籤上配置useGeneratedKeys和keyProperty屬性,useGeneratedKeys用來代表這個數據庫是否是自增加id的,因此這個獲取主鍵的方式不適用與Oracle。keyProperty用來指明返回的主鍵值存儲到哪一個屬性中。

<insert id="save" parameterType="work.domain.User" useGeneratedKeys="true" keyProperty="userId" >
    insert into user(userName,password,comment)
    values(#{userName},#{password},#{comment})
</insert>

數據庫式:利用數據庫的函數來獲取主鍵,keyProperty用來指明返回的主鍵值存儲到哪一個屬性中。order用來指定執行的順序,到底是插入前獲取--BEFORE,仍是插入後獲取--AFTER。resultType用來指明返回主鍵的數據類型。

<insert id="save" parameterType="work.domain.User" >
       <selectKey resultType="java.lang.Long" order="AFTER" keyProperty="userId">
          SELECT LAST_INSERT_ID()
      </selectKey>
        insert into user(userName,password) values(#{userName},#{password})
    </insert>


update標籤:

  • 屬性與insert差很少

  • MyBatis因爲是根據SQL去修改,因此它不會發生覆蓋修改問題(Hibernate基於對象來查詢,對於沒有設置值的屬性也會修改到數據表中,因此Hibernate中一般都是先查詢後修改)

  • <update id="updateUser" parameterType="com.pojo.User">
          UPDATE USER SET username = #{username} WHERE id = #{id}
    </update>


delete標籤:

  • 屬性與insert差很少

  • <delete id="deleteUser" parameterType="int">
          DELETE FROM user WHERE `id` = #{id1}
    </delete>


sql標籤:

  • sql標籤能夠用來定義一部分SQL語句,定義的部分SQL語句能夠在別的地方引用。好比咱們常常會用到列名,咱們須要反覆填寫列名,若是咱們將這一部分列名定義到sql標籤中,咱們就能夠經過引用來使用了。
<sql id="userCols">
        id,name,age
  </sql>
  <!-- 開始引用 -->
  <select id="getUserById" parameterType="int"  resultType="work.pojo.User">
        <!-- SELECT id,name,age FROM USER WHERE id = #{id1} -->
        SELECT <include refid="userCols"></include> FROM USER WHERE id = #{id1} 
  </select>


補充:

  • 因爲cache和cache-ref涉及緩存問題,這將留到後面緩存部分再講。



SQL發送


上面學過了映射文件裏面定義SQL,那麼下面來了解一下怎麼使用這些SQL語句。發送SQL的方式有兩種,一種是使用SqlSession來發送,一種是使用Mapper來發送。


使用SqlSession發送:



1.建立映射文件,這裏假設爲user.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="user">
    <select id="getUserById" parameterType="int" resultType="user" >
        SELECT id,name,age FROM USER WHERE id = #{id} 
    </select>
</mapper>


2.在配置文件中引入映射文件:【因爲沒有怎麼講mybatis配置文件,你能夠先按照基礎示例裏面來修改】

<mappers>
        <!-- 導入映射文件 -->
        <mapper resource="work/pojo/user.xml"/>
</mappers>


3.獲取SqlSession,並使用SqlSession調用對應的SQL:

@Test
    public void test3() throws IOException {
        SqlSessionFactoryBuilder sfb=new SqlSessionFactoryBuilder();
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sessionFactory = sfb.build(inputStream);
        SqlSession sqlSession = sessionFactory.openSession();
        //根據SQL ID來發送SQL,第二個是傳給SQL的參數。
        User user = sqlSession.selectOne("user.getUserById",3);
        System.out.println(user);
        sqlSession.close();
    }


經常使用方法

  • selectOne("sql id")方法表示調用的是查詢語句,而且返回的是一個對象【若是返回結果是多個,會報錯。】
  • selectList("sql id")方法表示調用的是查詢語句,而且返回的是一個列表。
  • insert("sql id")方法表示調用的是插入語句,對應映射文件中使用insert標籤訂義的SQL語句。
  • delete("sql id")方法表示調用的是刪除語句,對應映射文件中使用delete標籤訂義的SQL語句。
  • update("sql id")方法表示調用的是修改語句,對應映射文件中使用update標籤訂義的SQL語句。


使用Mapper接口發送:動態代理開發


1.首先建立一個接口,

  • 接口名一般都是XXXMapper,若是是針對User類,能夠是UserMapper
  • 接口名要求與映射文件名一致
  • 接口的方法名要求與映射文件的SQL id一致,
  • 接口的方法的形參必需要與對應SQL的parameterType類型一致,
  • 接口的返回類型必須與對應的SQL的resultType類型一致。
package work.pojo;

public interface UserMapper {
    public User getUserById(int id);
}

2.建立映射文件(這裏假設爲UserMapper.xml),映射文件要求namespace必需是接口的全路徑名:

<!-- namespace要寫接口的全限定名 -->
<mapper namespace="work.pojo.UserMapper">
    <!--注意返回類型和入參類型要與接口的一致 -->
    <select id="getUserById" parameterType="int" resultType="work.pojo.User" >
        SELECT id,name,age FROM USER WHERE id = #{id}
    </select>
</mapper>

3.在配置文件中引入Mapper:

<mappers>
        <!-- 導入映射文件 -->
        <!--既能夠導入接口,也能夠導入映射文件 -->
        <!--<mapper resource="work/pojo/UserMapper.xml"/> -->
        <mapper class="work.pojo.UserMapper"/>
</mappers>

4.在代碼中利用sqlSession獲取接口的Mapper,並使用Mapper發送SQL(調用對應的接口方法就是發送對應SQL):【這裏提一下,雖然獲取的是接口,但因爲遵循了開發規範,Mybatis會幫忙建立一個這個接口的實現類,因此實質返回的是一個實現類對象】

@Test
    public void test4() throws IOException {
        SqlSessionFactoryBuilder sfb=new SqlSessionFactoryBuilder();
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sessionFactory = sfb.build(inputStream);
        SqlSession sqlSession = sessionFactory.openSession();
        //根據接口.class來獲取mapper
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
     //調用接口的方法就是調用方法對應的SQL。
        User user = mapper.getUserById(3);
        System.out.println(user);
        sqlSession.close();
    }

Mapper說明:

  • UserMapper mapper = sqlSession.getMapper(UserMapper.class);傳入一個UserMapper.class就能夠獲取一個可以調用方法的mapper,咱們僅僅定義了UserMapper接口,爲何能獲取一個可以獲取一個可以調用方法的mapper呢?由於mybatis底層自動幫咱們去建立這個接口的實現類了,因爲咱們遵循了設計規範,mybatis可以很容易地瞭解該如何去建立實現類。建立完實現類後,返回一個實現類的對象。


兩種方式對比與DAO層開發問題

  • MyBatis一般用於數據訪問層DAO的開發,對於使用SqlSession發送SQL的方式來講,它跟以往的開發同樣,也須要定義DAO接口和實現方法;但對於使用Mapper發送SQL的方式來講,它跟以往的開發不一樣,它只須要定義接口了,而再也不須要接口實現類了。
  • SqlSession方式:

  • Mapper方式:因爲UserMapper接口已經包含原來的UserDao的功能了,而且不須要自定義實現類,因此這裏能夠省去DAO層。



兩種方式比較:

  • 建議使用Mapper方式發送
  • SqlSession發送是直接發送;Mapper方式是先經過SqlSession獲取Mapper接口再發送。
  • SqlSession方式是面向SQL的,Mapper方式是面向對象的。SqlSession發送SQL,須要一個SQL id 去匹配SQL。Mapper發送SQL只須要調用調用對應的方法,例如mapper.getRole(1L)
  • 使用Mapper方式發生SQL要調用對應的接口方法,接口方法能幫咱們檢查入參類型,參數類型不對時編譯器會檢錯,而sqlsession.selectOne()只有在運行時才報錯。



MyBatis配置文件


  • 上面談完了一些「定義SQL和發送SQL」方面的內容,這節主要是講解MyBatis配置文件的內容,配置文件是用來配置Mybatis的運行環境的,例如數據庫驅動、url、用戶名、密碼、採用的事務管理方式等。
  • 配置文件的命名是能夠隨意的,一般能夠命名爲mybatis-config.xml


配置文件的約束

  • 配置文件的dtd約束配置信息並無寫在jar包中,因此咱們不能從依賴包中查到,只能依靠官方文檔來獲取,下載的包中的mybatis-3.4.5.pdf中就有,在 Getting started中能夠複製dtd約束。

    • <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

可配置標籤:

標籤的出現的順序必須按照順序。

<configuration><!-- configuration是配置文件的頂級標籤  -->
    <!-- properties用於配置一些參數,這些參數能夠被引用到下面的其餘配置中,至關於properties文件 -->
    <properties></properties>
    <!-- setting用於配置一些mybatis底層相關的屬性,好比緩存 -->
    <settings></settings>
    <!--typeAliases 用於配置類型命名 -->
    <typeAliases></typeAliases>
    <!-- typeHandlers用於配置類型處理器  -->
    <typeHandlers></typeHandlers>
    <!-- objectFactory用於配置對象工廠  -->
    <objectFactory type=""></objectFactory>
    <!--plugins用於配置插件 -->
    <plugins></plugins>
    <!-- environments配置不一樣的數據庫鏈接環境 -->
    <environments default="">
        <environment id=""> <!--environment用於配置數據庫鏈接環境  -->
            <!-- transactionManager用於配置事務管理器 -->
            <transactionManager type=""></transactionManager> 
            <!--dataSource用於配置數據源  -->
            <dataSource type=""></dataSource>
        </environment>
    </environments>
    <!-- databaseIdProvider是數據庫產商標識 -->
    <databaseIdProvider type=""></databaseIdProvider>
    <!-- mappers用於配置映射器 -->
    <mappers></mappers>
</configuration>


上面的標籤中,plugins涉及插件,是偏向底層的內容,若是不瞭解mybatis運行原理,那麼可能會比較晦澀,這會留到另一篇博文再講述(若是有空的話)。【databaseIdProvider和objectFactory不講述】
下面主要講properties、settings、typeAliases、typerHandler、environments、mappers。



經常使用標籤:

environments標籤

  • environments用來配置多個數據庫鏈接環境,每個環境都使用environment標籤來定義。(不一樣的環境能夠定義不一樣的數據庫鏈接信息)

    • 惟一屬性--default:表明默認使用哪一個環境,值爲environment的id,這裏的值影響SqlSessionFactoryBuilder調用build(配置文件輸入流)時使用哪一個環境初始化SqlSessionFactory。例如:<environments default="development">省略environment配置</environments>表明默認使用id="development"的環境。
  • environment標籤:是environments的子標籤,用來定義數據庫鏈接環境,子標籤有transactionManager(用來定義事務管理器,事務管理器負責定義這個數據庫環境中的事務怎麼處理)、dataSource(用來定義數據源,主要是配置數據庫鏈接信息)。
    • 惟一屬性——id:用來惟一標識每一個環境。
    • 子標籤transactionManager:用來定義事務管理器
      • 惟一屬性:type:
        • JDBC:以JDBC的方式對數據庫的提交和回滾進行操做。MyBatis中JDBC事務管理器的事務默認是不自動提交的,因此增刪改操做須要sqlSession.comit()來進行事務提交。
        • MANAGED:把事務交給容器來管理,如JBOSS,Weblogic這樣的容器。
      • transactionManager有子標籤property,能夠配置事務管理器的一些規則【這裏不講,有興趣自查。】
      • 示例:<transactionManager type="JDBC" ></transactionManager>
    • 子標籤dataSource:用來定義數據源
      • 惟一屬性:type:
        • POOLED:數據庫鏈接池的方式,配置這個值表明使用MyBatis自帶的數據庫鏈接池。
        • UNPOOLED:非數據庫鏈接池的方式
        • JNDI:使用外部數據庫鏈接源,tomcat數據源之類的外部數據庫鏈接源
        • 第三方鏈接池【mybatis能夠整合第三方鏈接池,但這須要建立新的類,因此留到後面再講。】
      • 子標籤property能夠配置一些數據庫鏈接的參數,除了基本的數據庫driver、url、username、password,還能夠根據type的不一樣值來配置一些不一樣參數,不一樣type裏面的數據庫驅動的定義方式可能也是不一樣的。
<environments default="development">
        <!-- environment用來定義數據庫環境,environments用來定義多個environment -->
        <environment id="development">
            <!-- 配置事務管理,使用jdbc事務管理 -->
            <transactionManager type="JDBC" />
            <!-- 配置數據庫鏈接池 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis2" />
                <property name="username" value="root" />
                <property name="password" value="123456" />
            </dataSource>
        </environment>
  </environments>


properties標籤

  • properties標籤的做用相似properties文件,properties標籤能夠導入properties配置文件,也能夠直接使用子標籤property來配置參數。properties標籤中配置的參數能夠在後面的其它屬性配置中引用。

  • 使用方式:

    • 用properties標籤導入配置文件:

      • <!-- 示例 -->
        <properties resource="jdbc.properties"></properties>
        導入後,根據${鍵名}來引用配置文件中配置的參數,例如在配置數據源的時候:<property name="driver" value="${jdbc.driverClass}" />
    • 用properties標籤訂義property變量,定義後也可使用${鍵名}來引用。

      • 設置參數:

        • <properties>
                  <property name="db.driver" value="com.mysql.jdbc.Driver"/>
                  <property name="db.url" value="jdbc:mysql://localhost:3306/mybatis2"/>
                  <property name="db.user" value="root"/>
                  <property name="db.password" value="123456"/>
              </properties>
  • 引用參數:

    • <environment id="test">
                  <!-- 配置事務管理,使用jdbc事務管理 -->
                  <transactionManager type="JDBC" />
                  <!-- 配置數據庫鏈接池 -->
                  <dataSource type="POOLED">
                      <property name="driver" value="${db.driver}" />
                      <property name="url" value="${db.url}" />
                      <property name="username" value="${db.user}" />
                      <property name="password" value="${db.password}" />
                  </dataSource>
              </environment>
  • 爲了分清楚配置的屬性的做用,一般都會使用點分法來定義變量,如可使用db.xxx表明這個配置與數據庫相關。【固然,不使用點分法也能夠】


Settings標籤

  • Settings用於配置一些涉及MyBatis底層運行的參數(好比日誌打印、自動映射、緩存管理),大部分狀況下使用默認值。
  • 配置項以及配置項的值,能夠參考這篇博文https://blog.csdn.net/u014231523/article/details/53056032,好像寫的算靠譜了。


typeAliases標籤

  • typeAliases一般用來別名,定義別名後,能夠把很長的名字定義成一個較短的名字,例如類的全限定名(qualified name)很長時,能夠用一個別名來表明這個類路徑。

  • 別名可使用在映射文件中,一般能夠把類的全限定名定義成別名,這樣就能夠簡寫了。

  • 別名分爲系統定義別名和自定義別名

    • 系統定義別名主要是一些數據類型別名,因此要注意定義sql的入參類型和結果類型,SQL語句的結果類型resultType定義成int的時候,返回的結果應該用Integer類型的變量存儲

      別名 映射的類型
      _byte byte
      _long long
      _short short
      _int int
      _integer int
      _double double
      _float float
      _boolean boolean
      string String
      byte Byte
      long Long
      short Short
      int Integer
      integer Integer
      double Double
      float Float
      boolean Boolean
      date Date
      decimal BigDecimal
      bigdecimal BigDecimal
    • 自定義別名:

      • 可使用typeAlias標籤來定義別名:

        • <typeAliases>
                  <!-- alias的值是別名,type的值是要定義別名的名字 -->
                  <typeAlias type="work.pojo.User" alias="user"/>
          </typeAliases>
      • 別名包掃描:

        • 因爲別名不少時候都用在映射文件中(解決類全限定名過長問題),因此MyBatis支持對包進行別名掃描,掃描的包下的類都被定義成別名,別名就是類名
      • <typeAliases>
            <!-- 整個包下的類都被定義別名,別名爲類名,不區分大小寫-->
              <package name="work.pojo"/>
            <!--這樣pojo下的類都被定義成了別名,例如work.pojo.User能夠簡寫成user -->
        </typeAliases>
  • 別名是忽略大小寫的,因此別名user,你既可使用user來引用,也可使用User來引用。




typeHandler標籤:

  • typeHandler是用來處理類型轉換問題的,從數據庫中獲取的數據要想裝入到對象中,勢必要通過類型轉換,比如MyBatis必須知道如何將varchar轉成String。對於常見的類型轉換,MyBatis已經幫咱們定義了很多類型轉換器,下面給出常見的。

    • 數據庫數據類型 Java類型 類型轉換器
      CHAR, VARCHAR String StringTypeHandler
      BOOLEAN Boolean BooleanTypeHandler
      SMALLINT Short ShortTypeHandler
      INTEGER Integer IntegerTypeHandler
      FLOAT Float FloatTypeHandler
      DOUBLE Double DoubleTypeHandler
  • 因爲MyBatis的轉換器已經可以解決大部分問題了,因此這裏不講怎麼自定義轉換器,有興趣的能夠自查。



mappers標籤

  • mappers是用來引入映射器(映射文件)的,有了映射器,Mybatis纔可以管理到在映射文件中定義的SQL。

  • 引入方式:

    • 導入配置文件,經過配置文件的路徑名導入:

      • <mappers>
              <!-- 第一種方式,根據映射文件路徑-->
              <mapper resource="work/pojo/user.xml"/>
        </mappers>
    • 使用mapper方式開發時,因爲映射文件與mapper對應,也能夠經過mapper的全限定名導入:

      • 【要求映射文件與mapper處於同一目錄,並且映射文件的文件名與要mapper名一致】

        <mappers>
              <!-- 對下面的配置,要求映射配置文件的文件名爲UserMapper.xml;要求UserMapper.xml與UserMapper處於同一目錄下-->
              <mapper class="work.mapper.UserMapper"/> 
        </mappers>
    • 使用mapper方式開發時,因爲映射文件與mapper對應,也能夠經過掃描mapper所在的包來導入:

      • 【要求映射文件與mapper處於同一目錄,並且映射文件的文件名與要mapper名一致】

      • <mappers>
              <!-- 第三種方式,包掃描器:
                     一、映射文件與接口同一目錄下
                     二、映射文件名必需與接口文件名稱一致
                -->
              <package name="work.mapper"/>
        </mappers>


補充:

  • plugin插件是一個比較大的內容,並且偏向底層,這會留到另一篇博文再講述。。
  • 除了上面的,還有databaseIdProvide數據庫廠商標識,objectFactory對象工廠,但不經常使用。有興趣的自查。



動態SQL

  • 當容許使用不定數量的條件來查詢的時候(這種狀況就是多條件查詢的狀況),入參沒法肯定是否已經正確填寫,那麼你可能遇到查詢條件爲空的狀況。因此咱們須要對查詢條件進行判斷,動態SQL就能夠解決這種問題。


標籤:

if標籤

  • 單條件分支判斷
  • 屬性:
    • test:對條件進行判斷,test裏面能夠是一個表達式。判斷字符串時,容許字符串調用函數。
  • 下面的SQL語句,若是調用的時候不傳參,將獲得SELECT * FROM USER WHERE age = null;

    <select id="getUserByAge" parameterType="int" resultType="work.pojo.User">
          SELECT * FROM USER WHERE age = #{age};
    </select>
  • 利用if標籤解決問題:

    <select id="getUserByAge" parameterType="int" resultType="work.pojo.User">
          SELECT * FROM USER 
          <if test="age!=null ">
          WHERE  age = #{age}
          </if>
    </select>


where標籤

  • where主要用於處理多個判斷條件的拼接。在where A條件 and B條件可能須要判斷一下A和B的合法性,若是僅僅使用if標籤那麼可能會致使where、and字符串重複,而where會自動處理多餘的and和where,還會自動加上where。

  • <select id="getUserByAgeOrName" parameterType="work.pojo.User" resultType="work.pojo.User">
          SELECT * FROM USER 
          <where>
              <if test="age!=null ">
                  and age = #{age}
              </if>
              <if test="name!=null">
                  and name like '%${name}%'
              </if>
          </where>
    </select>


set標籤:

  • where標籤能夠解決查詢條件中的where問題,但若是對一個對象進行update時,如何判斷傳入的set值是否爲空呢?可使用if來進行判斷,而後再使用set標籤解決條件字符串重複問題,set標籤能夠去除多餘的逗號。

  • <update id="updateUser" parameterType="work.pojo.User">
          UPDATE USER SET 
              name=#{name},
              age=#{age}
          where id=#{id}
    </update>
  • <update id="updateUser" parameterType="work.pojo.User">
          UPDATE USER
          <set>
              <if test="name!=null">name=#{name},</if>
              <if test="age!=null">age=#{age}</if>
          </set>
              where id=#{id}
    </update>


foreach標籤

  • 當傳入的參數是一個集合的時候,遇到的問題是怎麼把集合中的數據遍歷出來,foreach標籤能夠遍歷集合。
  • 標籤的屬性:
    • collection:要遍歷的集合【傳入類型爲List,那麼集合名爲list;傳入的是數組,數組名爲array】
    • item:用來迭代的變量
    • open:循環開始以前輸出的內容
    • close:循環完成後輸出的內容
    • seperator:分隔符
<select id="getUserByAgeCollection" parameterType="List" resultType="work.pojo.User">
        SELECT * FROM USER WHERE 
        <foreach item="age" collection="list" open="age  IN("  separator=","  close=")">
            #{age}
        </foreach>
    </select>


補充:

  • 除了上面的標籤,還有例如trim,choose,when標籤,因爲上面的足夠平常使用了,因此就不講那麼多了。其餘的有興趣能夠自查瞭解一下。



關聯查詢


  • 在開發中,關聯查詢是很是常見的,關聯查詢就是在查詢某個表時,經過字段關聯關係,查出咱們所須要的另一個表中的數值。例如:一般來講,班級信息表和學生表是獨立的,但班級信息表和學生表是有關係的,咱們不能從班級信息表中獲取到學生信息,若是咱們查詢班級信息表時但願同時獲取到班級下的學生信息,那麼咱們就須要使用到關聯查詢。
  • 關聯查詢一般有一對一查詢(關係相似於一個部門只有一個經理),一對多查詢(關係相似於一個部分能夠有多個員工),多對多查詢(關係相似於一個學生能夠選修多門課,一門課也能夠被多名學生選修)。


一對一關聯查詢:

以一個部門對應一個經理爲例。


方式一:

建立一個POJO類,用鏈接查詢語句查詢出兩個表的數據,經過resultType封裝到一個POJO類中。


步驟:
1.首先建立一個POJO類,要求包含了部門信息和經理信息(若是你不想重複定義屬性,也能夠選擇繼承單個類的信息繼承兩個類,這裏考慮到有選擇地查詢字段因此不繼承):

package work.pojo;

public class Department_Manager {
    //部門的信息
    private Integer did;
    private String department_name;
    //經理的信息
    private Integer mid;
    private String name;
    private Double salary;
    public Integer getDid() {
        return did;
    }
    public void setDid(Integer did) {
        this.did = did;
    }
    public String getDepartment_name() {
        return department_name;
    }
    public void setDepartment_name(String department_name) {
        this.department_name = department_name;
    }
    public Integer getMid() {
        return mid;
    }
    public void setMid(Integer mid) {
        this.mid = mid;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Double getSalary() {
        return salary;
    }
    public void setSalary(Double salary) {
        this.salary = salary;
    }
    @Override
    public String toString() {
        return "Department_Manager [did=" + did + ", 部門名=" + department_name + ", mid=" + mid + ", 經理名="+ name + ", 工資=" + salary + "]";
    }
}



數據表的建立(要注意,因爲存在關係,那麼兩個表之間確定有一個字段用於創建關聯,在POJO類中能夠沒有這個字段,但表中必定要有,由於SQL語句依靠這個來查詢):

use mybatis2;
create table department(
did int primary key auto_increment,
department_name varchar(20)
);
create table manager(
mid int primary key auto_increment,
name varchar(20),
salary double(7,2),
did int,
foreign key(did) references department(did) --這裏用外鍵關聯
);
insert into department values(null,'開發部'),(null,'市場部');
insert into manager values(null,'張三',20000.00,1),(null,'李四',20000.00,2);


2.編寫SQL語句:【這裏使用Mapper方式】

定義接口:

package work.pojo;

public interface UserMapper {
    public Department_Manager[] getDepartmentManager();
}

建立映射文件:

<?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="work.pojo.UserMapper">
    <select id="getDepartmentManager" resultType="work.pojo.Department_Manager" >
        SELECT
             d.did,d.department_name,
             m.mid,m.name,m.salary
        FROM department d
        LEFT JOIN manager m
        ON d.did = m.did
    </select>
</mapper>

3.編寫測試方法:

  • @Test
      public void test5() throws IOException {
          SqlSessionFactoryBuilder sfb=new SqlSessionFactoryBuilder();
          InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
          SqlSessionFactory sessionFactory = sfb.build(inputStream);
          SqlSession sqlSession = sessionFactory.openSession();
          UserMapper mapper = sqlSession.getMapper(UserMapper.class);
          Department_Manager[] departmentManager = mapper.getDepartmentManager();
          for (Department_Manager department_Manager : departmentManager) {
              System.out.println(department_Manager);
          }
          sqlSession.close();
      }

因此第一種方式是把數據封裝到一個包含了多個表的全部字段的POJO對象的方式。若是有多個表,有多種關係的話,第一種方式會顯得麻煩複雜。

方式二:



建立一個在部門類裏面添加一個經理類對象,用鏈接查詢語句查詢出兩個表的數據,經過resultMap把查出來的數據封裝到部門類中。


步驟:

1.建立經理類,建立部門類,在部門類中添加一個經理類對象。

經理類:

java package work.pojo; public class Manager { private Integer mid; private String name; private Double salary; private Integer did; //did是部門表的主鍵,你能夠不定義,由於關注點在部門,你也能夠定義成一個部門對象。 public Integer getMid() { return mid; } public void setMid(Integer mid) { this.mid = mid; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } public Integer getDid() { return did; } public void setDid(Integer did) { this.did = did; } @Override public String toString() { //因爲沒有給did封裝數據,因此這裏不打印did return "Manager [mid=" + mid + ", name=" + name + ", salary=" + salary + "]"; } }

部門類:

package work.pojo;
public class Department {
    private Integer did;
    private String department_name;
    //經理的映像
    private Manager manager;
    public Integer getDid() {
        return did;
    }
    public void setDid(Integer did) {
        this.did = did;
    }
    public String getDepartment_name() {
        return department_name;
    }
    public void setDepartment_name(String department_name) {
        this.department_name = department_name;
    }
    public Manager getManager() {
        return manager;
    }
    public void setManager(Manager manager) {
        this.manager = manager;
    }
    @Override
    public String toString() {
        return "Department [did=" + did + ", department_name=" + department_name + ", manager=" + manager + "]";
    }
}


2.定義映射文件和接口:

定義接口:

package work.pojo;

public interface UserMapper {
    public Department_Manager[] getDepartmentManager2();
}

建立映射文件,使用resultMap定義多表關聯映射規則:

<?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="work.pojo.UserMapper">
    <resultMap type="work.pojo.Department" id="department_manager">
            <id property="did" column="did" />
            <result property="department_name" column="department_name" />
            <!--association用來處理對象中存在另外一個類的對象的狀況,
            property是對象中另外一個類的對象變量名,javaType是另外一個類的全限定名
            標籤裏面使用id和result來映射數據與另外一個類的對象的關係。
            -->
            <association property="manager" javaType="work.pojo.Manager">
                <id property="mid" column="mid"/>
                <result property="name" column="name" />
                <result property="salary" column="salary"/>
                <!-- 這裏要注意不要封裝外鍵字段,由於咱們這裏的經理表的外鍵是did,部門表主鍵是did,會命名衝突;因爲這裏沒封裝數據給經理類的did,因此經理類的did是沒有數據的;若是你想給經理類的did賦值,那麼你應該利用別名,在SQL中令m.did m_did,而後上面使用<result property="did"  column="m_did"/> -->
            </association>
    </resultMap>
    
    <select id="getDepartmentManager2"  resultMap="department_manager" >
        SELECT
             d.did,d.department_name,
             m.mid,m.name,m.salary
        FROM department d
        LEFT JOIN manager m
        ON d.did = m.did
    </select>
</mapper>

3.編寫測試方法:

@Test
    public void test6() throws IOException {
        SqlSessionFactoryBuilder sfb=new SqlSessionFactoryBuilder();
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sessionFactory = sfb.build(inputStream);
        SqlSession sqlSession = sessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Department[] departments = mapper.getDepartmentManager2();
        for (Department department : departments) {
            System.out.println(department);
        }
        sqlSession.close();
    }




一對多關聯查詢:

若是採用相似上面一對一的方式一來處理一對多話,一一方的會出現不少次(方式一是封裝查詢的每行記錄,一對多查詢的時候一一方的數據會出現屢次,因此採用方式二來實現一對多)。下面以一個班級能夠有多個學生爲例。

步驟:

1.建立Student類,建立Grade類,在Grade中添加一個由Student對象組成的List:

Student類:

package work.pojo;

public class Student {
    private Integer sid;
    private String stu_name;
    public Integer getSid() {
        return sid;
    }
    public void setSid(Integer sid) {
        this.sid = sid;
    }
    public String getStu_name() {
        return stu_name;
    }
    public void setStu_name(String stu_name) {
        this.stu_name = stu_name;
    }
    @Override
    public String toString() {
        return "Student [sid=" + sid + ", stu_name=" + stu_name + "]";
    }
}

Grade類:

package work.pojo;

import java.util.List;

public class Grade {
    private Integer gid;
    private String grade_name;
    private List<Student> students;
    public Integer getGid() {
        return gid;
    }
    public void setGid(Integer gid) {
        this.gid = gid;
    }
    public String getGrade_name() {
        return grade_name;
    }
    public void setGrade_name(String grade_name) {
        this.grade_name = grade_name;
    }
    public List<Student> getStudents() {
        return students;
    }
    public void setStudents(List<Student> students) {
        this.students = students;
    }
    @Override
    public String toString() {
        return "Grade [gid=" + gid + ", grade_name=" + grade_name + ", students=" + students + "]";
    }
}

2.建立數據表,插入測試數據:

create table grade(
gid int primary key auto_increment,
grade_name varchar(20)
);
create table student(
sid int primary key auto_increment,
stu_name varchar(20),
gid int,
foreign key(gid) references grade(gid) 
);
insert into grade values(null,"python班"),(null,'Java班');
insert into student values(null,'張三',1),(null,'李四',1),(null,'王五',2),(null,'趙六',2);

3.建立mapper接口和映射文件:

接口:

package work.pojo;
public interface UserMapper {
    public Grade[] getGradeStudent();
}

映射文件:

<?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="work.pojo.UserMapper">
    <resultMap type="work.pojo.Grade" id="grade_student_map">
        <id property="gid" column="gid"/>
        <result property="grade_name" column="grade_name"/>
        <!-- collection用來處理一個類中含有其餘類的集合時的數據映射(一對多)
            property是集合的變量名,ofType是集合元素的全限定名,
            collection內使用id和result創建映射關係
          -->
        <collection property="students" ofType="work.pojo.Student" >
            <id property="sid" column="sid"/>
            <result property="stu_name" column="stu_name" />
        </collection>
    </resultMap>
    
    <select id="getGradeStudent" resultMap="grade_student_map">
            SELECT
                 g.gid,g.grade_name,
                 s.sid,s.stu_name
            FROM grade g
            LEFT JOIN student s
            ON g.gid = s.gid
    </select>
</mapper>


4.編寫測試方法:

@Test
    public void test7() throws IOException {
        SqlSessionFactoryBuilder sfb=new SqlSessionFactoryBuilder();
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sessionFactory = sfb.build(inputStream);
        SqlSession sqlSession = sessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Grade[] gradeStudent = mapper.getGradeStudent();
        for (Grade grade : gradeStudent) {
            System.out.println(grade);
        }
        sqlSession.close();
    }




多對多:

因爲多對多太過複雜,Mybatis不支持多對多,Mybatis選擇使用兩個一對多來實現多對多。下面以一名學生能夠選多門課,一門課能夠被多個學生選爲例。【多對多關係模型是兩方都有對方的數據,兩方都能查詢到對方的數據,因此咱們使用一對多的時候,能向兩方的對象中都裝入數據,就能夠達到多對多的效果;但有一點效果不能達到,(以例子講解)課程對象中的學生集合沒有與課程對象創建上關係,但從業務需求來講一般不關注另一方的關係(若是關注,從學生一方查詢便可,爲何要使用課程中的學生對象?),因此這個缺點也不算缺點。另外,下面也會講一下解決這個問題的方法。】

步驟:

1.建立Cource類,建立SchoolChild類,分別在兩個類中建立對方類的集合:【這裏要注意toString問題,若是A的toString包含B,B包含A,那麼會死循環。】

Cource類:

package work.pojo;

import java.util.List;

public class Cource {
    private Integer cid;
    private String cource_name;
    private List<SchoolChild> schoolchilds;
    public Integer getCid() {
        return cid;
    }
    public void setCid(Integer cid) {
        this.cid = cid;
    }
    public String getCource_name() {
        return cource_name;
    }
    public void setCource_name(String cource_name) {
        this.cource_name = cource_name;
    }
    public List<SchoolChild> getSchoolchilds() {
        return schoolchilds;
    }
    public void setSchoolchilds(List<SchoolChild> schoolchilds) {
        this.schoolchilds = schoolchilds;
    }
}

SchoolChild類:

package work.pojo;

import java.util.List;

public class SchoolChild {
    private Integer sid;
    private String stu_name;
    private List<Cource> cources;
    public Integer getSid() {
        return sid;
    }
    public void setSid(Integer sid) {
        this.sid = sid;
    }
    public String getStu_name() {
        return stu_name;
    }
    public void setStu_name(String stu_name) {
        this.stu_name = stu_name;
    }
    public List<Cource> getCources() {
        return cources;
    }
    public void setCources(List<Cource> cources) {
        this.cources = cources;
    }
}

2.建立表(注意有中間表):

create table cource(
cid int primary key auto_increment,
cource_name varchar(20)

);
create table schoolChild(
sid int primary key auto_increment,
stu_name varchar(20)
);
create table cource_schoolChild(
cid int, 
sid int,
foreign key(cid) references cource(cid),
foreign key(sid) references schoolChild(sid)
);
insert into cource values(null,"python"),(null,'Java');
insert into schoolChild values(null,'張三'),(null,'李四'),(null,'王五');
insert into cource_schoolChild values(1,1),(1,2),(1,3),(2,1),(2,3);

3.建立接口和映射文件:

接口:

package work.pojo;

public interface UserMapper {
    public Cource[] getCource();
    public SchoolChild[] getSchoolChild();
}

映射文件:【難點是sql語句,其餘的resultMap與一對多的同樣】

<?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="work.pojo.UserMapper">
    <resultMap type="work.pojo.Cource" id="cource_schoolchild_map">
            <id property="cid" column="cid" />
            <result property="cource_name" column="cource_name" />
            <collection property="schoolchilds" ofType="work.pojo.SchoolChild">
                <id property="sid" column="sid" />
                <result  property="stu_name" column="stu_name" />
            </collection>
    </resultMap>
    <select id="getCource" resultMap="cource_schoolchild_map">
        select 
            t1.cid,t1.cource_name,
            t3.sid,t3.stu_name
        from cource t1 left join cource_schoolChild t2 on t1.cid=t2.cid left join schoolChild t3 on t2.sid=t3.sid;
    </select>
    
    <resultMap type="work.pojo.SchoolChild" id="schoolchild_cource_map">
            <id property="sid" column="sid" />
            <result property="stu_name" column="stu_name" />
            <collection property="cources" ofType="work.pojo.Cource">
                <id property="cid" column="cid" />
                <result  property="cource_name" column="cource_name" />
            </collection>
    </resultMap>
    <select id="getSchoolChild" resultMap="schoolchild_cource_map">
        select 
            t1.cid,t1.cource_name,
            t3.sid,t3.stu_name
        from cource t1 left join cource_schoolChild t2 on t1.cid=t2.cid left join schoolChild t3 on t2.sid=t3.sid;
    </select>
    
</mapper>

4.編寫測試方法:

@Test
    public void test8() throws IOException {
        SqlSessionFactoryBuilder sfb=new SqlSessionFactoryBuilder();
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sessionFactory = sfb.build(inputStream);
        SqlSession sqlSession = sessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        Cource[] cources = mapper.getCource();
        for (Cource cource : cources) {
            System.out.println(cource);
        }
        System.out.println("---分隔線---");
        SchoolChild[] schoolChilds = mapper.getSchoolChild();
        for (SchoolChild schoolChild : schoolChilds) {
            System.out.println(schoolChild);
        }

5.測試結果:

Cource [cid=1, cource_name=python, schoolchilds=[SchoolChild [sid=1, stu_name=張三, cources=null], SchoolChild [sid=2, stu_name=李四, cources=null], SchoolChild [sid=3, stu_name=王五, cources=null]]]
Cource [cid=2, cource_name=Java, schoolchilds=[SchoolChild [sid=1, stu_name=張三, cources=null], SchoolChild [sid=3, stu_name=王五, cources=null]]]
---分隔線---
SchoolChild [sid=1, stu_name=張三, cources=[Cource [cid=1, cource_name=python, schoolchilds=null], Cource [cid=2, cource_name=Java, schoolchilds=null]]]
SchoolChild [sid=2, stu_name=李四, cources=[Cource [cid=1, cource_name=python, schoolchilds=null]]]
SchoolChild [sid=3, stu_name=王五, cources=[Cource [cid=1, cource_name=python, schoolchilds=null], Cource [cid=2, cource_name=Java, schoolchilds=null]]]


使用說明:

  • 在上面的測試結果中,課程對象中查出的學生對象的課程對象是null,這是由於沒有封裝上,下面能夠提供一種方法來解決上面這個問題,但要注意--這個問題是解決不了的:這是由於MyBatis是面向SQL的,你定義了映射規則它才能幫你封裝,MyBatis對於對象與對象中嵌套對象的關係並不瞭解,它不知道怎麼作,只有你進行了封裝定義纔可以給嵌套對象封裝數據。若是你在resultMap的collection中嵌套一個collection就能夠對嵌套對象中的嵌套對象封裝數據,但嵌套對象中的嵌套對象的嵌套對象裏的對象仍是會空的。

    • <?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="work.pojo.UserMapper">
          <resultMap type="work.pojo.Cource" id="cource_schoolchild_map">
                  <id property="cid" column="cid" />
                  <result property="cource_name" column="cource_name" />
                  <collection property="schoolchilds" ofType="work.pojo.SchoolChild">
                      <id property="sid" column="sid" />
                      <result  property="stu_name" column="stu_name" />
                      <!-- 給嵌套對象裏面的嵌套對象封裝數據 -->
                      <collection property="cources" ofType="work.pojo.Cource">
                          <id property="cid" column="cid" />
                      <result  property="cource_name" column="cource_name" />
                  </collection>
                  </collection>
          </resultMap>
          <select id="getCource" resultMap="cource_schoolchild_map">
              select 
                  t1.cid,t1.cource_name,
                  t3.sid,t3.stu_name
              from cource t1 left join cource_schoolChild t2 on t1.cid=t2.cid left join schoolChild t3 on t2.sid=t3.sid;
          </select>
          <!-- 省略SchoolChild的。 -->
      </mapper>

關聯對象的插入問題

  • 若是你學過一些其餘的ORM框架,你可能會有點迷糊有關聯關係的對象怎麼插入到數據表。這裏提一下,MyBatis是面向SQL的,上面的一對多查詢本質上都是面向對象的,association和collection主要做用於Java對象,而不做用於數據庫,association和collection只是定義了返回的數據的封裝規則,從本質來看,SQL操做仍是join操做。
  • 因此,明白了MyBatis是面向SQL的以後,那麼怎麼插入有關聯關係的對象數據呢?像直接操做SQL那樣,插入記錄便可。一對多的,一般都是多一方維護外鍵,那麼直接在多一方的表插入帶外鍵的記錄便可,插入的時候獲取一下關聯對象的主鍵做爲外鍵值;多對多的,直接在中間表插入記錄,插入的時候獲取一下關聯對象的主鍵做爲中間表的外鍵值。
<mapper namespace="work.mapper.ProductMapper">
    <resultMap type="work.domain.Product" id="product_category">
        <id property="pid" column="pid"/>
        <result property="pname" column="pname"/>
        <result property="price" column="price"/>
        <result property="pimage" column="pimage"/>
        <result property="pdesc" column="pdesc"/>
        <!-- 這裏是多表查詢操做,這裏商品裏面有一個商品分類外鍵 -->
        <association property="category" javaType="work.domain.Category">
            <result property="category_name" column="category_name"/>
        </association>
    </resultMap>
    <select id="findAll" resultMap="product_category">
        select 
            p.pid,p.pname,p.price,p.pimage,p.pdesc,
            c.cid,c.category_name
         from product p left join category c on p.cid = c.cid;
    </select>
    
    <insert id="save" parameterType="work.domain.Product">
        insert into product(pname,price,pimage,pdesc,cid) values(
        #{pname},#{price},#{pimage},#{pdesc},#{category.cid}
        );
    </insert>
</mapper>


補充:

  • 不論一對1、一對多、多對多,都是兩種方式,但其實還有一種方式。創建獨立的查詢語句(關係模型中的每一個對象都有),經過在resultMap中配置,進行某個對象查詢的時候,調用查詢另外一個對象的SQL來傳入指定參數並封裝結果。【有興趣的自查。】



緩存

  • 一級緩存存放在sqlSession上,默認開啓。
  • 二級緩存存放在SqlSessionFactory上
  • 緩存的意義:
    • 一級緩存在sqlSession上,重複查詢時,不會重複發送SQL,會從緩存中獲取以前查詢到的數據。
    • 二級緩存是SqlSessionFactory層次上的緩存,它是面向命名空間,同一個命名空間(不一樣的SqlSession可使用同一個命名空間下的SQL)中使用重複的查詢時,不會重複發送SQL,會從緩存中獲取以前查詢到的數據。【二級緩存默認是不開啓的,並且涉及序列化內容,這裏不講。】


測試一級緩存的存在性:

  • 一級緩存是默認開啓的。
  • 嘗試重複查詢,若是僅僅發送一次SQL語句。那麼就能夠得出一級緩存是存在的【mybatis的運行信息輸出依賴log4j,因此要給一個log4j的配置文件】。
@Test
        public void test9() throws IOException {
            SqlSessionFactoryBuilder sfb=new SqlSessionFactoryBuilder();
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactory sessionFactory = sfb.build(inputStream);
            SqlSession sqlSession = sessionFactory.openSession();
            //根據接口.class來獲取mapper
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user = mapper.getUserById(3);
            User user2 = mapper.getUserById(3);
            System.out.println(user);
            System.out.println(user==user2);
            sqlSession.close();
        }


緩存的配置

在select\delete\insert標籤 中,有一個flushCache屬性:設置查詢以後是否刪除本地緩存和二級緩存。


補充:

  • 還能夠自定義緩存,而後在映射文件中使用cache引入,其餘映射文件再想引入時,能夠直接使用cache-ref。一般採用Redis這類數據庫來進行緩存。



mybatis整合第三方鏈接池


整合第三方鏈接池,固然要引入依賴包啦。這裏就很少講了,就是第三方鏈接池的依賴包。


首先須要定義一個數據源工廠,這個工廠要繼承UnpooledDataSourceFactory,在構造函數中返回須要使用的鏈接池對象

package work.dataSource;

import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
    public C3P0DataSourceFactory(){
        this.dataSource=new ComboPooledDataSource();
    }
}


而後在mybatis-config.xml中配置使用第三方鏈接池:

<!-- type裏填剛纔定義的鏈接池工廠的全限定名 -->
<dataSource type="work.dataSource.C3P0DataSourceFactory">
                <!-- 下面的屬性名要按照鏈接池自身的規則來配置,好比c3p0的驅動參數是driverClass -->
                <property name="driverClass" value="com.mysql.jdbc.Driver" />
                <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis2" />
                <property name="user" value="root" />
                <property name="password" value="123456" />
                <property name="idleConnectionTestPeriod" value="60" />
                <property name="maxPoolSize" value="20" />
                <property name="maxIdleTime" value="600" />
</dataSource>


補充:

  • 上面的演示採用的是c3p0的,dbcp的也同樣。
  • 除此以外,還有一種實現DataSourceFactory接口來整合鏈接池的方式,裏面有個setProperties能夠把mybatis-config.xml的鏈接池配置封裝到形參properties中,而後在getDataSource利用properties來新建目標鏈接池對象,返回的時候返回鏈接池對象;而後也須要在mybatis-config.xml中像上面同樣配置。【本來也想寫一下這個方式的,但不知道爲何dataSource.setProperties(properties)的時候沒法獲取到properties中的數據,打印過發現properties中是有數據的。】



寫在最後


如今沒有寫的內容:

  • mybatis的運行原理
  • 插件
    • 分頁插件
  • 存儲過程
  • 延遲加載


不寫,有興趣自查的內容:

  • 註解式mybatis:因爲mybatis使用註解來配置有幾點很是不方便的地方,通常都會使用XML來配置。
  • 二級緩存,自定義緩存。
相關文章
相關標籤/搜索