1. 程式人生 > 其它 >Linux 運維 (基本 進階 高階指令)

Linux 運維 (基本 進階 高階指令)

一、什麼是MyBatis

特點

  • 優秀持久層框架
  • 支援自定義 SQL、儲存過程以及高階對映
  • 免除了幾乎所有的 JDBC 程式碼以及設定引數和獲取結果集的工作
  • 可以通過簡單的 XML 或註解來配置和對映原始型別、介面和 Java POJO(Plain Old Java Objects,普通老式 Java 物件)為資料庫中的記錄

歷史發展

  • 本名“iBatis”,iBATIS一詞來源於“internet”和“abatis”的組合
  • 2010年由apache基金會遷移到谷歌,改名為MyBatis
  • 2013年11月由谷歌遷移到GitHub

持久化

  • 什麼是持久層

    即資料持久化,也就是指資料瞬時狀態和持久狀態的過程

    也指資料物件持久化,防止物件資料丟失

MyBatis優點

  • 解除sql與程式程式碼的耦合
  • 提供對映標籤
  • 提供物件關係對映標籤
  • 提供xml標籤,支援編寫動態sql

二、程式結構

  • 3個jar包

    • mysql-connector-java.jar
    • junit.jar
    • mybatis.jar
  • 核心配置檔案

    <configuration>
        <!--配置檔案-->
        <environments default="one">
            <!--多個環境-->
            <!--預設載入指定的環境ID-->
            <environment id="one">
                <transactionManager type="JDBC"/>
                <!--事務管理型別-->
                <dataSource type="POOLED">
                <!--資料資源型別為池子-->
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql//:localhost:3306/資料庫"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
        <mappers>
        	<mapper namespace="com.dragon.dao.Mapper.xml"/>
        </mappers>
    </configuration>
    

    注意:Mapper.xml註冊

  • 資料庫連線

    public class MybatisUtils{
        private SqlSessionFactory sqlSessionFactory;
        static{
            String resource = "mybatis-config.xml";
            InputStream inputStream = MybatisUtils.class.getClassLoader().getResourceAsStream(resource);
            //InputStream inputStream = Resources.getResourceAsStream(resource);
            sqSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        }
        //返出session對應mapper.xml
        public static SqlSession getSession(){
            return SqlSessionFactory.openSession();
        }
    }
    
    
  • SQL語句實現

    <?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">
    <!--繫結對應介面-->
    <!--這裡可以對應Impl實現介面功能-->
    
    <mapper namespace="com.dragon.dao.對應介面">
        <select id="方法名" resultType="結果型別">
        	select * from table;
        </select>
    </mapper>
    
  • 測試

    public class Test{
        SqlSession sqlSession = MybatisUtils.getSession();
        介面名 mapper = sqlSession.getMapper(介面名.class);
        返回型別 物件名 = mapper.方法名();
        //關閉
        sqlSession.close();
    }
    
  • 資源過濾

三、搭建入門Mybatis程式

