Mybatis從淺入深-Plus(IDEA版通俗易懂)

每一個成功者都有一個開始。勇於開始,才能找到成功的路。你好,我是夢陽辰!期待與你相遇!

Mybatis從淺入深(IDEA版通俗易懂)

01.使用註解開發

引入:
大家之前都學過面向對象編程,也學習過接口,但在真正的開發中,很多時候我們會選擇面向接口編程

根本原因∶解耦,可拓展,提高複用,分層開發中,上層不用管具體的實現,大家都遵守共同的標準,使得開發變得容易,規範性更好

在一個面向對象的系統中,系統的各種功能是由許許多多的不同對象協作完成的。在這種情況下,各個對象內部是如何實現自己的,對系統設計人員來講就不那麼重要了;

而各個對象之間的協作關係則成爲系統設計的關鍵。小到不同類之間的通信,大到各模塊之間的交互,在系統設計之初都是要着重考慮的,這也是系統設計的主要工作內容。面向接口編程就是指按照這種思想來編程。
關於接口的理解

接口從更深層次的理解,應是定義(規範,約束)與實現(名實分離的原則)的分離。-接口的本身反映了系統設計人員對系統的抽象理解。

接口應有兩類:

第一類是對一個個體的抽象,它可對應爲一個抽象體(abstract class);

第二類是對一個個體某一方面的抽象,即形成一個抽象面(interface) ;

一個體有可能有多個抽象面。抽象體與抽象面是有區別的。

三個面向的區別:

面向對象是指,我們考慮問題時,以對象爲單位,考慮它的屬性及方法.

面向過程是指,我們考慮問題時,以一個具體的流程(事務過程)爲單位,考慮它的實現.

接口設計與非接口設計是針對複用技術而言的,與面向對象(過程)不是一個問題.更多的體現就是對系統整體的架構

使用註解開發:
它們映射的語句可以不用 XML 來配置,而可以使用 Java 註解來配置。比如,上面的 XML 示例可以被替換成如下的配置:

package org.mybatis.example;public interface BlogMapper {
  @Select("SELECT * FROM blog WHERE id = #{id}")
  Blog selectBlog(int id);}

使用註解來映射簡單語句會使代碼顯得更加簡潔,但對於稍微複雜一點的語句,Java 註解不僅力不從心,還會讓你本就複雜的 SQL 語句更加混亂不堪。 因此,如果你需要做一些很複雜的操作,最好用 XML 來映射語句。

選擇何種方式來配置映射,以及認爲是否應該要統一映射語句定義的形式,完全取決於你和你的團隊。 換句話說,永遠不要拘泥於一種方式,你可以很輕鬆的在基於註解和 XML 的語句映射方式間自由移植和切換。

1.註解在接口上實現

**
 * 接口操作user表 */public interface UserMapper {

    @Select("select * from user")
   List<User> selectUser();}

2.需要在覈心配置文件中綁定接口。

<!--綁定接口-->
    <mappers>
        <mapper class="com.mengyangchen.dao.UserMapper"/>
    </mappers>

測試:

 @Test
    public void test(){
        //1.獲取SqlSession對象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //方式一:getMapper
        //2.執行sql,獲取sql,面向接口編程,獲取UserMapper接口(以前是實現接口的實現類,現在是配置文件)
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.selectUser();


        /*String sqlId = "com.mengyangchen.dao.UserDao"+"."+"selectUser";
        List<User> userList = sqlSession.selectList(sqlId);*/
        for(User user:userList){
            System.out.println(user);
        }

        //3.關閉SqlSession
        sqlSession.close();
    }

本質:反射機制實現

底層:動態代理。

Mybatis詳細的執行流程
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述



多debug,多看底層源碼。

註解完成增刪改查

接口:

 //方法存在多個參數,所有的參數前面必須加上@Param("id")註解,引用對象不需要寫
    @Select("select * from user where id=#{id}")
    User selectUserById(@Param("id") int id);

    @Insert("insert into user(id,name,pwd) values(#{id},#{name},#{password})")
    int insertUser(User user);

