Mybatis-06 動態Sql

zc發表於2021-02-21

Mybatis-06 動態Sql

多對一處理

多個學生,對應一個老師

對於學生這邊而言,關聯多個學生,關聯一個老師 【多對一】

對於老師而言,集合,一個老師又很多學生 【一對多】

1.建立資料庫

1 2

2.建立實體類

@Data
@NoArgsConstructor
@AllArgsConstructor
public class teacher {

    private int id;
    private String name;

}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class student {
    
    private int id;
    private String name;
    private teacher teacher;

}

3.介面類

public interface StudentMapper {

    public List<student> getStudent();

}

4.Mapper.xml檔案

思路:

  1. 查詢出所有學生
  2. 根據tid查詢其對應老師

複雜的物件就用associationcollection

物件:association 集合:collection

4.1 按照查詢巢狀處理

<mapper namespace="com.Dao.StudentMapper">
    <resultMap id="stutea" type="pojo.student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <association property="teacher" column="tid" javaType="pojo.teacher" select="getTeacher"/>
    </resultMap>
     <select id="getStudent" resultMap="stutea">
         select * from mybatistest.stu
     </select>
    <select id="getTeacher" resultType="pojo.teacher">
         select * from mybatistest.teacher where id = #{id}
    </select>
</mapper>

4.2 按照結果巢狀處理

<mapper namespace="com.Dao.StudentMapper">
    <select id="getStudent" resultMap="studentTeacher2">
            select s.id,s.name,t.name
            from mybatistest.stu s,mybatistest.teacher t
            where s.tid=t.id
    </select>
    <resultMap id="studentTeacher2" type="pojo.student">
            <result property="id" column="id"/>
            <result property="name" column="name"/>
            <association property="teacher" javaType="pojo.teacher">
                <result property="name" column="name"/>
            </association>
    </resultMap>
</mapper>    

5.測試

 @Test
    public void getStudent(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<student> student = mapper.getStudent();
        for (pojo.student student1 : student) {
            System.out.println(student1);
        }
        sqlSession.close();
    }
3 4

一對多處理

資料庫不變

1.建立實體類

@Data
@NoArgsConstructor
@AllArgsConstructor
public class teacher {

    private int id;
    private String name;
    private List<student> students;

}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class student {

    private int id;
    private String name;
   // private teacher teacher;
    private int tid;

}

2.介面類

public interface TeacherMapper {

     public teacher getTeacher(@Param("tid") int id);

}

3.Mapper.xml檔案

3.1 按照查詢巢狀處理

<mapper namespace="com.Dao.TeacherMapper">
    
    <select id="getTeacher" resultMap="geTeacher" >
        select * from mybatistest.teacher where id = #{tid}
    </select>
    <resultMap id="geTeacher" type="pojo.teacher">
        <collection property="students" javaType="ArrayList" ofType="pojo.student" select="getStudent" column="id"></collection>
    </resultMap>
    <select id="getStudent" resultType="pojo.student">
        select * from mybatistest.stu where tid = #{tid}
    </select>
    
</mapper>

3.2 按照結果巢狀處理

<mapper namespace="com.Dao.TeacherMapper">

    <select id="getTeacher" resultMap="teacherStudent">
        select t.id tid,t.name tname,s.id sid,s.name sname
        from mybatistest.stu s,mybatistest.teacher t
        where s.tid=t.id and t.id=#{tid}
    </select>
    <resultMap id="teacherStudent" type="pojo.teacher">
        <result property="id" column="tid"/>
        <result property="name" column="tname"/>
        <collection property="students" ofType="pojo.student">
            <result property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>

</mapper>

4.測試

 @Test
public void getTeacher(){
      SqlSession sqlSession = mybatis_util.getSqlSession1();
      TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
      teacher teacher = mapper.getTeacher(1);
      System.out.println(teacher);
      sqlSession.close();
}
5 6

ofType & javaType

  1. javaType用來指定實體類中屬性
  2. ofTyoe用來指定對映到List或者集合中pojo型別,泛型中的約束型別

注意點:注意一對多和多對一中,屬性名和欄位的問題

動態sql

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

  • If
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

1.基礎準備

1.1 建立資料庫

CREATE TABLE `blog`(
`id` INT(10) NOT NULL COMMENT '部落格id',
`title` VARCHAR(20) NOT NULL COMMENT '部落格標題',
`author` VARCHAR(10) NOT NULL COMMENT '作者',
`create_time` DATETIME NOT NULL COMMENT '建立時間',
`views` INT(20) NOT NULL COMMENT '瀏覽量'
)ENGINE=INNODB CHARSET=utf8;
7

1.2 建立實體類

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Blog {
    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
}

1.3 建立介面類

public interface BlogMapper {

