MyBatis 基礎

這是我參與8月更文挑戰的第11天,活動詳情查看:8月更文挑戰javascript

簡介

  1. 來源html

    MyBatis 本是 apache 的一個開源項目 iBatisjava

    2010 年 由 apache software foundation 遷移到了 google code,而且更名爲 MyBatismysql

    2013 年 11 月遷移到 Githubgit

  2. 做用github

    MyBatis 是一款優秀的持久層框架sql

    它支持自定義 SQL、存儲過程以及高級映射數據庫

    MyBatis 免除了幾乎全部的 JDBC 代碼以及設置參數和獲取結果集的工做apache

  3. 地址緩存

    中文簡介:mybatis.org/mybatis-3/z…

    GitHub:github.com/mybatis

    Maven:

    <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.5</version>
    </dependency>
    複製代碼

持久層

  1. 數據持久化是指數據由瞬時狀態轉化成持久狀態的過程

    • 瞬時狀態:內存中存儲的內容,斷電後就會丟失
    • 持久狀態:數據庫(JDBC)、文件(IO)
  2. 持久層是完成持久化工做的代碼

傳統 JDBC

  1. 創建測試數據庫

    CREATE DATABASE `mybatis`;
    CREATE TABLE `user` (
      `id` int NOT NULL,
      `name` varchar(30) DEFAULT NULL,
      `pwd` varchar(30) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    複製代碼
  2. 通常步驟

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.Statement;
    import java.util.Scanner;
    
    public class JDBCDemo {
    	public static void main(String[] args) throws Exception {
    		// 註冊驅動
    		Class.forName("com.mysql.jdbc.Driver");
    		// 獲取鏈接對象
    		String url = "jdbc:mysql://localhost:3306/mybaitis";
    		Connection conn = DriverManager.getConnection(url, "root", "root");
    		// SQL 語句
    		String sql = "select * from users where username = ? and password = ? ";
    		// 獲取執行語句對象
    		PreparedStatement pst = conn.prepareStatement(sql);
    		// 設置問號佔位符參數
    		pst.setObject(1, "小 a");
    		pst.setObject(2, "123456");
    		// 調用執行者對象方法,執行SQL語句獲取結果集
    		ResultSet rs = pst.executeQuery();
    		// 處理結果集
    		while (rs.next()) {
    			System.out.println(rs.getString("username") + " : " + rs.getString("password"));
      		}
    		// 關閉資源
    		rs.close();
    		pst.close();
    		conn.close();
    	}
    }
    複製代碼

使用 MyBatis

  1. 導入 jar 包

    maven 構建的項目在 pom.xml 中添加

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.5</version>
    </dependency>
    複製代碼
  2. 配置文件 mybatis-config-xml

    <?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?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                    <property name="username" value="root"/>
                    <property name="password" value="12345"/>
                </dataSource>
            </environment>
        </environments>
    </configuration>
    複製代碼
  3. 編寫工具類

    1. 每一個基於 MyBatis 的應用都是以一個 SqlSessionFactory 的實例爲核心的

    2. SqlSessionFactoryBuilder 經過 XML 配置文件能夠構建出 SqlSessionFactory 實例

    3. SqlSession 包含向數據庫執行 sql 的全部方法,經過 SqlSessionFactory 獲取

    4. utils 包新增工具類

      package utils;
      
      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 MyBatisUtil {
          private static SqlSessionFactory sqlSessionFactory;
          
          static {
              // 讀取配置文件,獲取 SqlSessionFactory 實例
              try {
                  String resource = "mybatis-config.xml";
                  InputStream inputStream = Resources.getResourceAsStream(resource);
                  sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
          
          /** * 獲取 sqlSession 實例 * * @return SqlSession 實例 */
          public static SqlSession getSqlSession() {
              return sqlSessionFactory.openSession();
          }
      }
      複製代碼
  4. 編寫代碼並測試

    1. pojo 包中增長 User 類

      package pojo;
      
      public class User {
          int id;
          String name;
          String pwd;
          
          // setter/getter 省略...
      }
      複製代碼
    2. dao 包中增長 UserDao 接口

      package dao;
      
      import pojo.User;
      
      import java.util.List;
      
      public interface UserDao {
          List<User> listUsers();
      }
      複製代碼
    3. dao 包中增長 UserMapper.xml

      指定命名空間,select 查詢的 id 對應 UserDao 方法名

      <?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="dao.UserDao">
          <select id="listUsers" resultType="pojo.User">
              select * from user
          </select>
      </mapper>
      複製代碼
    4. mybatis 配置文件中在 configuration 標籤下添加 mapper 位置信息

      <mappers>
          <mapper resource="dao/UserMapper.xml"></mapper>
      </mappers>
      複製代碼
    5. maven pom 文件中要配置開啓包下的 xml 文件資源過濾

      <build>
          <resources>
              <resource>
                  <directory>src/main/java</directory>
                  <includes>
                      <include>**/*.xml</include>
                      <include>**/*.properties</include>
                  </includes>
                  <filtering>true</filtering>
              </resource>
              <resource>
                  <directory>src/main/resources</directory>
                  <includes>
                      <include>**/*.xml</include>
                      <include>**/*.properties</include>
                  </includes>
                  <filtering>true</filtering>
              </resource>
          </resources>
      </build>
      複製代碼
    6. junit 測試

      public class UserDaoTest {
          
          @Test
          public void test() {
              // 工具類獲取 SqlSession
              SqlSession sqlSession = MyBatisUtil.getSqlSession();
              // sqlSession 獲取 userDao 實現實例
              UserDao userDao = sqlSession.getMapper(UserDao.class);
              // 執行 SQL 方法
              List<User> users = userDao.listUsers();
              System.out.println(users);
          }
      }
      複製代碼

配置文件

  1. 環境配置

    MyBatis 能夠配置成適應多種環境,但每一個 SqlSessionFactory 實例只能選擇一種環境

    配置多個 environment,使用 default 屬性進行指定

    <environments default="development">
      <environment id="development">
        <transactionManager type="JDBC">
          <property name="..." value="..."/>
        </transactionManager>
        <dataSource type="POOLED">
          <property name="driver" value="${driver}"/>
          <property name="url" value="${url}"/>
          <property name="username" value="${username}"/>
          <property name="password" value="${password}"/>
        </dataSource>
      </environment>
    </environments>
    複製代碼
  2. properties

    屬性能夠在外部進行配置,並能夠進行動態替換,能夠在典型的 Java 屬性文件中配置這些屬性,也能夠在 properties 元素的子元素中設置

    db.properties

    driver=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username=root
    password=12345
    複製代碼

    在 mybatis-config 中添加

    <properties resource="db.properties">
        <property name="username" value="root"/>
        <property name="password" value="12345" />
    </properties>
    複製代碼

    resource 指定的配置文件中的內容優先級高於標籤內的配置

  3. 類型別名

    配置 typeAliases

    <typeAliases>
        <typeAlias type="pojo.User" alias="User"></typeAlias>
        <package name="pojo"></package>
    </typeAliases>
    複製代碼

    pojo 上註解

    @Alias("User")
    public class User {
    }
    複製代碼

    類型別名可爲 Java 類型設置一個縮寫名字。 它僅用於 XML 配置,意在下降冗餘的全限定類名書寫

    包別名配置會在包名下面搜索須要的 Java Bean,在沒有註解時,會使用 Bean 的首字母小寫的非限定類名來做爲它的別名

    如有註解,則別名爲其註解值

  4. settings

    <settings>
      <setting name="cacheEnabled" value="true"/>
      <setting name="lazyLoadingEnabled" value="true"/>
      <setting name="logImpl" value="LOG4J"/>
    <settings>
    複製代碼

    cacheEnabled:全局性地開啓或關閉全部映射器配置文件中已配置的任何緩存

    lazyLoadingEnabled:延遲加載的全局開關。當開啓時,全部關聯對象都會延遲加載

    logImpl:指定 MyBatis 所用日誌的具體實現,未指定時將自動查找

  5. 映射器

    指定資源路徑

    <mappers>
        <mapper resource="dao/UserMapper.xml"></mapper>
    </mappers>
    複製代碼

    指定類名,須要 mapper.xml 和 mapper 接口同名且在同一個包下

    <mappers>
        <mapper class="dao.UserMapper"></mapper>
    </mappers>
    複製代碼

    指定包名,須要 mapper.xml 和 mapper 接口同名且在同一個包下

    <mappers>
        <package name="dao"></package>
    </mappers>
    複製代碼

XML 映射器

  1. 查詢

    dao 包中 mapper 接口

    public interface UserDao {
        User getUserById(int id);
    }
    複製代碼

    UserMapper.xml

    <mapper namespace="dao.UserDao">
        <select id="getUserById" parameterType="int" resultType="hashmap">
          SELECT * FROM PERSON WHERE ID = #{id}
        </select>
    </mapper>
    複製代碼
  2. 增刪改

    dao 包中 mapper 接口

    public interface UserDao {    
        int addUser(User user);
        
        int updateUser(User user);
        
        int deleteUserById(int id);
    }
    複製代碼

    UserMapper.xml

    <insert id="addUser" parameterType="pojo.User">
        insert into user values (#{id}, #{name}, #{pwd})
    </insert>
    <update id="updateUser" parameterType="pojo.User">
        update user set name = #{name}, pwd = #{pwd} where id = #{id}
    </update>
    <delete id="deleteUserById" parameterType="int">
        delete from user where id = #{id}
    </delete>
    複製代碼

    增刪改執行方法後,須要提交事務

    sqlSession.commit();
    // 可使用 sqlSessionFactory.openSession(true) 開啓自動提交
    複製代碼
  3. 經常使用屬性

    • id:在命名空間中惟一的標識符,能夠被用來引用這條語句,與 namespace 接口方法名一致
    • parameterType:將會傳入這條語句的參數的類全限定名或別名
      • 傳入普通對象,使用 #{屬性名} 獲取屬性
      • 傳入 map,使用 #{key} 獲取 value
      • 傳入一個基本類型,直接使用 #{參數名} 獲取
    • resultType:指望從這條語句中返回結果的類全限定名或別名,若是返回的是集合,那應該設置爲集合包含的類型,而不是集合自己的類型
  4. resultMap 結果集映射

    1. 簡單映射

      當查詢出的結果集沒法與 Bean 字段映射時,須要指定 resultMap

      例如,數據庫中字段爲 password,Bean 中爲 pwd,能成功映射的字段沒必要要配置

      <resultMap type="pojo.User" id="UserMap">
          <result column="id" property="id"></result>
          <result column="name" property="name"></result>
          <result column="password" property="pwd"></result>
      </resultMap>
      
      <select id="listUsers" resultMap="UserMap">
          select * from user
      </select>
      複製代碼
    2. 複雜映射

      例如學生和老師的多對一關係

      public class Student {
          int id;
          String name;
          Teacher teacher;
          // ...
      }
      public class Teacher {
          int id;
          String name;
          // ...
      }
      複製代碼

      StudentMapper.xml 中配置 resultMap,association 標籤 select 屬性指向一個查詢,column 做爲查詢參數,javaType

      <resultMap id="StudentMap" type="pojo.Student">
          <result column="id" property="id"></result>
          <result column="name" property="name"></result>
          <association property="teacher" column="tid" javaType="pojo.Teacher" select="getTeacher" ></association>
      </resultMap>
      
      <select id="listStudents" resultMap="StudentMap">
          SELECT * FROM student
      </select>
      <select id="getTeacher" parameterType="int" resultType="pojo.Teacher">
          SELECT * FROM student where id = #{id}
      </select>
      複製代碼

      也能夠用一個查詢完成,須要在 SQL 中對結果集定義別名

      <resultMap id="StudentMap" type="pojo.Student">
          <result column="sid" property="id"></result>
          <result column="sname" property="name"></result>
          <association property="teacher" column="tid" javaType="pojo.Teacher">
              <result column="tid" property="id"></result>
              <result column="tname" property="name"></result>
          </association>
      </resultMap>
      
      <select id="listStudents" resultMap="StudentMap">
          SELECT s.id sid, s.name sname, t.id tid, t.name tname FROM student s, teacher t where s.tid = t.id
      </select>
      複製代碼

      一樣的,老師和學生的一對多關係,可以下解決

      // pojo
      public class Teacher {
          int id;
          String name;
          List<Student> students;
      }
      public class Student {
          int id;
          String name;
          int tid;
      }
      複製代碼
      <!-- TeacherMapper.xml -->
      <resultMap id="TeacherMap" type="pojo.Teacher">
              <result column="tid" property="id"></result>
              <result column="tname" property="name"></result>
              <collection property="students" ofType="pojo.Student">
                  <result column="sid" property="id"></result>
                  <result column="stid" property="tid"></result>
                  <result column="sname" property="name"></result>
              </collection>
      </resultMap>
      <select id="getTeacher" resultMap="TeacherMap">
          select t.id tid, t.name tname, s.id sid, s.name sname, s.tid stid
          from teacher t,
          student s
          where t.id = s.tid
          and t.id = #{id}
      </select>
      複製代碼

      association 用來處理多對一,collection 用來處理一對多,javaType 表示屬性的類型,ofType 表示集合中的元素類型

生命週期

  1. 生命週期和做用域使用有誤可能會致使嚴重的併發問題

  2. 基本流程

    SqlSessionFactoryBuilder -> SqlSessionFactory -> SqlSession -> getMapper()
    複製代碼
  3. SqlSessionFactoryBuilder

    • 經過配置文件建立 SqlSessionFactory,建立完畢後,就不在須要它了
    • 最佳的做用域是方法做用域(也就是局部變量)
  4. SqlSessionFactory

    • 相似於鏈接池
    • 一旦建立就在運行期間一直存在
    • 最佳做用域爲應用做用域
    • 最簡單的就是使用單例模式或者靜態單例模式
  5. SqlSession

    • 表示鏈接到數據庫的一個請求,使用完畢後須要調用 close() 釋放資源
    • SqlSession 的實例不是線程安全的,因此它的最佳的做用域是請求或方法做用域

日誌

  1. 日誌工廠

    Mybatis 經過使用內置的日誌工廠提供日誌功能。內置日誌工廠將會把日誌工做委託給下面的實現之一:

    SLF4J | Apache Commons Logging | Log4j 2 | Log4j  | JDK logging
    複製代碼

    在配置文件的 setting 標籤中能夠設置具體使用哪種日誌實現

    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"></setting>
    </settings>
    複製代碼

    STDOUT_LOGGING 是標準日誌實現,能夠直接使用

    若使用 LOG4J 須要導入相關包並指定 logImpl 爲 LOG4J

  2. LOG4J

    LOG4J 是 Apache 的開源項目,用於實現輸入日誌到控制檯、GUI 組件或文件,可用外部文件靈活配置

    pom.xml

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
    複製代碼

    log4j.properties

    #將等級爲 DEBUG 的信息輸出到控制檯和文件
    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=%5p [%d{yyyy-MM-dd HH:mm:ss}][%t][%F:%L] - %m%n
    #文件輸出設置
    log4j.appender.file=org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=./log/log4j.log
    log4j.appender.file.MaxFileSize=10mb
    log4j.appender.file.Threshold=DEBUG
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=%5p [%d{yyyy-MM-dd HH:mm:ss}][%t][%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
    複製代碼

    配置完 pm.xml、mybatis-config.xml、log4j.properties 運行代碼,可見 LOG4J 打印信息在控制檯

    也能夠在類中主動使用 Logger 類自定義打印內容

    public class LoggerTest {
        // 獲取 logger 實例
        static Logger logger = Logger.getLogger(LoggerTest.class);
        
        @Test
        public void log4jTest() {
            // 測試不一樣的日誌級別
            logger.info("info => info 內容");
            logger.debug("debug => debug 內容");
            logger.warn("warn => warn 內容");
            logger.error("error => error 內容");
        }
    }
    複製代碼

分頁

  1. 當數據量過大時,須要分頁以減小數據處理量,並能夠多頁顯示,提高用戶體驗

  2. mysql limit 關鍵字分頁

    SELECT * FROM user limit startIndex, pageSize
    SELECT * FROM user limit pageSize
    // 至關於
    SELECT * FROM user limit 0, pageSize
    複製代碼

    mapper.xml

    <select id="listUsersLimit" parameterType="map" resultType="pojo.User">
       select * from user limit #{startIndex},#{pageSize}
    </select>
    複製代碼

    dao 或 mapper 接口

    public interface UserDao {    
        List<User> listUsersLimit(Map map);
    }
    複製代碼
  3. RowBounds 分頁

    mapper.xml 中的 SQL 查詢中不作數據量的限制,經過 sqlSession 的查詢方法和 RowBounds 對象進行數量的限制

    List<User> users = sqlSession.selectList("dao.UserDao.listUsersByRowBounds",null, new RowBounds(1,2));
    複製代碼

註解

  1. CURD 註解

    不須要在 mapper.xml 中指定 SQL,直接在註解中傳入 SQL 語句便可,適合簡單的查詢

    可使用 @Select/@Update/@Delete/@Insert

    public interface UserDao {
        @Select("select * from user")
        List<User> listUserByAnnotation();
    }
    複製代碼
  2. 參數註解

    多個基本類型或 String 類型參數須要經過 @Param 指定名稱,以便在 SQL 語句中使用

    不指定也能夠在 SQL 中使用 arg0,arg1 或 param1,param2 等按順序來獲取參數

    public interface UserDao {
        @Select("select * from user where name = #{name} and pwd = #{pwd}")
        User getUserByNameAndPwd(@Param("name") String name, @Param("pwd") String pwd);
    }
    複製代碼

動態 SQL

  1. if

    方法傳入 id 爲 0 時不開啓 if 內 and 條件,則查詢所有。傳入 id 不爲 0 時,則查詢指定 id

    <select id="listStudentsById" parameterType="int" resultType="pojo.Student">
        SELECT *
        FROM student
        WHERE
        <if test="id != 0">
            id = #{id}
        </if>
    </select>
    複製代碼
  2. choose,when,otherwise

    相似於 switch 條件控制

    <select id="listStudentsById" parameterType="int" resultType="pojo.Student">
        SELECT *
        FROM student
        <where>
        	<choose>
        		<when test="id == 1">
        			id = 2
        		</when>
        		<when test="id == 2">
        			and id = 1
        		</when>
       	 		<otherwise>
        			and id = 3
        		</otherwise>
        	</choose>
        </where>
    </select>
    複製代碼
  3. where,trim,set

    在上面 if 的例子中,若是 if 條件不知足,不會拼接 if 中的內容,只有一個 where 會致使 SQL 錯誤

    能夠經過 where 標籤更優雅的解決,

    <select id="listStudentsById" parameterType="int" resultType="pojo.Student">
        SELECT *
        FROM student
        <where>
       		<if test="id != 0">
           		id = #{id}
        	</if>
            ...
        </whrer>
    </select>
    複製代碼

    where 標籤當內部有內容返回時纔會返回 where 條件語句,而且自動去除子句開頭的 and/or

    trim 和 where 功能相似,能夠指定拼接子句時要替換的關鍵字,prefix 表示最後返回的子句,prefixOverrides/suffixOverrides 表示須要被刪除或添加的前綴或後綴

    <trim prefix="where" prefixOverrides="and |or ">
         <choose>
             <when test="id == 1">
                 and id = 2
             </when>
             <when test="id == 2">
                 and id = 1
             </when>
             <otherwise>
                 and id = 3
             </otherwise>
         </choose>
    </trim>
    複製代碼

    set 用於實現動態的更新語句

    <update id="updateStudent" parameterType="map">
        update student
        <set>
            <if test="id != null">id = #{id},</if>
            <if test="name != null">name = #{name},</if>
            <if test="tid != null">tid = #{tid}</if>
        </set>
    </update>
    複製代碼

    trim 實現 set 功能

    <trim prefix="SET" suffixOverrides=",">
      ...
    </trim>
    複製代碼
  4. foreach

    select * from student where id in (1, 2, 3)
    複製代碼

    相似於上面的 SQL,當 in 子句的範圍不肯定時,可使用 foreach

    <select id="listStudents" parameterType="list" resultType="pojo.Student">
        SELECT *
        FROM student
        <where>
            <foreach item="item" index="index" collection="list" open="id in (" separator="," close=")">
                #{item}
            </foreach>
        </where>
    </select>
    複製代碼

    若是 list 元素個數爲 0,則不會返回任何字符串

  5. sql 片斷

    sql 標籤訂義的片斷能夠經過 include 引入,提升複用性

    <sql id="sql-if-id">
    	<if test="id != 0">
            id = #{id}
        </if>
    </sql>
    複製代碼

    在其餘標籤中使用

    <select id="listStudentsById" parameterType="int" resultType="pojo.Student">
        SELECT *
        FROM student
        <where>
    		<include refid="sql-if-id"></include>
        </whrer>
    </select>
    複製代碼

緩存

  1. 緩存是內存中的臨時數據,能夠提高查詢效率、減小數據庫壓力,適合常常查詢但不常常改變的數據

  2. MyBatis 默認開啓一級緩存,是 SqlSession 級別的緩存

    從打開一個 SqlSession 實例開始,重複執行兩次查詢方法,只會執行一次 SQL,返回的數據指向同一對象

    如何兩次查詢之間執行了增刪改操做或 sqlSession 調用了 clearCache 方法,則第二次會從新查詢

  3. MyBatis 二級緩存能夠手動開啓,是 namespace 級別的緩存

    先要在 myatis-config.xml 中配置開啓緩存

    <settings>
    	<setting name="cacheEnabled" value="true"></setting>
    </settings>
    複製代碼

    在要使用二級緩存的 mapper.xml 經過 cache 標籤中開啓

    <cache />
    <!--一些屬性-->
    <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
    複製代碼

    開啓了二級緩存後,在同一個 mapper 中有效

    一個 SqlSession 實例關閉後,其中的一級緩存會轉存到二級緩存中,可供另外一個 Sqlsession 實例使用

相關文章
相關標籤/搜索