MyBatis技術(shù)總結(jié)

1.什么是MyBatis?

MyBatis 是一款優(yōu)秀的持久層框架脸狸,它支持自定義 SQL最仑、存儲過程以及高級映射。MyBatis 免除了幾乎所有的 JDBC 代碼以及設置參數(shù)和獲取結(jié)果集的工作炊甲。MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型泥彤、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為數(shù)據(jù)庫中的記錄卿啡。

2.MyBatis中的主要組成部分

  • SqlSessionFactoryBuilder( 工廠構(gòu)造器 ) :根據(jù)配置文件來生成SqlSessionFactory
    這個類可以被實例化吟吝、使用和丟棄,一旦創(chuàng)建了 SqlSessionFactory颈娜,就不再需要它了剑逃。 因此SqlSessionFactoryBuilder 實例的最佳作用域是方法作用域(也就是局部方法變量)。 你可以重用SqlSessionFactoryBuilder 來創(chuàng)建多個 SqlSessionFactory 實例揭鳞,但最好還是不要一直保留著它炕贵,以保證所有的 XML 解析資源可以被釋放給更重要的事情。
    SqlSessionFactoryBuilder創(chuàng)建代碼示例:

    SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
    
  • SqlSessionFactory(SqlSession工廠):創(chuàng)建SqlSession野崇。
    SqlSessionFactory 一旦被創(chuàng)建就應該在應用的運行期間一直存在称开,沒有任何理由丟棄它或重新創(chuàng)建另一個實例。 使用 SqlSessionFactory 的最佳實踐是在應用運行期間不要重復創(chuàng)建多次乓梨,多次重建 SqlSessionFactory被視為一種代碼“壞習慣”鳖轰。因此 SqlSessionFactory 的最佳作用域是應用作用域。 有很多方法可以做到扶镀,最簡單的就是使用單例模式或者靜態(tài)單例模式蕴侣。
    SqlSessionFactory創(chuàng)建代碼示例:

    InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    
  • SqlSession(會話):執(zhí)行SQL并返回結(jié)果。
    每個線程都應該有它自己的 SqlSession 實例臭觉。SqlSession 的實例不是線程安全的昆雀,因此是不能被共享的辱志,所以它的最佳的作用域是請求或方法作用域。 絕對不能將 SqlSession 實例的引用放在一個類的靜態(tài)域狞膘,甚至一個類的實例變量也不行揩懒。 也絕不能將 SqlSession 實例的引用放在任何類型的托管作用域中,比如 Servlet 框架中的 HttpSession挽封。 如果你現(xiàn)在正在使用一種 Web 框架已球,考慮將 SqlSession 放在一個和 HTTP 請求相似的作用域中。 換句話說辅愿,每次收到 HTTP 請求智亮,就可以打開一個 SqlSession,返回一個響應后点待,就關(guān)閉它阔蛉。這個關(guān)閉操作很重要,為了確保每次都能執(zhí)行關(guān)閉操作亦鳞,你應該把這個關(guān)閉操作放到 finally 塊中馍忽。 下面的示例就是一個確保 SqlSession 關(guān)閉的標準模式:
    SqlSession創(chuàng)建代碼示例:

    try (SqlSession session = sqlSessionFactory.openSession()) {
    // 應用邏輯代碼
    }
    
  • SQL Mapper:由一個Java接口和XML文件(或注解)構(gòu)成棒坏。接口負責定義數(shù)據(jù)訪問接口燕差,
    XML文件(或注解)負責定義SQL和映射規(guī)則。

    映射器是一些綁定映射語句的接口坝冕。映射器接口的實例是從 SqlSession 中獲得的徒探。雖然從技術(shù)層面上來講,任何映射器實例的最大作用域與請求它們的 SqlSession 相同喂窟。但方法作用域才是映射器實例的最合適的作用域测暗。 也就是說,映射器實例應該在調(diào)用它們的方法中被獲取磨澡,使用完畢之后即可丟棄碗啄。 映射器實例并不需要被顯式地關(guān)閉。盡管在整個請求作用域保留映射器實例不會有什么問題稳摄,但是你很快會發(fā)現(xiàn)稚字,在這個作用域上管理太多像 SqlSession 的資源會讓你忙不過來。 因此厦酬,最好將映射器放在方法作用域內(nèi)胆描。就像下面的例子一樣:
    代碼示例:

    try (SqlSession session = sqlSessionFactory.openSession()) {
          BlogMapper mapper = session.getMapper(BlogMapper.class);
         // 你的應用邏輯代碼
    }
    

