1. 程式人生 > 實用技巧 >Mybatis 小談 (下篇)

Mybatis 小談 (下篇)

寫在前面:分享技術,共同進步,有不足請見諒,相關意見可評論告知 ~

程式設計路漫之遠兮,運架構體之帷幄;
勸君專注案前事,亦是杯酒敬蒼生;

目錄

多對一處理

測試環境搭建

  1. 匯入lombok

  2. 新建實體類 Teacher,Student

  3. 建立Mapper介面

  4. 建立Mapper.XML檔案

  5. 在核心配置檔案中繫結註冊我們的Mapper介面或者檔案!【方式很多,隨心選】

  6. 測試查詢是否能夠成功!

搭建圖解

原始資料庫SQL 操作:

按照查詢巢狀處理

解決兩個內容無法連線的問題(即有一個屬性為空)

<!--
    思路:
        1. 查詢所有的學生資訊
        2. 根據查詢出來的學生的tid,尋找對應的老師!  子查詢
    -->

<select id="getStudent" resultMap="StudentTeacher">
    select * from student
</select>

<resultMap id="StudentTeacher" type="Student">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
    <!--複雜的屬性,我們需要單獨處理 物件: association 集合: collection -->
    <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>

<select id="getTeacher" resultType="Teacher">
    select * from teacher where id = #{id}
</select>

按照結果巢狀處理(推薦)

<!--按照結果巢狀處理-->
<select id="getStudent2" resultMap="StudentTeacher2">
    select s.id sid,s.name sname,t.name tname
    from student s,teacher t
    where s.tid = t.id;
</select>

<resultMap id="StudentTeacher2" type="Student">
    <result property="id" column="sid"/>
    <result property="name" column="sname"/>
    <association property="teacher" javaType="Teacher">
        <result property="name" column="tname"/>
    </association>
</resultMap>

回顧Mysql 多對一查詢方式:

  • 子查詢
  • 聯表查詢

一對多處理

前序:一對多關係一般都由多端維護系統關係

環境搭建

同上

實體類

@Data
public class Student {

    private int id;
    private String name;
    private int tid;

}

@Data
public class Teacher {
    private int id;
    private String name;

    //一個老師擁有多個學生
    private List<Student> students;
}

按照結果巢狀處理

    <!--按結果巢狀查詢-->
    <select id="getTeacher" resultMap="TeacherStudent">
        select s.id sid, s.name sname, t.name tname,t.id tid
        from student s,teacher t
        where s.tid = t.id and t.id = #{tid}
    </select>

    <resultMap id="TeacherStudent" type="Teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!--複雜的屬性,我們需要單獨處理 物件: association 集合: collection
        javaType="" 指定屬性的型別!
        集合中的泛型資訊,我們使用ofType獲取
        -->
        <collection property="students" ofType="Student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>

按照查詢巢狀處理

<select id="getTeacher2" resultMap="TeacherStudent2">
    select * from mybatis.teacher where id = #{tid}
</select>

<resultMap id="TeacherStudent2" type="Teacher">
    <collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
</resultMap>

<select id="getStudentByTeacherId" resultType="Student">
    select * from mybatis.student where tid = #{tid}
</select>

小結

  1. 關聯 - association 【多對一】
  2. 集合 - collection 【一對多】
  3. javaType & ofType
    1. JavaType 用來指定實體類中屬性的型別
    2. ofType 用來指定對映到List或者集合中的 pojo型別,泛型中的約束型別!

注意點

  • 保證SQL的可讀性,儘量保證通俗易懂
  • 注意一對多和多對一中,屬性名和欄位的問題!
  • 如果問題不好排查錯誤,可以使用日誌 , 建議使用 Log4j

動態 SQL

什麼是動態SQL:動態SQL就是指根據不同的條件生成不同的SQL語句

動態 SQL 元素和 JSTL 或基於類似 XML 的文字處理器相似MyBatis 採用功能強大的基於 OGNL 的表示式來淘汰其它大部分元素。



if
choose (when, otherwise)
trim (where, set)
foreach



搭建環境

CREATE TABLE `blog` (
  `id` varchar(50) NOT NULL COMMENT '部落格id',
  `title` varchar(100) NOT NULL COMMENT '部落格標題',
  `author` varchar(30) NOT NULL COMMENT '部落格作者',
  `create_time` datetime NOT NULL COMMENT '建立時間',
  `views` int(30) NOT NULL COMMENT '瀏覽量'
) ENGINE=InnoDB DEFAULT CHARSET=utf8

建立一個基礎工程

  1. 導包

  2. 編寫配置檔案

  3. 編寫實體類

    @Data
    public class Blog {
        private int id;
        private String title;
        private String author;
        private Date createTime;
        private int views;
        
        
    }
    
  4. 編寫實體類對應Mapper介面 和 Mapper.XML檔案

Tips:解決Idea拼寫不符合規範問題(波浪線提示)

@SuppressWarnings("all")

編寫隨機ID工具類


配置資料庫欄位名與實體類中的相容問題


利用駝峰式命名解決

IF

<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from mybatis.blog where 1=1
    <if test="title != null">
        and title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</select>

choose (when, otherwise)

    <select id="queryBlogChoose" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
            <choose>
                <when test="title != null">
                    title = #{title}
                </when>
                <when test="author != null">
                    and author = #{author}
                </when>
                <otherwise>
                    and views = #{views}
                </otherwise>
            </choose>
        </where>
    </select>

類似於 switch....case

trim (where,set)

select * from mybatis.blog
<where>
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</where>
<update id="updateBlog" parameterType="map">
    update mybatis.blog
    <set>
        <if test="title != null">
            title = #{title},
        </if>
        <if test="author != null">
            author = #{author}
        </if>
    </set>
    where id = #{id}