1.程式部署

  • MySQL5.7/MySQL8.0以上

  • 建立資料庫和資料表

    CREATE TABLE `users` (
      `id` int NOT NULL AUTO_INCREMENT,
      `name` varchar(20) CHARACTER SET utf8 COLLATE utf8 NOT NULL,
      `pwd` int NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8
    

    注意寫引擎(ENGINE)和字符集(CHARSET

    注意配置Maven的setting.xml和倉庫

  • 建立一個MavenModel

    • 將其src刪除,以當前Model為父工程,建立子工程,防止多次配置pom.xml

    • 在父工程pom.xml配置以下內容

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
      
          <!--父工程-->
          <groupId>com.dragon</groupId>
          <artifactId>mybatisStudy</artifactId>
          <packaging>pom</packaging>
          <version>1.0-SNAPSHOT</version>
          <modules>
              <module>mybatis-01</module>
          </modules>
          <!--匯入依賴-->
          <!--mysql驅動-->
          <dependencies>
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>8.0.19</version>
              </dependency>
              <!--mybatis驅動-->
              <dependency>
                  <groupId>org.mybatis</groupId>
                  <artifactId>mybatis</artifactId>
                  <version>3.5.9</version>
              </dependency>
              <!--junit-->
              <dependency>
                  <groupId>junit</groupId>
                  <artifactId>junit</artifactId>
                  <version>4.12</version>
              </dependency>
          </dependencies>
          <properties>
              <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
          </properties>
              <!--資源過濾-->
          <build>
              <resources>
                  <resource>
                      <directory>src/main/resources</directory>
                      <includes>
                          <include>**/*.properties</include>
                          <include>**/*.xml</include>
                      </includes>
                      <filtering>true</filtering>
                  </resource>
                  <resource>
                      <directory>src/main/java</directory>
                      <includes>
                          <include>**/*.properties</include>
                          <include>**/*.xml</include>
                      </includes>
                      <filtering>true</filtering>
                  </resource>
              </resources>
          </build>
      </project>
      

      注意:一定要配置資源過濾,否則專案執行不成功

    • 該專案中包含三個包

      • pojo實體類

      • dao資料處理層

        1.介面

        2.userMapper.xml

      • utils工具包

        1.MybatisUtils.class

  • 連結資料庫在IDEA中

  • 建立mybatis配置檔案“mybatis-config.xml

    • 位於src/main/resources/

    • 配置內容如下

      <?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 id="development">
                  <transactionManager type="JDBC"/><!--事務管理-->
                  <dataSource type="POOLED">
                      <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                      <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&amp;useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                      <property name="username" value="root"/>
                      <property name="password" value="root"/>
                  </dataSource>
              </environment>
          </environments>
          <mappers>
              <mapper resource="com/dragon/dao/userMapper.xml"/>
          </mappers>
      </configuration>
      

      注意:此處一定要配置mapper,找到userMapper.xml檔案,否則執行不成功(userMapper.xml是實現介面的方法)

  • userMapper.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">
    <!--namespace繫結一個對應的dao介面-->
    
    <mapper namespace="com.dragon.dao.UserDao">
        <select id="getUserList" resultType="com.dragon.pojo.User">
            select * from mybatis.users
        </select>
    </mapper>
    

    此處getUserList為介面中的方法名

  • MybatisUtils.class

    import org.apache.ibatis.io.Resources;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.apache.ibatis.session.SqlSessionFactoryBuilder;
    
    import java.io.IOException;
    import java.io.InputStream;
    
    public class MybatisUtils {
        private static SqlSessionFactory sqlSessionFactory;
        static {
            //使用Mybatis第一步,獲取sqlSessionFactory物件
            String resource = "mybatis-config.xml";
            InputStream inputStream = null;
            try {
                inputStream = Resources.getResourceAsStream(resource);
            } catch (IOException e) {
                e.printStackTrace();
            }
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        }
        public static SqlSession getSqlSession(){
            return sqlSessionFactory.openSession();
        }
    }
    
  • Test測試類

    import org.apache.ibatis.session.SqlSession;
    import org.junit.Test;
    
    import java.util.List;
    
    public class MyBatisTest {
        @Test
        public void test(){
            //獲取sqlSession物件
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserDao mapper = sqlSession.getMapper(UserDao.class);
            List<User> userList = mapper.getUserList();
    
            for (User user : userList) {
                System.out.println(user);
            }
            sqlSession.close();
        }
    }
    
    

2.解決問題

2.1

  • mybatis-config連線驅動中,如果是mysql8版本以下的jar包/依賴,那麼可以如下寫

    <property name="driver" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
    
  • 相反,在mysql8以及8以上版本

    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&amp;useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
    

2.2

  • 配置pom.xml一定要配置資源過濾,否則會出現找不到userMapper.xml檔案。

     <build>
         <resources>
             <resource>
                 <directory>src/main/resources</directory>
                 <includes>
                     <include>**/*.properties</include>
                     <include>**/*.xml</include>
                 </includes>
                 <filtering>true</filtering>
             </resource>
             <resource>
                 <directory>src/main/java</directory>
                 <includes>
                     <include>**/*.properties</include>
                     <include>**/*.xml</include>
                 </includes>
                 <filtering>true</filtering>
             </resource>
         </resources>
     </build>
    
  • 在mybatis-config.xml檔案中配置內容中一定要註冊mapper,否則會出現“org.apache.ibatis.binding.BindingException: Type interface com.dragon.dao.UserDao is not known to the MapperRegistry.”這種錯誤。

    <mappers>
    <mapper resource="com/dragon/dao/userMapper.xml"/>
     <!--或者  <package name="com.dragon.dao"/> -->
    </mappers>
    

四、CRUD

1.選擇、查詢語句

  • id:就是對應的namespace方法名
  • resultType:sql語句執行的返回值型別
  • parameterType:引數型別
  1. 編寫介面

      List<User> getUserList();
    
  2. 配置mapper中SQL語句

    <mapper namespace="com.dragon.dao.UserMapper">
        <select id="getUserList" resultType="com.dragon.pojo.User">
            select * from mybatis.users
        </select>
    </mapper>
    
  3. 測試類

     @Test
        public void test(){
            //獲取sqlSession物件
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            users = mapper.getUserList();
    
            for (User user : users) {
                System.out.println(user);
            }
            sqlSession.close();
        }
    

2.增加資料

  1. 編寫介面

     int insertUser(User user);
    
  2. 配置Mapper中SQL語句

     <insert id="insertUser" parameterType="com.dragon.pojo.User">
            insert into mybatis.users values (#{id},#{name},#{pwd});
     </insert>
    
  3. 測試類

     @Test
        public void testInsert(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            int user0 = mapper.insertUser(new User(6, "小紅", 888));
            if (user0>0){
                System.out.println("插入成功");
            }
            //提交事務
            sqlSession.commit();
            sqlSession.close();
        }
    

3.刪除資料

  1. 編寫介面

    void deleteUserById(int id);
    
  2. 配置Mapper中SQL語句

    <delete id="deleteUserById" parameterType="int">
        delete from mybatis.users where id = #{id}
    </delete>
    
  3. 測試類

        @Test
        public void testDeleteById(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            mapper.deleteUserById(6);
            users = mapper.getUserList();
            for (User user : users) {
                System.out.println(user);
            }
            sqlSession.commit();
            sqlSession.close();
        }
    

4.更改資料

  1. 編寫介面

    void queryUser(int pwd);
    
  2. 配置Mapper中SQL語句

    <update id="queryUser" parameterType="int">
        update mybatis.users set pwd=#{pwd} where id = 3
    </update>
    
  3. 測試類

    @Test
        public void testQueryById(){
            SqlSession sqlSession = MybatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            mapper.queryUser(0000);
            users = mapper.getUserList();
            for (User user : users) {
                System.out.println(user);
            }
            sqlSession.close();
        }
    

5.注意

  • 在增、刪、改三種情況下必須提交事務!

    sqlSession.commit();
    
  • 關閉sqlSession

    sqlSession.close();
    
  • 介面所有的普通引數,儘量都寫上@Param引數,尤其是多個引數時,必須寫上!

  • 有時候根據業務的需求,可以考慮使用map傳遞引數!

    當一個實體類屬性過多時,可以使用Map來解決命名傳參

    //java---controller層
    Map<> list = new HashMap<String,Object>;
    list.put("a",值);
    
    <!--xml---sql語句-->
    <insert id="方法名" paramType="map">
        insert into table values(#{a});
    </insert>
    
    • Map傳遞引數,直接在SQL中取出即可
    • 物件傳遞引數,直接在SQL中取物件的屬性即可
    • 只有一個基本型別引數的情況下,可以直接在sql中取到
    • 多個引數用Map或者註解
  • 為了規範操作,在SQL的配置檔案中,我們儘量將Parameter引數和resultType都寫上!

6.SQL模糊查詢

  • 在sql.xml中直接%%,容易被SQL注入,不知道為啥
  • 在controller中用%%

五、配置

1.結構

MyBatis 的配置檔案包含了會深深影響 MyBatis 行為的設定和屬性資訊。 配置文件的頂層結構如下:

2.屬性(properties)

這些屬性可以在外部進行配置,並可以進行動態替換。你既可以在典型的 Java 屬性檔案中配置這些屬性,也可以在 properties 元素的子元素中設定。例如:

<properties resource="org/mybatis/example/config.properties">
  <property name="username" value="dev_user"/>
  <property name="password" value="F2Fa3!33TYyg"/>
</properties>

設定好的屬性可以在整個配置檔案中用來替換需要動態配置的屬性值。比如:

<dataSource type="POOLED">
  <property name="driver" value="${driver}"/>
  <property name="url" value="${url}"/>
  <property name="username" value="${username}"/>
  <property name="password" value="${password}"/>
</dataSource>

3.屬性別名(typeAliases)

  • 為實體類設定別名,意在降低冗餘的全限定類名書寫

    • DIY自定義
    <typeAliases>
    	<typeAlias alias="User" type="com.dragon.pojo.User"/>
    </typeAliases>
    
    • 掃描包
    <typeAliases>
    	<package name="com.dragon.pojo"/>
    </typeAliases>
    <!--掃描完之後,就可以直接使用類名-->
    
    • 註解
    @Alias("user")
    public class User{
        
    }
    

4.對映器(mappers)

  • 註冊mapper-SQL語句

  • 三種方式

    • 相對路徑

      <mappers>
      	<mapper resource="com.dragon.dao.UserMapper.xml" />
      </mappers>
      
    • 使用完全限定資源定位符(URL)

      <mappers>
        <mapper url="file:///var/mappers/AuthorMapper.xml"/>
        <mapper url="file:///var/mappers/BlogMapper.xml"/>
        <mapper url="file:///var/mappers/PostMapper.xml"/>
      </mappers>
      
    • 使用對映器介面實現類的完全限定類名

      <mappers>
        <mapper class="org.mybatis.builder.AuthorMapper"/>
        <mapper class="org.mybatis.builder.BlogMapper"/>
        <mapper class="org.mybatis.builder.PostMapper"/>
      </mappers>
      
    • 將包內的對映器介面實現全部註冊為對映器

      <mappers>
        <package name="c"/>
      </mappers>
      

六、生命週期及作用域

  • SqlSession建造流程

  • 兩種方式結束

    1. Sqlmapper獲取類,類獲取方法
    2. SqlSession直接呼叫sql操作(不建議使用)
  • 生命週期、作用域是至關重要的,因為錯誤的使用會導致非常嚴重的併發問題

  • SqlSessionFactoryBuilder

    • 一旦建立了SqlSessionFactory,就不需要建造者了
    • 區域性變數
  • SqlSessionFactory:

    • 可以理解為:資料庫連線池
    • 一旦被建立,在執行期間一直存在,沒有任何理由丟棄或重新建立另外一個例項(當然也可以建立,但是會出現高併發,導致程式崩潰
    • 最簡單就是使用單列模式/靜態單列模式
  • SqlSession

    • 連線到連線池的一個請求
    • SqlSession的例項不是執行緒安全的,因此是不能被共享的,所以最佳作用域是請求/方法作用域
    • 用完之後需要趕緊關閉,否則資源被佔用
  • Mapper

    • 每個Mapper代表一個業務

七、ResultMap結果對映集

1.解決屬性名和欄位名不一致問題

  • 起別名

SQL中的欄位與bean中的欄位不一致

修改sql語句

as 別名

  • resultMap的使用

    當類中的屬性和資料庫的欄位不一致時

    <resultMap id="對應下面的resultMapd" type="類名">
    	<result property="pwd" column="password"/>
    </resultMap>
    <mapper namespace="com.dragon.dao.UserMapper">
        <select resultMap="map" id="方法名">
        	select id,username,username from table
        </select>
    </mapper>
    

八、日誌工廠

  • 在配置檔案出錯時,我們基本無法排查,所以有了日誌工廠,以此來監聽內部步驟

  • mybatis-config.xml檔案中設定以下內容

    <settings>
    	<setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--注意該句位置,以及書寫對錯-->
    </settings>
    
  • 官方文件:

logImpl 指定 MyBatis 所用日誌的具體實現,未指定時將自動查詢。 SLF4J | LOG4J(deprecated since 3.5.9) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING

1.Log4j

  • 什麼是Log4j

    • Apache的一個開源專案,通過使用Log4j,可以控制日誌資訊輸送的目的地是控制檯、檔案、GUI元件
    • 也可以控制每一條日誌的輸出格式
    • 通過配置檔案來靈活地進行配置,而不需要修改程式碼
  • 先匯入log4j的包

    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
  • 配置log4j.properties

    #將等級為DEBUG的日誌資訊輸出到console和file這兩個目的地,console和file的定義在下面的程式碼
    log4j.rootLogger=DEBUG,console,file
    
    #控制檯輸出的相關設定
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target = System.out
    log4j.appender.console.Threshold=DEBUG
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
    
    #檔案輸出的相關設定
    log4j.appender.file = org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=./log/dragon.log
    log4j.appender.file.MaxFileSize=10mb
    log4j.appender.file.Threshold=DEBUG
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
    
    #日誌輸出級別
    log4j.logger.org.mybatis=DEBUG
    log4j.logger.java.sql=DEBUG
    log4j.logger.java.sql.Statement=DEBUG
    log4j.logger.java.sql.ResultSet=DEBUG
    log4j.logger.java.sql.PreparedStatement=DEBUG
    
  • 配置log4j為日誌實現

    <settings>
    	<setting name="logImpl" value=""/>
    </settings>
    
  • 簡單使用

    1. 在使用Log4j類中,匯入包 import org.apache.log4j.Logger

    2. 日誌物件,引數為當前類的class

      static Logger logger = Logger.getLogger(當前類名.class);
      
    3. 日誌級別

      logger.info("info進入testlog4j");
      logger.debug("debug進入testlog4j");
      logger.error("error進入testlog4j");
      

九、分頁

9.1LIMIT分頁

  • 作用:減少資料處理量

  • SELECT * FROM user LIMIT startIndex,pageSize;
    #或者-----起始位置,每頁數量
    SELECT * FROM user LIMIT pageSize;
    
  • 使用MyBatis分頁

    1. 介面

      List<User> getUserByLimit(Map<String,Integer> map);
      
    2. SQL

      <select id="getUserByLimit" paramterType="map" resultMap="UserMap">
      	select * from mybatis.user limit #{startIndex},#{pageSize}
      </select>
      
    3. HashMap<> map = new HashMap<String,Integer>();
      map.put("startIndex",1);
      map.put("pageSize",2);
      

9.2RowBounds分頁

十、註解開發MyBatis

  • 開發中基本不用
  • 簡單,不用配置xml

十一、剖面解析原理

十二、複雜查詢

1.多對一

  • ResultMap結果對映

    • association:物件
    • collection*:集合
  • 兩個實體類

    • Teacher.java
    • Student.java

    其中多個學生對應一個老師

    在Student類中建立Teacher的姓名

  • <select id="getStudent" resultMap="StudentTeacher">
    	select * from student
    </select>
    <resultMap id="StudentTeacher" type="Student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <!--對應Teacher類-->
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    <select id="getTeacher" resultType="Teacher">
    	select * from teacher where id =#{id}
    </select>
    
  • 注意:提前對映Mapper檔案,設定typeAlias

十三、動態SQL

  • 動態 SQL 是 MyBatis 的強大特性之一。

  • 動態關鍵詞

    • if

      <select id="findActiveBlogLike"
           resultType="Blog">
        SELECT * FROM BLOG WHERE state = ‘ACTIVE’ 
        <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>
      
    • choose (when, otherwise)

      有時候,我們不想使用所有的條件,而只是想從多個條件中選擇一個使用。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。

      還是上面的例子,但是策略變為:傳入了 “title” 就按 “title” 查詢,傳入了 “author” 就按 “author” 查詢的情形。若兩者都沒有傳入,就返回標記為 featured 的 BLOG(這可能是管理員認為,與其返回大量的無意義隨機 Blog,還不如返回一些由管理員精選的 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>
      
    • trim (where, set)

      <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>
      
    • foreach

      動態 SQL 的另一個常見使用場景是對集合進行遍歷(尤其是在構建 IN 條件語句的時候)。比如:

      <select id="selectPostIn" resultType="domain.blog.Post">
        SELECT *
        FROM POST P
        <where>
          <foreach item="item" index="index" collection="list"
              open="ID in (" separator="," close=")" nullable="true">
                #{item}
          </foreach>
        </where>
      </select>
      

十四、快取

簡介

1、什麼是快取 [ Cache ]?

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

2、為什麼使用快取?

  • 減少和資料庫的互動次數,減少系統開銷,提高系統效率。

3、什麼樣的資料能使用快取?

  • 經常查詢並且不經常改變的資料。

Mybatis快取

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

  • MyBatis系統中預設定義了兩級快取:一級快取二級快取

    • 預設情況下,只有一級快取開啟。(SqlSession級別的快取,也稱為本地快取)
    • 二級快取需要手動開啟和配置,他是基於namespace級別的快取。
    • 為了提高擴充套件性,MyBatis定義了快取介面Cache。我們可以通過實現Cache介面來自定義二級快取

1.一級快取

  • 一級快取又叫“本地快取”:SqlSession
    • 與資料庫同一次會話期間查詢到資料會放在本地快取中
    • 以後如果需要獲取相同資料,直接存快取中拿,沒必要去查詢資料庫
  • 快取時間即事務開啟到事務結束

一級快取失效的四種情況

一級快取是SqlSession級別的快取,是一直開啟的,我們關閉不了它;

一級快取失效情況:沒有使用到當前的一級快取,效果就是,還需要再向資料庫中發起一次查詢請求!

1、sqlSession不同

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   SqlSession session2 = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session2.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   User user2 = mapper2.queryUserById(1);
   System.out.println(user2);
   System.out.println(user==user2);

   session.close();
   session2.close();
}

觀察結果:發現傳送了兩條SQL語句!

結論:每個sqlSession中的快取相互獨立

2、sqlSession相同,查詢條件不同

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   User user2 = mapper2.queryUserById(2);
   System.out.println(user2);
   System.out.println(user==user2);

   session.close();
}

觀察結果:發現傳送了兩條SQL語句!很正常的理解

結論:當前快取中,不存在這個資料

3、sqlSession相同,兩次查詢之間執行了增刪改操作!

增加方法

//修改使用者
int updateUser(Map map);

編寫SQL

<update id="updateUser" parameterType="map">
  update user set name = #{name} where id = #{id}
</update>

測試

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);

   HashMap map = new HashMap();
   map.put("name","kuangshen");
   map.put("id",4);
   mapper.updateUser(map);

   User user2 = mapper.queryUserById(1);
   System.out.println(user2);

   System.out.println(user==user2);

   session.close();
}

觀察結果:查詢在中間執行了增刪改操作後,重新執行了

結論:因為增刪改操作可能會對當前資料產生影響

4、sqlSession相同,手動清除一級快取

@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);

   session.clearCache();//手動清除快取

   User user2 = mapper.queryUserById(1);
   System.out.println(user2);

   System.out.println(user==user2);

   session.close();
}