3.使用單例設計模式(懶漢式)設計一個用于生成SqlSession的MyBatis工具類

代碼實現(xiàn):

import java.io.IOException;
import java.io.InputStream;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

public class MyBatisUtils {
    //靜態(tài)字符串保存主配置文件的路徑
    private static final String CONFIG = "mybatis-config.xml";
    //聲明靜態(tài)SqlSessionFactory確保一個程序中只會有一個工廠類
    private static SqlSessionFactory factory;
    //創(chuàng)建同步鎖確保線程安全
    private static final Class<MyBatisUtils02> CLASS_LOCK = MyBatisUtils02.class;
    //靜態(tài)代碼塊創(chuàng)建工廠類
    static {
        factory = initSqlSessionFactory();
    }
    private MyBatisUtils02() {
    }
    /**
     * 返回創(chuàng)建的工廠
     * @return SqlSessionFactory對象
     */
    private static SqlSessionFactory initSqlSessionFactory() {
        if(factory == null) {
            //加同步鎖確保只會創(chuàng)建一個工廠。
            synchronized (CLASS_LOCK) {
                try (InputStream in = Resources.getResourceAsStream(CONFIG)){
                    factory = new SqlSessionFactoryBuilder().build(in);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return factory;
    }
    /**
     * @return 一個個SqlSession對象
     */
    public static SqlSession openSqlSession() {
        if(factory == null) {
            initSqlSessionFactory();
        }
        return factory.openSession();
    }
}

4.MyBatis配置

  • 引入外部的properties文件

主要作用:加載外部的properties文件
引入文件(properties)代碼示例:

<properties resource="jdbc.properties"/>

properties文件中的內(nèi)容:
driver_class = com.mysql.cj.jdbc.Driver
jdbc_url = jdbc:mysql://服務器IP地址/數(shù)據(jù)庫名稱?useSSL=false&serverTimezone=UCT
db_username = 用戶賬號
db_password = 用戶密碼

  • 設置(settings)

主要作用:MyBatis框架運行規(guī)則配置
設置(settings)代碼示例:

<settings>
  <!-- 開啟日志 -->
  <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
  • 類型別名(typeAliases)

主要作用:設置別名(為實體類設置別名
類型別名(typeAliases)代碼示例:

<typeAliases>
<!-- 使用 typeAlias 將實體類進行單一的重命名-->
      <typeAlias type="com.apesource.entity.Employee"alias="EmployeeMapper" />
  <!-- 設置實體類包仗阅,為該package中的每個實體類自動設置別名 -->
  <package name="com.apesource.entity"/>
</typeAliases>
  • 環(huán)境配置(environments)

主要作用:配置當前的環(huán)境(開發(fā)環(huán)境昌讲,測試環(huán)境等等),數(shù)據(jù)庫連接信息减噪。
環(huán)境配置(environments)代碼示例:

<environments default="development">
  <!-- 環(huán)境1:開發(fā)環(huán)境 -->
  <environment id="development">
      <!-- 使用JDBC的事物管理機制 -->
      <transactionManager type="JDBC" />
      <!-- 啟用數(shù)據(jù)庫連接池 -->
      <dataSource type="POOLED">
          <!-- 如果加載了外部的 Properties 文件則可以直接使用這樣方式賦值數(shù)據(jù)庫參數(shù)  -->
          <!-- 注意value大括號中的內(nèi)容和 Properties 文件中的鍵對應-->
          <property name="driver" value="${driver_class}" />
          <property name="url" value="${jdbc_url}" />
          <property name="username" value="${db_username}" />
          <property name="password" value="${db_password}" />
      </dataSource>
  </environment>
</environments>
  • 映射器(mappers)

主要作用:傳遞映射器的地址短绸。

映射器(mappers)代碼示例:

<mappers>
   <!-- 映射XML文件 -->
  <mapper resource="com/apesource/dao/mapper/EmployeeMapper.xml" />
   <!-- 映射接口 -->
  <mapper class="com.apesource.dao.mapper.OrderMapper" />
</mappers>

5.MyBatis XML 映射器

1. 常用節(jié)點作用總結(jié)

  • select :用SQL語句查詢數(shù)據(jù)庫中的數(shù)據(jù)车吹。
  • update:用SQL語句修改數(shù)據(jù)庫中的數(shù)據(jù)。
  • delete:用SQL語句刪除數(shù)據(jù)庫中的數(shù)據(jù)醋闭。
  • insert:用SQL語句向數(shù)據(jù)庫添加數(shù)據(jù)礼搁。

2.常用屬性作用總結(jié)

  • namespace屬性:mapper節(jié)點中對應映射器接口的完全限定名。
  • id 屬性:接口中的方法名目尖。
  • resultType 屬性:該方法的返回類型馒吴。
  • parameterType 屬性:該方法的返回參數(shù)類型。
  • useGeneratedKeys屬性:是否開啟主鍵回填瑟曲。
  • keyProperty屬性:主鍵回填后保存的位置饮戳。

3.常見SQL映射示例

示例1:普通增加

<insert id="insertAnswerRecord" parameterType="AnswerRecord" useGeneratedKeys="true" keyProperty="recordId">
  INSERT INTO answer_record(respondent,question,right_answer,submit_answer,submit_datetime)
  VALUES(#{respondent},#{question},#{rightAnswer},#{submitAnswer},#{submitDatetime})
</insert>
接口方法定義:int insertAnswerRecord(AnswerRecord answerRecord);

示例2:批量增加

<!-- foreach 用于動態(tài)SQL的循環(huán)遍歷,Collection 指定遍歷的集合類型洞拨,item為每次循環(huán)遍歷的元素命名 -->
<insert id="insertAnswerRecordBatch" parameterType="list">
  INSERT INTO answer_record(respondent,question,right_answer,submit_answer,submit_datetime)
  VALUES
  <foreach collection="list" item="record" separator=",">
      (
          #{record.respondent},
          #{record.question},
          #{record.rightAnswer},
          #{record.submitAnswer},
          now()
      )
  </foreach>
</insert>
接口方法定義:int insertAnswerRecordBatch(List<AnswerRecord> answerRecordsList);

示例3:普通刪除

<delete id="deleteAnswerRecord">
  DELETE FROM answer_record WHERE record_id LIKE #{recordId}
</delete>
接口方法定義:int deleteAnswerRecord(int recordId);

示例4:批量刪除

<!-- 批量刪除答題記錄 -->
<update id="deleteAnswerRecordBatch" parameterType="list">
  DELETE FROM answer_record WHERE record_id IN 
  <foreach collection="list" item="rid" open="(" close=")" separator=",">
      #{rid}
  </foreach>
</update>
接口方法定義:int deleteAnswerRecordBatch(List<Integer> count);

示例5:動態(tài)修改

<update id="updateAnswerRecord" parameterType="AnswerRecord">
  UPDATE answer_record
  <set>
      <if test="respondent!=null">respondent = #{respondent},</if>
      <if test="question!=null">question = #{question},</if>
      <if test="rightAnswer!=null">right_answer = #{rightAnswer},</if>
      <if test="submitAnswer!=null">submit_answer = #{submitAnswer},</if>
      submit_datetime = now()
  </set>
  WHERE record_id = #{recordId}
</update>
接口方法定義:int updateAnswerRecord(AnswerRecord answerRecord);

示例6:動態(tài)查詢

<select id="listAnswerRecordByCondition" resultType="AnswerRecord" parameterType="AnswerRecord">
  SELECT  record_id as recordId,
          respondent as respondent,
          question as question,
          right_answer as rightAnswer,
          submit_answer as submitAnswer,
          submit_datetime as submitDatetime
  FROM answer_record
  <where>
      <if test="respondent!=null">AND respondent = #{respondent}</if>
      <if test="question!=null">AND question LIKE concat('%',#{question},'%')</if>
      <if test="rightAnswer!=null">AND right_answer = #{rightAnswer}</if>
      <if test="submitAnswer!=null">AND submit_answer = #{submitAnswer}</if>
  </where>
</select>
接口方法定義:List<AnswerRecord> listAnswerRecordByCondition(AnswerRecord answerRecord);

示例7:查詢結(jié)果封裝為Map

<select id="countAnswerRecordDataByRespondent" resultType="map">
  SELECT  COUNT(record_id) AS sum,
          (SELECT COUNT(record_id) FROM answer_record 
          WHERE right_answer = submit_answer AND respondent = #{respondent}) AS correct,
          (SELECT COUNT(record_id) FROM answer_record 
          WHERE right_answer != submit_answer AND respondent = #{respondent}) AS total 
  FROM answer_record
  WHERE respondent = #{respondent}
</select>
接口方法定義:Map<String,Integer> countAnswerRecordDataByRespondent(String respondent);

6.MyBatis的緩存機制

  • Mybatis的一級緩存是SqlSession
  • MyBatis的二級緩存是SqlSessionFactory

如何設置二級緩存
主配置文件配置

<!-- MyBatis框架運行規(guī)則配置 -->
<settings>
    <!-- 開啟二級緩存 -->
    <setting name="cacheEnabled" value="true"/>
</settings>

mapper映射文件配置

<!--
    eviction:代表的是緩存回收策略扯罐,目前MyBatis提供以下策略。
        (1)LRU烦衣,最近最少使用的歹河,回收最長時間不用的對象 
        (2)FIFO,先進先出花吟,按對象進入緩存的順序來移除他們
        (3)SOFT秸歧,軟引用,移除基于垃圾回收器狀態(tài)和軟引用規(guī)則對象
        (4)WEAK衅澈,弱引用键菱,更積極的移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對象。
    這里采用的是LRU今布,移除最長時間不用的對象
        
    flushInterval:刷新間隔時間经备,單位為毫秒,這里配置的是100秒刷新部默;
                 如果沒有配置侵蒙,當SQL被執(zhí)行的時候才會去刷新緩存。
                         
    size:引用數(shù)目傅蹂,一個正整數(shù)纷闺,代表緩存最多可以存儲多少個對象,不宜設置過大贬派。
        設置過大會導致內(nèi)存溢出急但。這里配置的是1024個對象
        
    readOnly:只讀,意味著緩存數(shù)據(jù)只能讀取而不能修改搞乏,這樣設置的好處是我們可以快速讀取緩存波桩,
               缺點是我們沒辦法修改緩存,他的默認值是false请敦,不允許我們修改镐躲。
-->
 <!-- cache節(jié)點:配置當前命名空間下的mapper的緩存配置 -->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024" />
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末储玫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子萤皂,更是在濱河造成了極大的恐慌撒穷,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件裆熙,死亡現(xiàn)場離奇詭異端礼,居然都是意外死亡,警方通過查閱死者的電腦和手機入录,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門蛤奥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人僚稿,你說我怎么就攤上這事凡桥。” “怎么了蚀同?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵缅刽,是天一觀的道長。 經(jīng)常有香客問我蠢络,道長衰猛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任谢肾,我火速辦了婚禮腕侄,結(jié)果婚禮上小泉,老公的妹妹穿的比我還像新娘芦疏。我一直安慰自己,他們只是感情好微姊,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布酸茴。 她就那樣靜靜地躺著,像睡著了一般兢交。 火紅的嫁衣襯著肌膚如雪薪捍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天配喳,我揣著相機與錄音酪穿,去河邊找鬼。 笑死晴裹,一個胖子當著我的面吹牛被济,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播涧团,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼只磷,長吁一口氣:“原來是場噩夢啊……” “哼经磅!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起钮追,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤预厌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后元媚,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體轧叽,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年刊棕,在試婚紗的時候發(fā)現(xiàn)自己被綠了犹芹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鞠绰,死狀恐怖腰埂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蜈膨,我是刑警寧澤屿笼,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站翁巍,受9級特大地震影響驴一,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜灶壶,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一肝断、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧驰凛,春花似錦胸懈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至胚宦,卻和暖如春首有,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背枢劝。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工井联, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人您旁。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓烙常,卻偏偏與公主長得像,于是被迫代替她去往敵國和親被冒。 傳聞我的和親對象是個殘疾皇子军掂,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355