    public int addBlog(Blog blog);

}

1.4 建立Mapper.xml檔案

<mapper namespace="com.Dao.BlogMapper">
    <insert id="addBlog" parameterType="pojo.Blog">
        insert into mybatistest.blog(id,title,author,create_time,views)
        values (#{id},#{title},#{author},#{createTime},#{views})
    </insert>
</mapper>

1.5 測試程式碼

  @Test
    public void Test(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        Blog blog = new Blog(1, "title", "張", new Date(), 11);
        int i = mapper.addBlog(blog);
        System.out.println(i);
    }

2.IF

介面

public interface BlogMapper {

   public List<Blog> queryBlogIF(Map map);

}

對映檔案

<mapper namespace="com.Dao.BlogMapper">
    
    <select id="queryBlogIF" parameterType="map" resultType="pojo.Blog">
        select * from mybatistest.blog where 1=1
        <if test="views != null">
            and views > #{views}
        </if>
        <if test="author != null">
            and author=#{author}
        </if>
        <if test="title != null">
            and title like #{title}
        </if>
    </select>

</mapper>

測試

   @Test
    public void queryBlogIF(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("views",10);
        List<Blog> blogs = mapper.queryBlogIF(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
    }
8

注意:

  • 未繫結mapper

在配置檔案中繫結:

<mappers>
   <mapper class="com.Dao.BlogMapper"/>
</mappers>
  • createTime資料為null

這是因為在實體類中,資料庫中定義時間屬性為:create_time,有_

可以開啟駝峰命名法對映,在配置檔案中加入:

  <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
  </settings>

在資料庫欄位命名規範中常常用下劃線 "_" 對單詞進行連線,如:"create_time",而開發中實體屬性通常會採用駝峰命名法命名為 createTime

3.choose (when, otherwise)

介面

public interface BlogMapper {

   public List<Blog> queryBlogChoose(Map map);

}

對映檔案

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

測試

    @Test
    public void queryBlogChoose(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("title","%啦%");
        List<Blog> blogs = mapper.queryBlogChoose(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
    }
9

4.trim (where, set)

where元素只會在子元素返回任何內容的情況下才插入 “WHERE” 子句。而且,若子句的開頭為 ANDORwhere 元素也會將它們去除。

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

set 元素可以用於動態包含需要更新的列,忽略其它不更新的列。

介面

 public int updateBlogSet(Map map);

對映檔案

<update id="updateBlogSet" parameterType="map">
    update mybatistest.blog
    <set>
        <if test="title != null">title=#{title},</if>
        <if test="author != null">author=#{author},</if>
        <if test="views != null">views=#{views},</if>
    </set>
    where id=#{id}
</update>

測試

    @Test
    public void updateBlogSet(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("id",1);
        map.put("title","t-test");
        map.put("author","a-test");
        map.put("views",100);
        int i = mapper.updateBlogSet(map);
        System.out.println(i);
        HashMap map1 = new HashMap();
        List<Blog> blogs = mapper.queryBlogIF(map1);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
    }
10

5.Foreach

介面

public List<Blog> queryBlogForeach(Map map);

對映檔案

<select id="queryBlogForeach" parameterType="map">
    select * from mybatistest.blog
    <where>
        /*此處的collection是一個list,所以map需要傳入一個list來進行遍歷*/
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
            id=#{id}
        </foreach>
        <if test="views != null">
            and views > #{views}
        </if>
        <if test="author != null">
            and author=#{author}
        </if>
    </where>
</select>

測試

    @Test
    public void queryBlogForeach(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        HashMap<String, Object> map = new HashMap<String, Object>();
        List<Integer> ids = new ArrayList<Integer>();
        ids.add(2);
        ids.add(3);
        map.put("ids",ids);
        List<Blog> blogs = mapper.queryBlogForeach(map);
        for (Blog blog : blogs) {
            System.out.println(blog);
        }
    }
11

6.Sql片段

我們可以將一些公共的部分用<sql>抽取出來,方便複用!

<sql id="id-test">
    <choose>
        <when test="title != null">
            and title like #{title}
        </when>
        <when test="author != null">
            and author = #{author}
        </when>
        <otherwise>
            and views > #{views}
        </otherwise>
    </choose>
</sql>
<select id="queryBlogChoose" parameterType="map" resultType="pojo.Blog">
   select * from mybatistest.blog
   <where>
       <include refid="id-test"></include>
   </where>
</select>

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

我們可以先在Mysql中寫出完整的SQL,在對應的去修改稱為我們的動態SQL

快取

1.簡介

查詢:連線資料庫,耗資源!

一次查詢的結果,給他暫存在一個可以直接取到的地方——記憶體:快取

那麼我們再次查詢的時候就可以不用走資料庫了

  1. 快取【Cache】?
    • 存在記憶體中的臨時資料
    • 將使用者經常查詢的資料放在快取中,使用者查詢的時候就不用從磁碟上查詢了,而從快取中查詢,提高查詢效率
  2. 為什麼使用快取?
    • 減少和資料庫的互動次數,減少系統開銷
  3. 什麼樣的資料能使用快取?
    • 經常查詢並且不經常改變的資料

2.Mybatis快取

Mybatis系統中預設頂一個兩級快取:一級快取和二級快取

  • 預設情況下,只有一級快取開啟。這是sqlSession級別的,隨著Session開啟而開啟,關閉而關閉,也稱其為本地快取
  • 二級快取是namespace級別的,需要手動開啟和配置
  • Mybatis有一個配置快取的介面Cache,可以定義二級快取

注意事項:

  • 對映語句檔案中的所有 select 語句的結果將會被快取。
  • 對映語句檔案中的所有 insert、update 和 delete 語句會重新整理快取。
  • 快取會使用最近最少使用演算法(LRU, Least Recently Used)演算法來清除不需要的快取。
  • 快取不會定時進行重新整理(也就是說,沒有重新整理間隔)。
  • 快取會儲存列表或物件(無論查詢方法返回哪種)的 1024 個引用。快取會被視為讀/寫快取,這意味著獲取到的物件並不是共享的,可以安全地被呼叫者修改,而不干擾其他呼叫者或執行緒所做的潛在修改。

3.一級快取

一級快取也叫本地快取:

  • 在域資料庫互動的同一個會話中,會將查過的資料放在快取中
  • 以後再查詢相同的資料時,直接從快取中取資料

測試

  1. 開啟日誌
  2. 測試兩次查詢同一條資料
    @Test
    public void cache(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        user user = mapper.getUserById(1);
        System.out.println(user);
        System.out.println("===============================");
        user user1 = mapper.getUserById(1);
        System.out.println(user1);
        System.out.println(user==user1);
        sqlSession.close();
    }
12

從圖中可以看出,資料在一級快取,只查詢一次,這兩者相同,為true

手動清理快取

    @Test
    public void cache(){
        SqlSession sqlSession = mybatis_util.getSqlSession1();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        user user = mapper.getUserById(1);
        System.out.println(user);
        sqlSession.clearCache();    //手動清理快取
        System.out.println("===============================");
        user user1 = mapper.getUserById(1);
        System.out.println(user1);
        System.out.println(user==user1);
        sqlSession.close();
    }
13

從圖中可以看出,資料在一級快取,手動清理快取後,查詢了兩次,這兩者不同,為false

4.二級快取

二級快取是基於namespace的快取,它的作用域比一級大

  • 我們希望當會話關閉的時候,儲存在一級快取的資料可以進入二級快取
  • 使用者進行第二次會話的時候,就可以直接從二級快取拿資料

4.1 開啟快取

在配置檔案開啟二級快取

<setting name="cacheEnabled" value="true"/>

在對應的mapper.xml中選擇開啟二級快取

<cache/>

也可以自定義cache

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

4.2測試

      @Test
      public void SecondCache(){
          SqlSession sqlSession = mybatis_util.getSqlSession();
          UserDao mapper = sqlSession.getMapper(UserDao.class);
          user user = mapper.getUserByID(1);
          System.out.println(user);
          sqlSession.close();
          System.out.println("===============================");
          SqlSession sqlSession1 = mybatis_util.getSqlSession();
          UserDao mapper1 = sqlSession1.getMapper(UserDao.class);
          user user1 = mapper1.getUserByID(1);
          System.out.println(user1);
          System.out.println(user==user1);
          sqlSession.close();
      }
14

從圖中可以看出,開啟二級快取後,sqlSession關閉時,資料存入二級快取,直接在二級快取調出資料,只用查詢了一次 ,這兩者不同,為false

注意:可能會出現的錯誤:Error serializing object. Cause:java.io.NotSerializableException: pojo.user,這個錯誤只需要在實體類繼承Serializable,即:class user implements Serializable

5.快取原理

15

6.自定義快取-encache

Ehcache是一種廣泛使用的開源Java分散式快取。EhCache 是一個純Java的程式內快取框架,具有快速、精幹等特點,是Hibernate中預設的CacheProvider。

6.1 匯入依賴

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-ehcache -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.0.0</version>
</dependency>

6.2 匯入配置檔案

建立ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">

    <diskStore path="./tmpdir/Tmp_EhCache"/>

    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>

    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>
</ehcache>

6.3 開啟二級快取

 <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

其實沒什麼大的區別,想用可以用

個人部落格為:
MoYu's Github Blog
MoYu's Gitee Blog

相關文章