</update>

所謂的動態SQL,本質還是SQL語句 , 只是我們可以在SQL層面,去執行一個邏輯程式碼

if

where , set , choose ,when

關於動態SQL的參考:
官方文件
文件
部落格

Foreach

遍歷集合等

select * from user where 1=1 and 

  <foreach item="id" collection="ids"
      open="(" separator="or" close=")">
        #{id}
  </foreach>

(id=1 or id=2 or id=3)

<!--
        select * from mybatis.blog where 1=1 and (id=1 or id = 2 or id=3)

        我們現在傳遞一個萬能的map , 這map中可以存在一個集合!
-->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from mybatis.blog

    <where>
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
            id = #{id}
        </foreach>
    </where>

</select>

動態SQL就是在拼接SQL語句,我們只要保證SQL的正確性,按照SQL的格式,去排列組合就可以了

SQL片段

功能的部分抽取出來,方便複用

  1. 使用SQL標籤抽取公共的部分

    <sql id="if-title-author">
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </sql>
    
  2. 在需要使用的地方使用Include標籤引用即可

    <select id="queryBlogIF" parameterType="map" resultType="blog">
        select * from mybatis.blog
        <where>
            <include refid="if-title-author"></include>
        </where>
    </select>
    

注意事項:

  • 最好基於單表來定義SQL片段!
  • 不要存在where標籤

快取

簡介

快取 [ Cache ]

存在記憶體中的臨時資料,將使用者經常查詢的資料放在快取(記憶體)中,使用者去查詢資料就不用從磁碟上(關係型資料庫資料檔案)查詢,從快取中查詢,從而提高查詢效率,解決高併發系統的效能問題。以減少和資料庫的互動次數,減少系統開銷,提高系統效率。

Mybatis快取

系統定義兩級快取:一級快取二級快取

  • 預設情況下,只有一級快取開啟。(SqlSession級別的快取,也稱為本地快取)

  • 二級快取需要手動開啟和配置,他是基於namespace級別的快取。

  • 為了提高擴充套件性,MyBatis定義了快取介面Cache。我們可以通過實現Cache介面來自定義二級快取

一級快取

又名本地快取: SqlSession
與資料庫同一次會話期間查詢到的資料會放在本地快取中, 以後如果需要獲取相同的資料,直接從快取中拿,沒必須再去查詢資料庫;

測試步驟:

  1. 開啟日誌!
  2. 測試在一個Sesion中查詢兩次相同記錄
  3. 檢視日誌輸出

快取失效的情況:

  1. 查詢不同的東西

  2. 增刪改操作,可能會改變原來的資料,所以必定會重新整理快取!
    3.手動清除快取

sqlSession.clearCache() 

小結:一級快取預設是開啟的,只在一次SqlSession中有效,也就是拿到連線到關閉連線這個區間段!一級快取就是一個Map。

二級快取

  • 二級快取也叫全域性快取,一級快取作用域太低了,所以誕生了二級快取
  • 基於namespace級別的快取,一個名稱空間,對應一個二級快取;
  • 工作機制
    • 一個會話查詢一條資料,這個資料就會被放在當前會話的一級快取中;
    • 如果當前會話關閉了,這個會話對應的一級快取就沒了;但是我們想要的是,會話關閉了,一級快取中的資料被儲存到二級快取中;
    • 新的會話查詢資訊,就可以從二級快取中獲取內容;
    • 不同的mapper查出的資料會放在自己對應的快取(map)中;

步驟:

  1. 開啟全域性快取

    <!--顯示的開啟全域性快取-->
    <setting name="cacheEnabled" value="true"/>
    

  1. 在要使用二級快取的Mapper中開啟

    <!--在當前Mapper.xml中使用二級快取-->
    <cache/>
    

    也可以自定義引數

    <!--在當前Mapper.xml中使用二級快取-->
    <cache  eviction="FIFO"
           flushInterval="60000"
           size="512"
           readOnly="true"/>
    

  1. 測試

    1. 問題:我們需要將實體類序列化!否則就會報錯!

      Caused by: java.io.NotSerializableException: com.kuang.pojo.User
      

小結:

  • 只要開啟了二級快取,在同一個Mapper下就有效
  • 所有的資料都會先放在一級快取中;
  • 只有當會話提交,或者關閉的時候,才會提交到二級緩衝中!

快取原理

Redis資料庫來做快取 K-V

常見異常及錯誤

①關於無法new java類錯誤

現象:只有file,沒有java class 如下圖

解法:因為該不在可編譯的目錄下,可在 Project Structure 中進行設定

②關於Test測試

(1)下圖情況

方法錯誤(即寫的不是方法 eg: 少了方法的括號)

(2)沒有匯入包 import org.junit.Test;

③關於資料庫查詢xml檔案提示

(1)波浪線為正常
(2)裡面包含的有些SQL語句是不加 ;

④Mapper介面中未建立方法

下圖為在測試程式碼中

解法如下圖:

⑤resultType 與resultMap

Caused by: org.apache.ibatis.type.TypeException: Could not resolve type alias 'Student'. Cause: java.lang.ClassNotFoundException: Cannot find class: Student

原因:將resultMap寫成了resultType,Type對應的是物件類,所以丟擲ClassNotFoundException的異常,mybatis的結果是存放在resultMap中的。

⑥類找不到異常(*)

Could not resolve type alias 'Student'. Cause: java.lang.ClassNotFoundException: Cannot find class: Student
原因分析:找不到類替代的別名,在mybatis-config.xml,漏寫typeAliases
解決:

基於實戰中學習,學習快樂中成長
.
時間會回答成長,成長會回答夢想