測試:

 @Test
    public void selectUserById(){
        //1.獲取SqlSession對象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //方式一:getMapper
        //2.執行sql,獲取sql,面向接口編程,獲取UserMapper接口(以前是實現接口的實現類,現在是配置文件)
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.selectUserById(1);
        System.out.println(user);
        sqlSession.close();
    }

    @Test
    public void insertUser(){
        //1.獲取SqlSession對象
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        //方式一:getMapper
        //2.執行sql,獲取sql,面向接口編程,獲取UserMapper接口(以前是實現接口的實現類,現在是配置文件)
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        int num = mapper.insertUser(new User(3,"meng","123456"));
        if(num>0){
            System.out.println("插入成功!");
        }
        sqlSession.commit();
        sqlSession.close();
    }

#{},${}的區別
#{}可以防止sql注入。

02.Lombok

Lombok項目是一個Java庫,它會自動插入您的編輯器和構建工具中,從而使您的Java更加生動有趣。
永遠不要再寫另一個getter或equals方法,帶有一個註釋的您的類有一個功能全面的生成器,自動化您的日誌記錄變量等等。

Lombok是一款Java開發插件,使得Java開發者可以通過其定義的一些註解來消除業務工程中冗長和繁瑣的代碼,尤其對於簡單的Java模型對象(POJO)。在開發環境中使用Lombok插件後,Java開發人員可以節省出重複構建,諸如hashCode和equals這樣的方法以及各種業務對象模型的accessor和ToString等方法的大量時間。對於這些方法,它能夠在編譯源代碼期間自動幫我們生成這些方法,並沒有如反射那樣降低程序的性能。

使用步驟:
1.在idea中安裝Lombok插件
打開setting
在這裏插入圖片描述


2.在項目中導入Lombokjar包

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
    <scope>provided</scope></dependency>

Lombok含有的註解:

@Getter and @Setter@FieldNameConstants@ToString@EqualsAndHashCode@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog@Data@Builder@SuperBuilder@Singular@Delegate@Value@Accessors@Wither@With@SneakyThrows@val@var

結果:

import lombok.Data;@Datapublic class User {
    private Integer id;
    private String name;
    private String password;
    }

在這裏插入圖片描述
自動幫我們寫了getter或equals等方法。

@Data:無參構造,get,set,toString,hashcode,equals

無參和所有參數:
在這裏插入圖片描述
注意:瞭解即可。

03.多對一處理

在這裏插入圖片描述
多個學生,對應一個老師。
對於學生而言:關聯…
多個學生關聯一個老師(多對一)
在這裏插入圖片描述



對於老師而言:
集合

一個老師,有很多學生(一對多)

多對一:
實體類

public class Student {
    private Integer id;
    private String name;

    //學生需要關聯一個老師
    private Teacher teacher;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", teacher=" + teacher +
                '}';
    }}
public class Teacher {
    private Integer id;
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }}

環境搭建:
1.新建實體類Teacher,Student
2.建立Mapper接口
3.建立Mapper.xml文件
4.在覈心配置文件中綁定註冊我們的mapper接口或者文件。
5.測試是否成功。




public interface TeacherMapper {
    @Select("select * from teacher where id=#{tid}")
    Teacher selectTeacherById(@Param("tid") int id);}
<mappers>
    <mapper class="com.mengyangchen.dao.TeacherMapper"/>
    <mapper class="com.mengyangchen.dao.StudentMapper"/></mappers>
public class MyTest {
    @Test
    public void test(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacher =  mapper.selectTeacherById(1);
        System.out.println(teacher);
        sqlSession.close();
    }}

在這裏插入圖片描述
嵌套查詢(按照查詢嵌套處理)(子查詢):
select語句的嵌套。
這裏測試的時候出現異常:
org.apache.ibatis.binding.bindingexception: invalid bound statement (not found)



問題出在:我在創建包的時候
在這裏插入圖片描述
採用的是:

com.mengyangchen.dao

改爲一個一個的創建即可。

<?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"><!--綁定一個對應的Dao/Mapper接口--><!--以前是寫一個類實現Dao接口,現在綁定即可--><mapper namespace="com.mengyangchen.dao.StudentMapper"><!--sql語句嵌套思路:    1.查詢所有的學生信息    2.根據查詢出來的學生的tid,尋找對應的老師-->
    <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="selectTeacherById"/>
    </resultMap><select id="selectStudentInfo" resultMap="studentTeacher">
   select * from student</select>

    <select id="selectTeacherById" resultType="teacher">
        select * from teacher where id=#{id}
    </select></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"><!--綁定一個對應的Dao/Mapper接口--><!--以前是寫一個類實現Dao接口,現在綁定即可--><mapper namespace="com.mengyangchen.dao.StudentMapper"><!--sql語句嵌套思路:    1.查詢所有的學生信息    2.根據查詢出來的學生的tid,尋找對應的老師-->
    <resultMap id="studentTeacher" type="student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!--複雜的屬性,我們需要單獨處理 對象association 集合collection-->
        <association property="teacher" column="tid" javaType="Teacher">
            <result property="name" column="teacherName"/>
        </association>
    </resultMap><select id="selectStudentInfo" resultMap="studentTeacher">
  select s.id  id, s.name  name,t.name  teacherName from student s inner join teacher t on s.tid = t.id</select></mapper>

測試:

 @Test
    public void studentTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> list =  mapper.selectStudentInfo();
        for (Student student:list) {
            System.out.println(student);
        }
        sqlSession.close();
    }

在這裏插入圖片描述

04.一對多處理

一個老師對應多個學生
對於老師而言就是一對多。
實體類:

public class Teacher {
    private Integer id;
    private String name;
    //一個老師有多個學生
    private List<Student> student;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Student> getStudent() {
        return student;
    }

    public void setStudent(List<Student> student) {
        this.student = student;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", student=" + student +
                '}';
    }}
public class Student {
    private Integer id;
    private String name;
    private Integer tid;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getTid() {
        return tid;
    }

    public void setTid(Integer tid) {
        this.tid = tid;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", tid=" + tid +
                '}';
    }}

接口:

public interface TeacherMapper {
    Teacher selectTeacherById(@Param("tid") int id);}

配置文件:

<?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"><!--綁定一個對應的Dao/Mapper接口--><!--以前是寫一個類實現Dao接口,現在綁定即可--><mapper namespace="com.mengyangchen.dao.TeacherMapper">

    <!--查詢語句-->
    <!--id相對於以前的書寫,表示重寫的方法名(以前在方法中執行連接數據庫等操作,現在不需要了,只需要執行sql-->
    <!--resultType返回結果-->

    <!--按照結果嵌套查詢--><select id="selectTeacherById" resultMap="teacherStudent">
     select s.id  id, s.name  name,t.name  teacherName from student s inner join teacher t on s.tid = t.id</select>

    <resultMap id="teacherStudent" type="teacher">
        <result property="name" column="teacherName"/>
        <collection property="student" ofType="Student">
            <result property="id" column="id"/>
            <result property="name" column="name"/>
        </collection>
    </resultMap></mapper>

在這裏插入圖片描述

05.動態sql

動態 SQL 是 MyBatis 的強大特性之一。如果你使用過 JDBC 或其它類似的框架,你應該能理解根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記添加必要的空格,還要注意去掉列表最後一個列名的逗號。利用動態 SQL,可以徹底擺脫這種痛苦。

如果你之前用過 JSTL 或任何基於類 XML 語言的文本處理器,你對動態 SQL 元素可能會感覺似曾相識。在 MyBatis 之前的版本中,需要花時間瞭解大量的元素。藉助功能強大的基於 OGNL 的表達式,MyBatis 3 替換了之前的大部分元素,大大精簡了元素種類,現在要學習的元素種類比原來的一半還要少。

ifchoose (when, otherwise)trim (where, set)foreach

使用動態 SQL 最常見情景是根據條件包含 where 子句的一部分。比如:

<select id="findActiveBlogWithTitleLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’  <if test="title != null">
    AND title like #{title}
  </if></select>

動態sql之if

實現根據不同條件查詢數據!

實體類:

package com.mengyangchen.pojo;import java.util.Date;public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;//屬性名和字段名不一致
    private Integer views;

    public Blog() {
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Integer getViews() {
        return views;
    }

    public void setViews(Integer views) {
        this.views = views;
    }

    @Override
    public String toString() {
        return "Blog{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", createTime=" + createTime +
                ", views=" + views +
                '}';
    }}

接口:

package com.mengyangchen.dao;import com.mengyangchen.pojo.Blog;import java.util.List;import java.util.Map;public interface BlogMapper {
    /*插入數據*/
    int insertBlog(Blog blog);

    /*查詢博客*/
    List<Blog> queryBlogIf(Map map);}

配置文件:

<?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="com.mengyangchen.dao.BlogMapper">
   <insert id="insertBlog" parameterType="blog">
       insert into mybatis.blog (id,title,author,create_time,views) values (#{id},#{title},#{author},#{createTime},#{views});
   </insert>
    
    <select id="queryBlogIf" parameterType="map" resultType="blog">
        select * from mybatis.blog where 1=1
        <if test="title !=null">
            and title like "%"#{title}"%"
        </if>

        <if test="author != null">
            and author like "%"#{author}"%"
        </if>
    </select></mapper>

屬性名和字段名不一致,一個是下劃線,一個爲駝峯。

在這裏插入圖片描述

<?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">
        <property name="username" value="root"/><!--一配置文件中爲準-->
        <property name="password" value="123456"/>
    </properties>

    <!--標準日誌實現-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--是否開啓自動駝峯命名規則映射-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <!--別名-->
    <typeAliases>
        <package name="com.mengyangchen.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <!--事務管理-->
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/><!--&amp是xml的轉義字符(即爲&)-->
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>

    </environments><mappers>
    <mapper class="com.mengyangchen.dao.BlogMapper"/></mappers></configuration>

動態sql之trim (where, set)

前面幾個例子已經方便地解決了一個臭名昭著的動態 SQL 問題。現在回到之前的 “if” 示例,這次我們將 “state = ‘ACTIVE’” 設置成動態條件,看看會發生什麼。

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE  <if test="state != null">
    state = #{state}
  </if>
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if></select>

如果沒有匹配的條件會怎麼樣?最終這條 SQL 會變成這樣:

SELECT * FROM BLOG
WHERE

這會導致查詢失敗。如果匹配的只是第二個條件又會怎樣?這條 SQL 會是這樣:

SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’

這個查詢也會失敗。這個問題不能簡單地用條件元素來解決。這個問題是如此的難以解決,以至於解決過的人不會再想碰到這種問題。

MyBatis 有一個簡單且適合大多數場景的解決辦法。而在其他場景中,可以對其進行自定義以符合需求。而這,只需要一處簡單的改動:

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where></select>

where 元素只會在子元素返回任何內容的情況下才插入 “WHERE” 子句。而且,若子句的開頭爲 “AND” 或 “OR”,where 元素也會將它們去除。
上個例子改完後:

<?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="com.mengyangchen.dao.BlogMapper">
   <insert id="insertBlog" parameterType="blog">
       insert into mybatis.blog (id,title,author,create_time,views) values (#{id},#{title},#{author},#{createTime},#{views});
   </insert>
    
    <select id="queryBlogIf" parameterType="map" resultType="blog">
        select * from mybatis.blog        <where>
            <if test="title !=null">
                title like "%"#{title}"%"
            </if>

            <if test="author != null">
                and author like "%"#{author}"%"
            </if>
        </where>

    </select>

   </mapper>

如果 where 元素與你期望的不太一樣,你也可以通過自定義 trim 元素來定製 where 元素的功能。比如,和 where 元素等價的自定義 trim 元素爲:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...</trim>

prefixOverrides 屬性會忽略通過管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子會移除所有 prefixOverrides 屬性中指定的內容,並且插入 prefix 屬性中指定的內容。

用於動態更新語句的類似解決方案叫做 set。set 元素可以用於動態包含需要更新的列,忽略其它不更新的列。比如:

解決了最後一個條件不需要,的問題。

<update id="updateAuthorIfNecessary">
  update Author    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}</update>

這個例子中,set 元素會動態地在行首插入 SET 關鍵字,並會刪掉額外的逗號(這些逗號是在使用條件語句給列賦值時引入的)。

來看看與 set 元素等價的自定義 trim 元素吧:

<trim prefix="SET" suffixOverrides=",">
  ...</trim>

動態sql之choose (when, otherwise)

有時候,我們不想使用所有的條件,而只是想從多個條件中選擇一個使用。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。
還是上面的例子,但是策略變爲:傳入了 “title” 就按 “title” 查找,傳入了 “author” 就按 “author” 查找的情形。若兩者都沒有傳入,就返回標記爲 featured 的 BLOG

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’  <choose>
    <when test="title != null">
     AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose></select>

與if的區別只會執行一個條件。
練習:

 <select id="queryBlogChoose" parameterType="map" resultType="blog">
        select * from 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>
 @Test
    public void queryBlogChoose(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        Map map = new HashMap();
        map.put("title","Mybatis你好!");
        map.put("author","夢陽辰");
        List<Blog> list = mapper.queryBlogChoose(map);
        for (Blog blog:list) {
            System.out.println(blog);
        }
    }

在這裏插入圖片描述

動態sql之foreach

在這裏插入圖片描述

06.sql片段

將一些通用的sql片段提取出來。實現sql代碼的複用。
在這裏插入圖片描述

07.緩存

1.什麼是緩存?
存在內存中的臨時數據。
將用戶經常查詢的數據放在緩存((內存)中,用戶去查詢數據就不用從磁盤上(關係型數據庫數據文件)查詢,從緩存中查詢,從而提高查詢效率,解決了高併發系統的性能問題。

2.爲什麼使用緩存?
減少和數據庫的交互次數,減少系統開銷,提高系統效率。

3.什麼樣的數據能使用緩存?
經常查詢並且不經常改變的數據。
在這裏插入圖片描述
Mybatis緩存


MyBatis包含一個非常強大的查詢緩存特性,它可以非常方便地定製和配置緩存。緩存可以極大的提升查詢效率。

MyBatis系統中默認定義了兩級緩存:一級緩存和二級緩存

默認情況下,只有一級緩存開啓。(SqlSession級別的緩存,也稱爲本地緩存)。

二級緩存需要手動開啓和配置,他是基於namespace級別的緩存。

爲了提高擴展性,MyBatis定義了緩存接口Cache。我們可以通過實現Cache接口來自定義二級緩存

一級緩存

MyBatis 內置了一個強大的事務性查詢緩存機制,它可以非常方便地配置和定製。 爲了使它更加強大而且易於配置,我們對 MyBatis 3 中的緩存實現進行了許多改進。
默認情況下,只啓用了本地的會話緩存,它僅僅對一個會話中的數據進行緩存。

一級緩存也叫本地緩存:SqlSession

與數據庫同一次會話期間查詢到的數據會放在本地緩存中。
以後如果需要獲取相同的數據,直接從緩存中拿,沒必須再去查詢數據庫;

測試步驟:
1.開啓日誌!

2.測試在一個Sesion中查詢兩次相同記錄

3.查看日誌輸出

在這裏插入圖片描述

映射語句文件中的所有 select 語句的結果將會被緩存。
映射語句文件中的所有 insert、update 和 delete 語句會刷新緩存
緩存會使用最近最少使用算法(LRU, Least Recently Used)算法來清除不需要的緩存。
緩存不會定時進行刷新(也就是說,沒有刷新間隔)。
緩存會保存列表或對象(無論查詢方法返回哪種)的 1024 個引用。
緩存會被視爲讀/寫緩存,這意味着獲取到的對象並不是共享的,可以安全地被調用者修改,而不干擾其他調用者或線程所做的潛在修改。




緩存失效的情況:
1.查詢不同的內容

2.增刪改操作,可能會改變原來的數據,所以會刷新緩存。

3.查詢不同的Mapper.xml

4.手動清除緩存。

sqlSession.clearCache();//手動清理緩存

一級緩存存在於sqlsession獲得到關閉,默認開啓(有效期在獲得連接到關閉連接)。

二級緩存

二級緩存也叫全局緩存,一級緩存作用域太低了,所以誕生了二級緩存·

基於namespace級別的緩存,一個名稱空間,對應一個二級緩存;

工作機制
一個會話查詢一條數據,這個數據就會被放在當前會話的一級緩存中;

如果當前會話關閉了,這個會話對應的一級緩存就沒了;但是我們想要的是,會話關閉了,一級緩存中的數據被保存到二級緩存中;

新的會話查詢信息,就可以從二級緩存中獲取內容;

不同的mapper查出的數據會放在自己對應的緩存(map)中;

默認情況下,只啓用了本地的會話緩存(一級緩存),它僅僅對一個會話中的數據進行緩存。 要啓用全局的二級緩存,只需要在你的 SQL 映射文件中添加一行:

<cache/>

緩存只作用於 cache 標籤所在的映射文件中的語句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的語句將不會被默認緩存。你需要使用 @CacheNamespaceRef 註解指定緩存作用域。

這些屬性可以通過 cache 元素的屬性來修改。比如:

<cache
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="true"/>

這個更高級的配置創建了一個 FIFO 緩存,每隔 60 秒刷新,最多可以存儲結果對象或列表的 512 個引用,而且返回的對象被認爲是隻讀的,因此對它們進行修改可能會在不同線程中的調用者產生衝突。

可用的清除策略有:

LRU – 最近最少使用:移除最長時間不被使用的對象。
FIFO – 先進先出:按對象進入緩存的順序來移除它們。
SOFT – 軟引用:基於垃圾回收器狀態和軟引用規則移除對象。
WEAK – 弱引用:更積極地基於垃圾收集器狀態和弱引用規則移除對象。
默認的清除策略是 LRU。



步驟:
1.開啓全局緩存(默認開啓)。
在這裏插入圖片描述
2.在要使用二級緩存的Mapper中開啓
在這裏插入圖片描述
3.問題:我們需要將實體類序列化!否則會報錯。




implements Serializable

只要開啓了二級緩存,在同一個Mapper下就有效。

所有的數據都會先放在一級緩存中。

只有當會話提交,或者關閉的時候,纔會提交到二級緩存中!

緩存的原理

在這裏插入圖片描述

自定義緩存

EhCache 是一個純Java的進程內緩存框架,具有快速、精幹等特點,是Hibernate中默認的CacheProvider。
1.導包

第三方緩存<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache --><dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.1.0</version></dependency>

除了上述自定義緩存的方式,你也可以通過實現你自己的緩存,或爲其他第三方緩存方案創建適配器,來完全覆蓋緩存行爲。

<cache type="com.domain.something.MyCustomCache"/>

這個示例展示瞭如何使用一個自定義的緩存實現。type 屬性指定的類必須實現 org.apache.ibatis.cache.Cache 接口,且提供一個接受 String 參數作爲 id 的構造器。 這個接口是 MyBatis 框架中許多複雜的接口之一,但是行爲卻非常簡單。

public interface Cache {
  String getId();
  int getSize();
  void putObject(Object key, Object value);
  Object getObject(Object key);
  boolean hasKey(Object key);
  Object removeObject(Object key);
  void clear();}

放假回家已經好幾天啦,感覺要學習的東西還很多。加油,下一篇spring見!

在這裏插入圖片描述


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章