一級快取就是一個map

2、二級快取

  • 二級快取也叫全域性快取,一級快取作用域太低了,所以誕生了二級快取

  • 基於namespace級別的快取,一個名稱空間,對應一個二級快取;

  • 工作機制

    • 一個會話查詢一條資料,這個資料就會被放在當前會話的一級快取中;
    • 如果當前會話關閉了,這個會話對應的一級快取就沒了;但是我們想要的是,會話關閉了,一級快取中的資料被儲存到二級快取中;
    • 新的會話查詢資訊,就可以從二級快取中獲取內容;
    • 不同的mapper查出的資料會放在自己對應的快取(map)中;

使用步驟

1、開啟全域性快取 【mybatis-config.xml】

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

2、去每個mapper.xml中配置使用二級快取,這個配置非常簡單;【xxxMapper.xml】

<cache/>

官方示例=====>檢視官方文件
<cache
 eviction="FIFO"
 flushInterval="60000"
 size="512"
 readOnly="true"/>
這個更高階的配置建立了一個 FIFO 快取,每隔 60 秒重新整理,最多可以儲存結果物件或列表的 512 個引用,而且返回的物件被認為是隻讀的,因此對它們進行修改可能會在不同執行緒中的呼叫者產生衝突。

3、程式碼測試

  • 所有的實體類先實現序列化介面
  • 測試程式碼
@Test
public void testQueryUserById(){
   SqlSession session = MybatisUtils.getSession();
   SqlSession session2 = MybatisUtils.getSession();

   UserMapper mapper = session.getMapper(UserMapper.class);
   UserMapper mapper2 = session2.getMapper(UserMapper.class);

   User user = mapper.queryUserById(1);
   System.out.println(user);
   session.close();

   User user2 = mapper2.queryUserById(1);
   System.out.println(user2);
   System.out.println(user==user2);

   session2.close();
}

結論

  • 只要開啟了二級快取,我們在同一個Mapper中的查詢,可以在二級快取中拿到資料
  • 查出的資料都會被預設先放在一級快取中
  • 只有會話提交或者關閉以後,一級快取中的資料才會轉到二級快取中

3.快取原理

當一級快取的session關閉時,利用我們在mapper中設定的<cache/>來進行二級快取