MyBatis學習筆記

一、簡介

image-20210129225418682

環境說明:html

  • jdk 8 +
  • MySQL 5.7.19
  • maven-3.6.1
  • IDEA

學習前須要掌握:java

  • JDBC
  • MySQL
  • Java 基礎
  • Maven
  • Junit

1.一、什麼是 MyBatis ?

  • MyBatis 是一款優秀的持久層框架
  • 它支持定製化 SQL、存儲過程以及高級映射。
  • MyBatis 避免了幾乎全部的 JDBC 代碼和手動設置參數以及獲取結果集。
  • MyBatis 可使用簡單的 XML 或註解來配置和映射原生類型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 對象)爲數據庫中的記錄。
  • MyBatis 本是 apache 的一個開源項目iBatis, 2010年這個項目由 apache software foundation 遷移到了 google code,而且更名爲MyBatis 。
  • 2013年11月遷移到Github。

如何得到 MyBatis?mysql

1.二、持久化

數據持久化shell

  • 持久化就是將程序的數據在持久狀態和瞬時狀態轉化的過程
  • 內存:斷電即失
  • 數據庫(jdbc),io 文件持久化。
  • 生活:冷藏. 罐頭。

爲何須要須要持久化?數據庫

  • 有一些對象,不能讓他丟掉。
  • 內存太貴了

1.三、持久層

Dao 層,Service 層,Controller 層…apache

  • 完成持久化工做的代碼塊
  • 層界限十分明顯。

1.4 爲何須要 Mybatis ?

  • 幫助程序猿將數據存入到數據庫中。
  • 方便
  • 傳統的 JDBC 代碼太複雜了。簡化。框架。自動化。
  • 不用 MyBatis 也能夠。更容易上手。 技術沒有高低之分
  • 優勢:
    • 簡單易學
    • 靈活
    • SQL 和代碼的分離,提升了可維護性。
    • 提供映射標籤,支持對象與數據庫的 ORM 字段關係映射
    • 提供對象關係映射標籤,支持對象關係組建維護
    • 提供 XML 標籤,支持編寫動態 SQL 。

最重要的一點:使用的人多!緩存

二、環境搭建

2.一、搭建實驗數據庫

CREATE DATABASE `mybatis`;

USE `mybatis`;

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
`id` int(20) NOT NULL,
`name` varchar(30) DEFAULT NULL,
`pwd` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `user`(`id`,`name`,`pwd`) values (1,'狂神','123456'),(2,'張三','abcdef'),(3,'李四','987654');

2.二、導入 Maven 相關依賴

<dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis</artifactId>
   <version>3.5.2</version>
</dependency>
<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.47</version>
</dependency>

2.三、編寫 MyBatis 核心配置文件

能夠根據幫助文檔來進行編寫

<?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.jdbc.Driver"/>
               <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
               <property name="username" value="root"/>
               <property name="password" value="123456"/>
           </dataSource>
       </environment>
   </environments>
   <mappers>
       <mapper resource="com/kuang/dao/userMapper.xml"/>
   </mappers>
</configuration>

2.四、編寫 MyBatis 工具類

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 {
       try {
           String resource = "mybatis-config.xml";
           InputStream inputStream = Resources.getResourceAsStream(resource);
           sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
      } catch (IOException e) {
           e.printStackTrace();
      }
  }

   //獲取SqlSession鏈接
   public static SqlSession getSession(){
       return sqlSessionFactory.openSession();
  }

}

2.五、建立實體類

public class User {
   
   private int id;  //id
   private String name;   //姓名
   private String pwd;   //密碼
   
   //構造,有參,無參
   //set/get
   //toString()
   
}

2.六、編寫 Mapper 接口

import com.kuang.pojo.User;
import java.util.List;

public interface UserMapper {
   List<User> selectUser();
}

2.七、編寫 Mapper.xml 配置文件

注意 namespace 不要寫錯!

<?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.kuang.dao.UserMapper">
    
 <select id="selectUser" resultType="com.kuang.pojo.User">
  	select * from user
 </select>
    
</mapper>

2.八、編寫測試類

**junit 包測試 **

public class MyTest {
   @Test
   public void selectUser() {
       SqlSession session = MybatisUtils.getSession();
       //方法一:
       //List<User> users = session.selectList("com.kuang.mapper.UserMapper.selectUser");
       
       //方法二:
       UserMapper mapper = session.getMapper(UserMapper.class);
       List<User> users = mapper.selectUser();

       for (User user: users){
           System.out.println(user);
      }
       session.close();
  }
}

出現的問題:

一、Maven 靜態資源過濾問題

<resources>
   <resource>
       <directory>src/main/java</directory>
       <includes>
           <include>**/*.properties</include>
           <include>**/*.xml</include>
       </includes>
       <filtering>false</filtering>
   </resource>
   <resource>
       <directory>src/main/resources</directory>
       <includes>
           <include>**/*.properties</include>
           <include>**/*.xml</include>
       </includes>
       <filtering>false</filtering>
   </resource>
</resources>

三、CRUD操做配置解析

3.一、select 標籤

  • select標籤是mybatis中最經常使用的標籤之一

  • select語句有不少屬性能夠詳細配置每一條SQL語句

    • SQL語句返回值類型。【完整的類名或者別名】
    • 傳入SQL語句的參數類型 。【萬能的Map,能夠多嘗試使用】
    • 命名空間中惟一的標識符
    • 接口中的方法名與映射文件中的SQL語句ID 一一對應
    • id
    • parameterType
    • resultType

練習 1 :根據 id 查詢 用戶

1.在 UserMapper 中添加對應方法

public interface UserMapper {
   //查詢所有用戶
   List<User> selectUser();
   //根據id查詢用戶
   User selectUserById(int id);
}

2.在UserMapper.xml中添加 select 語句

<select id="selectUserById" resultType="com.anti.pojo.User">
	select * from user where id = #{id}
</select>

3.在測試類中測試

@Test
public void tsetSelectUserById() {
   SqlSession session = MybatisUtils.getSession();  //獲取SqlSession鏈接
   UserMapper mapper = session.getMapper(UserMapper.class);
   User user = mapper.selectUserById(1);
   System.out.println(user);
   session.close();
}

4.運行結果

運行結果

練習2:根據 密碼 和名字 查詢用戶

方法一:直接在方法中傳遞參數

  • ​ 在接口方法中的參數前加 @Param 屬性。
  • ​ SQL 語句編寫的時候,直接取 @Param 中設置的值便可,不須要到單獨設置參數類型。
//經過密碼和名字查詢用戶
User selectUserByNP(@Param("username") String username,@Param("pwd") String pwd);


//mapper.xml
<select id="selectUserByNP" resultType="com.kuang.pojo.User">
    select * from user where name = #{username} and pwd = #{pwd}
</select>

方法二:萬能Map

  • 在接口方法中,參數數直接傳遞Map。
User selectUserByNP2(Map<String,Object> map);
  • 在編寫SQL語句的時候,須要傳遞參數類型 parameterType="map"
<select id="selectUserByNP2" parameterType="map" resultType="com.kuang.pojo.User">
    select * from user where name = #{username} and pwd = #{pwd}
</select>
  • 在使用方法的時候,Map 的 KEY 爲 SQL 中取的值便可。
@Test
public void test03(){
    SqlSession session = MybatisUtils.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("username","張三");
    map.put("pwd","abcdef");
    User user = mapper.selectUserByNP2(map);
    System.out.println(user);
}

若是參數過多,咱們能夠考慮直接使用 Map 實現,若是參數比較少,直接傳遞參數便可。

3.二、insert 標籤

咱們通常使用 insert 標籤進行插入操做,它的配置和 select 標籤差很少.

練習1:增長一個用戶

1.在 UserMapper 接口中添加對應的方法

//添加一個用戶
int addUser(User user);

二、在UserMapper.xml中添加insert語句

<insert id="addUser" parameterType="com.anti.pojo.User">
    insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>

3.測試

@Test
public void testAddUser() {
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);
   User user = new User(5,"王五","zxcvbn");
   int i = mapper.addUser(user);
   System.out.println(i);
   session.commit(); //提交事務,重點!不寫的話不會提交到數據庫
   session.close();
}

注意點:增、刪、改操做須要提交事務!

3.三、update 標籤

咱們通常使用update標籤進行更新操做,它的配置和select標籤差很少。

練習:修改用戶的信息

一、同理,編寫接口方法

//修改一個用戶
int updateUser(User user);

二、編寫對應的配置文件SQL

<update id="updateUser" parameterType="com.kuang.pojo.User">
  update user set name=#{name},pwd=#{pwd} where id = #{id}
</update>

三、測試

@Test
public void testUpdateUser() {
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);
   User user = mapper.selectUserById(1);
   user.setPwd("asdfgh");
   int i = mapper.updateUser(user);
   System.out.println(i);
   session.commit(); //提交事務,重點!不寫的話不會提交到數據庫
   session.close();
}

3.四、delete 標籤

需求:根據id刪除一個用戶

一、同理,編寫接口方法

//根據id刪除用戶
int deleteUser(int id);

二、編寫對應的配置文件SQL

<delete id="deleteUser" parameterType="int">
  delete from user where id = #{id}
</delete>

三、測試

@Test
public void testDeleteUser() {
   SqlSession session = MybatisUtils.getSession();
   UserMapper mapper = session.getMapper(UserMapper.class);
   int i = mapper.deleteUser(5);
   System.out.println(i);
   session.commit(); //提交事務,重點!不寫的話不會提交到數據庫
   session.close();
}

小結:

  • 全部的 增、刪、改操做都須要提交事務!
  • 全部的普通參數,儘可能都寫上 @Param 參數,尤爲是多個參數時,必須寫上!
  • 有時候根據業務的需求,能夠考慮使用 map 傳遞參數!
  • 爲了規範操做, 在 SQL 的配置文件中,咱們儘可能將 parameterTyperesultType 都寫上!

3.五、模糊查詢

第1種(推薦):在 Java代碼中添加 SQL通配符。

List<User> users = mapper.selectLikeUser("%朱%");
<select id="selectLikeUser">
	select * from user where name like #{name}
</select>

第2種(不推薦):在 SQL 語句中拼接通配符,會引發 SQL 注入。

String name = "朱";
List<User> users = mapper.selectLikeUser(name);
<select id="selectLikeUser">
	select * from user where name like "%" #{name} "%"
</select>

四、配置解析

4.一、mybatis-config.xml 核心配置文件

  • MyBatis 的配置文件包含了會深深影響 MyBatis 行爲的設置和屬性信息。
  • 能配置的內容以下:
configuration(配置)
properties(屬性)
settings(設置)
typeAliases(類型別名)
typeHandlers(類型處理器)
objectFactory(對象工廠)
plugins(插件)
environments(環境配置)
environment(環境變量)
transactionManager(事務管理器)
dataSource(數據源)
databaseIdProvider(數據庫廠商標識)
mappers(映射器)
<!-- 注意元素節點的順序!順序不對會報錯 -->

咱們能夠閱讀 mybatis-config.xml 上面的 dtd 的頭文件!

4.二、environments 元素

<environments default="development">
       <environment id="development">
           <transactionManager type="JDBC"/>
           <dataSource type="POOLED">
               <property name="driver" value="com.mysql.jdbc.Driver"/>
               <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8"/>
               <property name="username" value="root"/>
               <property name="password" value="123456"/>
           </dataSource>
       </environment>
   </environments>

配置 MyBatis 的多套運行環境,將 SQL 映射到多個不一樣的數據庫上,必須指定其中一個爲默認運行環境(經過default指定)

  • 子元素節點:environment

    • dataSource 元素使用標準的 JDBC 數據源接口來配置 JDBC 鏈接對象的資源。

    • 數據源是必須配置的。

    • 有三種內建的數據源類型

      type="[UNPOOLED|POOLED|JNDI]")
    • UNPOOLED:這個數據源的實現只是每次被請求時打開和關閉鏈接。

    • POOLED:這種數據源的實現利用「池」的概念將 JDBC 鏈接對象組織起來 , 這是一種使得併發 Web 應用快速響應請求的流行處理方式。

    • JNDI:這個數據源的實現是爲了能在如 Spring 或應用服務器這類容器中使用,容器能夠集中或在外部配置數據源,而後放置一個 JNDI 上下文的引用。

    • 數據源也有不少第三方的實現,好比dbcp,c3p0,druid等等....

  • 子元素節點:transactionManager - [ 事務管理器 ]

<!-- 語法 -->
<transactionManager type="[ JDBC | MANAGED ]"/>

4.三、mappers 元素

mappers

  • 映射器 : 定義映射SQL語句文件
  • 既然 MyBatis 的行爲其餘元素已經配置完了,咱們如今就要定義 SQL 映射語句了。可是首先咱們須要告訴 MyBatis 到哪裏去找到這些語句。Java 在自動查找這方面沒有提供一個很好的方法,因此最佳的方式是告訴 MyBatis 到哪裏去找映射文件。你可使用相對於類路徑的資源引用, 或徹底限定資源定位符(包括 file:/// 的 URL),或類名和包名等。映射器是MyBatis中最核心的組件之一,在MyBatis 3以前,只支持xml映射器,即:全部的SQL語句都必須在xml文件中配置。而從MyBatis 3開始,還支持接口映射器,這種映射器方式容許以Java代碼的方式註解定義SQL語句,很是簡潔。
<!-- 使用相對於類路徑的資源引用 -->
<mappers>
	<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用徹底限定資源定位符(URL) -->
<mappers>
	<mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>
<!--
使用映射器接口實現類的徹底限定類名
須要配置文件名稱和接口名稱一致,而且位於同一目錄下
-->
<mappers>
	<mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
<!--
將包內的映射器接口實現所有註冊爲映射器
可是須要配置文件名稱和接口名稱一致,而且位於同一目錄下
-->
<mappers>
	<package name="org.mybatis.builder"/>
</mappers>

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">
<mapper namespace="com.kuang.mapper.UserMapper">
   
</mapper>

namespace 中文意思:命名空間,做用以下:

  • ​ namespace 的命名必須跟某個接口同名
  • ​ 接口中的方法與映射文件中 sql 語句 id 應該一 一對應

namespace 和子元素的 id 聯合保證惟一 , 區別不一樣的mapper

綁定 DAO 接口

namespace 命名規則 : 包名 + 類名

4.四、properties 優化

數據庫這些屬性都是可外部配置且可動態替換的,既能夠在典型的 Java 屬性文件中配置,亦可經過 properties 元素的子元素來傳遞。具體的請參考官方文檔

咱們來優化咱們的配置文件

第一步 ; 在resources資源目錄下新建一個 db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
username=root
password=123456

第二步 : 將文件導入 properties 配置文件

<configuration>
   <!--導入properties文件-->
   <properties resource="db.properties"/>

   <environments default="development">
       <environment id="development">
           <transactionManager type="JDBC"/>
           <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>
   <mappers>
       <mapper resource="mapper/UserMapper.xml"/>
   </mappers>
</configuration>

五、typeAliases優化

類型別名是爲 Java 類型設置一個短的名字。它只和 XML 配置有關,存在的意義僅在於用來減小類徹底限定名的冗餘。

<!--配置別名,注意順序-->
<typeAliases>
   <typeAlias type="com.anti.pojo.User" alias="User"/>
</typeAliases>

當這樣配置時,User 能夠用在任何使用 com.kuang.pojo.User 的地方。

也能夠指定一個包名,MyBatis 會在包名下面搜索須要的 Java Bean,好比:

<typeAliases>
   <package name="com.anti.pojo"/>
</typeAliases>

每個在包 com.anti.pojo 中的 Java Bean,在沒有註解的狀況下,會使用 Bean 的首字母小寫的非限定類名來做爲它的別名。

如有註解,則別名爲其註解值。見下面的例子:

@Alias("user")
public class User {
  ...
}

去官網查看一下Mybatis默認的一些類型別名: https://mybatis.org/mybatis-3/zh/configuration.html#typeAliases

六、其餘配置瀏覽

6.一、設置

設置(settings)相關 => 查看幫助文檔

懶加載

日誌實現

緩存開啓關閉

一個配置完整的 settings 元素的示例以下:

<settings>
 <setting name="cacheEnabled" value="true"/>
 <setting name="lazyLoadingEnabled" value="true"/>
 <setting name="multipleResultSetsEnabled" value="true"/>
 <setting name="useColumnLabel" value="true"/>
 <setting name="useGeneratedKeys" value="false"/>
 <setting name="autoMappingBehavior" value="PARTIAL"/>
 <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
 <setting name="defaultExecutorType" value="SIMPLE"/>
 <setting name="defaultStatementTimeout" value="25"/>
 <setting name="defaultFetchSize" value="100"/>
 <setting name="safeRowBoundsEnabled" value="false"/>
 <setting name="mapUnderscoreToCamelCase" value="false"/>
 <setting name="localCacheScope" value="SESSION"/>
 <setting name="jdbcTypeForNull" value="OTHER"/>
 <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

6.二、類型處理器

  • 不管是 MyBatis 在預處理語句(PreparedStatement)中設置一個參數時,仍是從結果集中取出一個值時, 都會用類型處理器將獲取的值以合適的方式轉換成 Java 類型。
  • 你能夠重寫類型處理器或建立你本身的類型處理器來處理不支持的或非標準的類型。【瞭解便可】

6.三、對象工廠

  • MyBatis 每次建立結果對象的新實例時,它都會使用一個對象工廠(ObjectFactory)實例來完成。
  • 默認的對象工廠須要作的僅僅是實例化目標類,要麼經過默認構造方法,要麼在參數映射存在的時候經過有參構造方法來實例化。
  • 若是想覆蓋對象工廠的默認行爲,則能夠經過建立本身的對象工廠來實現。【瞭解便可】

七、生命週期和做用域

理解咱們目前已經討論過的不一樣做用域和生命週期類是相當重要的,由於錯誤的使用會致使很是嚴重的併發問題。

咱們能夠先畫一個流程圖,分析一下 Mybatis 的執行過程!

image-20201205171543934

7.一、做用域理解

  • SqlSessionFactoryBuilder 的做用在於建立 SqlSessionFactory,建立成功後,SqlSessionFactoryBuilder 就失去了做用,因此它只能存在於建立 SqlSessionFactory 的方法中,而不要讓其長期存在。所以 SqlSessionFactoryBuilder 實例的最佳做用域是方法做用域(也就是局部方法變量)。
  • SqlSessionFactory 能夠被認爲是一個數據庫鏈接池,它的做用是建立 SqlSession 接口對象。由於 MyBatis 的本質就是 Java 對數據庫的操做,因此 SqlSessionFactory 的生命週期存在於整個 MyBatis 的應用之中,因此一旦建立了 SqlSessionFactory,就要長期保存它,直至再也不使用 MyBatis 應用,因此能夠認爲 SqlSessionFactory 的生命週期就等同於 MyBatis 的應用週期。
  • 因爲 SqlSessionFactory 是一個對數據庫的鏈接池,因此它佔據着數據庫的鏈接資源。若是建立多個 SqlSessionFactory,那麼就存在多個數據庫鏈接池,這樣不利於對數據庫資源的控制,也會致使數據庫鏈接資源被消耗光,出現系統宕機等狀況,因此儘可能避免發生這樣的狀況。
  • 所以在通常的應用中咱們每每但願 SqlSessionFactory 做爲一個單例,讓它在應用中被共享。因此說 SqlSessionFactory 的最佳做用域是應用做用域。
  • 若是說 SqlSessionFactory 至關於數據庫鏈接池,那麼 SqlSession 就至關於一個數據庫鏈接(Connection 對象),你能夠在一個事務裏面執行多條 SQL,而後經過它的 commit、rollback 等方法,提交或者回滾事務。因此它應該存活在一個業務請求中,處理完整個請求後,應該關閉這條鏈接,讓它歸還給 SqlSessionFactory,不然數據庫資源就很快被耗費精光,系統就會癱瘓,因此用 try...catch...finally... 語句來保證其正確關閉。
  • 因此 SqlSession 的最佳的做用域是請求或方法做用域。

image-20201205171555725

八、ReusltMap

8.一、在進行簡單查詢時,查詢出來的值爲 null

image-20201206133705016

能夠看到查詢出來的結果集中 username 屬性爲 null

8.二、緣由分析

MyBatis 會根據這些查詢的列名(會將列名轉化爲小寫,數據庫不區分大小寫) , 去對應的實體類中查找相應列名的 set方法 設值 , 因爲找不到 setUsernmae() , 因此 username 返回 null ; 【自動映射】

image-20201206133911746

8.三、解決方法

1.【不推薦】修改實體類的屬性名,使其和數據庫字段名一致。

image-20201206134056441

2.【不推薦】在 SQL 語句中使用別名對應實體類中的屬性名。

image-20201206134740333

3.【推薦】使用在 xxMapper.xml` 中使用 ResultMap 進行結果集映射。

image-20201206135146896

8.四、數據庫中,存在一對多,多對一的狀況時

【多對一】的處理:

  • 多個學生對應一個老師
  • 若是對於學生這邊,就是一個多對一的現象,即從學生這邊關聯一個老師。

image-20201206135928746

image-20201206141735576

image-20201206141721977

image-20201206141754594

【一對多】的處理:

image-20201206142906498

image-20201206142852540

image-20201206142917246

8.五、 總結:

一、關聯 - association

二、集合 - collection

三、因此 association 是用於一對一和多對一,而 collection 是用於一對多的關係

四、JavaType ofType 都是用來指定對象類型的

  • JavaType 是用來指定 pojo 中屬性的類型
  • ofType指定的是映射到 List 集合屬性中 pojo 的類型。

注意說明:

一、保證SQL的可讀性,儘可能通俗易懂

二、根據實際要求,儘可能編寫性能更高的SQL語句

三、注意屬性名和字段不一致的問題

四、注意一對多和多對一 中:字段和屬性對應的問題

五、儘可能使用Log4j,經過日誌來查看本身的錯誤

九、 Log日誌

9.一、思考

咱們在測試 SQL 的時候,要是可以在控制檯輸出 SQL 的話,是否是就可以有更快的排錯效率?

若是一個 數據庫相關的操做出現了問題,咱們能夠根據輸出的 SQL 語句快速排查問題。

對於以往的開發過程,咱們會常用到debug模式來調節,跟蹤咱們的代碼執行過程。可是如今使用 Mybatis 是基於接口,配置文件的源代碼執行過程。所以,咱們必須選擇日誌工具來做爲咱們開發,調節程序的工具。

Mybatis內置的日誌工廠提供日誌功能,具體的日誌實現有如下幾種工具:

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j
  • JDK logging

具體選擇哪一個日誌實現工具由 MyBatis 的內置日誌工廠肯定。它會使用最早找到的(按上文列舉的順序查找)。若是一個都未找到,日誌功能就會被禁用。

9.二、標準日誌實現

指定 MyBatis 應該使用哪一個日誌記錄實現。若是此設置不存在,則會自動發現日誌記錄實現。

<settings>
       <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

測試,能夠看到控制檯有大量的輸出!咱們能夠經過這些輸出來判斷程序到底哪裏出了Bug

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
Created connection 355115154.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@152aa092]
==>  Preparing: select * from teacher where id = ? 
==> Parameters: 1(Integer)
<==    Columns: id, name
<==        Row: 1, 秦老師
====>  Preparing: select * from student 
====> Parameters: 
<====    Columns: id, name, tid
<====        Row: 1, 小明, 1
<====        Row: 2, 小紅, 1
<====        Row: 3, 小張, 1
<====        Row: 4, 小李, 1
<====        Row: 5, 小王, 1
<====      Total: 5
<==      Total: 1
Teacher(id=null, name=秦老師, students=[Student(id=1, name=小明), Student(id=2, name=小紅), Student(id=3, name=小張), Student(id=4, name=小李), Student(id=5, name=小王)])

9.三、使用 Log4j

簡介:

  • Log4j是Apache的一個開源項目
  • 經過使用Log4j,咱們能夠控制日誌信息輸送的目的地:控制檯,文本,GUI組件....
  • 咱們也能夠控制每一條日誌的輸出格式;
  • 經過定義每一條日誌信息的級別,咱們可以更加細緻地控制日誌的生成過程。最使人感興趣的就是,這些能夠經過一個配置文件來靈活地進行配置,而不須要修改應用的代碼。

使用步驟:

一、導入log4j的包

<dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.17</version>
</dependency>

二、配置文件編寫

#將等級爲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/kuang.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

三、setting設置日誌實現

<settings>
   <setting name="logImpl" value="LOG4J"/>
</settings>

輸出結果:

[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 71706941.
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@446293d]
[com.anti.dao.TeacherMapper.getTeacherById]-==>  Preparing: select * from teacher where id = ? 
[com.anti.dao.TeacherMapper.getTeacherById]-==> Parameters: 1(Integer)
[com.anti.dao.TeacherMapper.student]-====>  Preparing: select * from student 
[com.anti.dao.TeacherMapper.student]-====> Parameters: 
[com.anti.dao.TeacherMapper.student]-<====      Total: 5
[com.anti.dao.TeacherMapper.getTeacherById]-<==      Total: 1
Teacher(id=null, name=秦老師, students=[Student(id=1, name=小明), Student(id=2, name=小紅), Student(id=3, name=小張), Student(id=4, name=小李), Student(id=5, name=小王)])

image-20201206145306565

十、分頁

在學習 MyBatis 等持久層框架的時候,會常常對數據進行增刪改查操做,使用最多的是對數據庫進行查詢操做,若是查詢大量數據的時候,咱們每每使用分頁進行查詢,也就是每次處理小部分數據,這樣對數據庫壓力就在可控範圍內。

使用Limit實現分頁

#語法
SELECT * FROM table LIMIT page,pageSize

page = 當前頁數 = (page-1)*pageSize
pageSize = 一頁多少條數據

UserMapper.java

public interface UserMapper {
    //選擇所有用戶實現分頁
    List<User> selectUser(Map<String,Integer> map);
}

UserMapper.xml

<select id="selectUser" parameterType="map" resultType="com.anti.pojo.User">
    select * from user limit #{page},#{pageSize}
</select>

MyTest.java

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

    HashMap<String, Integer> map = new HashMap<String, Integer>();
    int page = 1; //第幾頁
    int limit = 10; //每頁多少數據
    map.put("page",(page-1) * limit);
    map.put("limit",limit);

    List<User> users = mapper.selectUser(map);
    for (User user : users) {
        System.out.println(user);
    }
    session.close();
}

結果 :

==>  Preparing: select * from user limit ?,? 
==> Parameters: 0(Integer), 10(Integer)
<==    Columns: uid, name, gender, birthday, dept, cno, address, phone, remark, password, type
<==        Row: 999, 管理員, M, 2020/09/02, AAA, 0, AAA, null, null, 123, 0
<==        Row: 10101, 怡香, M, 2020/02/08, Accounting, 1, Cameroon, 823-954-4217, null, 1, 1
<==        Row: 10102, 惟楓, M, 2020/01/25, Accounting, 2, Palestinian Territory, 978-827-9275, null, 2, 1
<==        Row: 10103, 海程, M, 2020/09/05, Human Resources, 3, Thailand, 978-712-9955, null, 3, 1
<==        Row: 10104, 琪煜, M, 2020/10/07, Accounting, 4, Palestinian Territory, 730-153-0025, null, 4, 1
<==        Row: 10105, 彥軍, F, 2020/11/05, Services, 5, China, 504-460-1356, null, 5, 1
<==        Row: 10106, 宇涵, F, 2020/11/08, Product Management, 6, Argentina, 252-143-6848, null, 6, 1
<==        Row: 10107, 辰華, M, 2019/11/25, Business Development, 7, Philippines, 884-928-7856, null, 7, 1
<==        Row: 10108, 曉烽, M, 2020/08/05, Engineering, 8, Philippines, 152-366-5638, null, 8, 1
<==        Row: 10109, 尹智, F, 2020/01/12, Human Resources, 9, Argentina, 803-602-3704, null, 9, 1
<==      Total: 10

十一、使用註解開發

MyBatis最初配置信息是基於 XML ,映射語句(SQL)也是定義在 XML 中的。而到MyBatis 3提供了新的基於註解的配置。不幸的是,java 註解的的表達力和靈活性十分有限。

最強大的 MyBatis 映射並不能用註解來構建

  • 註解主要分紅 :
    • @select ()
    • @update ()
    • @Insert ()
    • @delete ()

注意:利用註解開發就不須要mapper.xml映射文件了 .

1.在接口中添加註解

public interface UserMapper {
	//查詢所有用戶
    @Select("select * from user")
    List<User> getUsers();
}

2.在mybatis的核心配置文件中注入

<!--使用class綁定接口-->
<mappers>
   <mapper class="com.anti.mapper.UserMapper"/>
</mappers>

3.測試

@Test
public void test01(){
    UserMapper mapper = MybatisUtils.getSession().getMapper(UserMapper.class);
    List<User> users = mapper.getUsers();
    for (User user : users) {
        System.out.println(user);
    }
}

4.結果

Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3c0be339]
==>  Preparing: select * from user 
==> Parameters: 
<==    Columns: id, name, pwd
<==        Row: 1, 狂神, 123456
<==        Row: 2, 張三, abcdef
<==        Row: 3, 李四, 987654
<==        Row: 5, 王五, zxcvbn
<==      Total: 4
User(id=1, name=狂神, pwd=123456)
User(id=2, name=張三, pwd=abcdef)
User(id=3, name=李四, pwd=987654)
User(id=5, name=王五, pwd=zxcvbn)

Process finished with exit code 0

十二、動態SQL

12.一、if 語句

<!--需求1:
根據做者名字和博客名字來查詢博客!
若是做者名字爲空,那麼只根據博客名字查詢,反之,則根據做者名來查詢
select * from blog where title = #{title} and author = #{author}
-->
<select id="queryBlogIf" parameterType="map" resultType="blog">
  select * from blog where
   <if test="title != null">
      title = #{title}
   </if>
   <if test="author != null">
      and author = #{author}
   </if>
</select>

這樣寫咱們能夠看到,若是 author 等於 null,那麼查詢語句爲 select * from user where title=#{title},可是若是 title 爲空呢?那麼查詢語句爲 select * from user where and author=#{author},這是錯誤的 SQL 語句,如何解決呢?請看下面的 where 語句!

12.二、where 語句

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

這個「where」標籤會知道若是它包含的標籤中有返回值的話,它就插入一個‘where’。此外,若是標籤返回的內容是以AND 或OR 開頭的,則它會剔除掉。

12.三、set 語句

同理,上面的對於查詢 SQL 語句包含 where 關鍵字,若是在進行更新操做的時候,含有 set 關鍵詞,咱們怎麼處理呢?

<!--注意set是用的逗號隔開-->
<update id="updateBlog" parameterType="map">
  update blog
     <set>
         <if test="title != null">
            title = #{title},
         </if>
         <if test="author != null">
            author = #{author}
         </if>
     </set>
  where id = #{id};
</update>

12.四、choose 語句

有時候,咱們不想用到全部的查詢條件,只想選擇其中的一個,查詢條件有一個知足便可,使用 choose 標籤能夠解決此類問題,相似於 Java 的 switch 語句。

choose 與 if 的區別:

  • if 若是條件都知足會帶出全部知足條件的語句
  • choose 只會帶出最早知足的條件的語句
  • choose 若是沒有知足的條件會執行 otherwise 語句
<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>

12.五、SQL 片斷

有時候可能某個 sql 語句咱們用的特別多,爲了增長代碼的重用性,簡化代碼,咱們須要將這些代碼抽取出來,而後使用時直接調用。

提取SQL片斷:

<sql id="if-title-author">
   <if test="title != null">
      title = #{title}
   </if>
   <if test="author != null">
      and author = #{author}
   </if>
</sql>

引用SQL片斷:

<select id="queryBlogIf" parameterType="map" resultType="blog">
  select * from blog
   <where>
       <!-- 引用 sql 片斷,若是refid 指定的不在本文件中,那麼須要在前面加上 namespace -->
       <include refid="if-title-author"></include>
       <!-- 在這裏還能夠引用其餘的 sql 片斷 -->
   </where>
</select>

注意:

  1. 最好基於 單表來定義 sql 片斷,提升片斷的可重用性
  2. 在 sql 片斷中不要包括 where

12.六、foreach 語句

需求:咱們須要查詢 blog 表中 id 分別爲1,2,3的博客信息

<select id="queryBlogForeach" parameterType="map" resultType="blog">
  select * from blog
   <where>
       <!--
       collection:指定輸入對象中的集合屬性
       item:每次遍歷生成的對象
       open:開始遍歷時的拼接字符串
       close:結束時拼接的字符串
       separator:遍歷對象之間須要拼接的字符串
       select * from blog where 1=1 and (id=1 or id=2 or id=3)
     -->
       <foreach collection="ids"  item="id" open="and (" close=")" separator="or">
          id=#{id}
       </foreach>
   </where>
</select>

小結:

其實動態 sql 語句的編寫每每就是一個拼接的問題,爲了保證拼接準確,咱們最好首先要寫原生的 sql 語句出來,而後在經過 mybatis 動態sql 對照着改,防止出錯。多在實踐中使用纔是熟練掌握它的技巧。

1三、緩存

一、什麼是緩存 [ Cache ]?

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

二、爲何使用緩存?

  • 減小和數據庫的交互次數,減小系統開銷,提升系統效率。

三、什麼樣的數據能使用緩存?

  • 常常查詢而且不常常改變的數據。

13.一、Mybatis緩存

  • MyBatis包含一個很是強大的查詢緩存特性,它能夠很是方便地定製和配置緩存。緩存能夠極大的提高查詢效率。

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

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

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

    • 爲了提升擴展性,MyBatis定義了緩存接口Cache。咱們能夠經過實現Cache接口來自定義二級緩存

13.二、一級緩存

一級緩存也叫本地緩存:

  • 與數據庫同一次會話期間查詢到的數據會放在本地緩存中。

  • 之後若是須要獲取相同的數據,直接從緩存中拿,沒必須再去查詢數據庫;

1.在mybatis中加入日誌,方便測試結果

2.編寫接口方法

//根據id查詢用戶
User queryUserById(@Param("id") int id);

3.接口對應的Mapper文件

<select id="queryUserById" resultType="user">
  select * from user where id = #{id}
</select>

4.測試

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

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

   session.close();
}

5.結果分析

image-20201206213629212

13.三、一級緩存失效的四種狀況:

一級緩存是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相同,兩次查詢之間執行了增刪改操做!

@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

13.四、二級緩存

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();
}

image-20201206214729672

結論:

  • 只要開啓了二級緩存,咱們在同一個Mapper中的查詢,能夠在二級緩存中拿到數據
  • 查出的數據都會被默認先放在一級緩存中
  • 只有會話提交或者關閉之後,一級緩存中的數據纔會轉到二級緩存中

緩存原理圖:

image-20201206214908158

13.五、第三方緩存實現 EhCache

image-20210130005613005

Ehcache是一種普遍使用的 java 分佈式緩存,用於通用緩存;

1.要在應用程序中使用Ehcache,須要引入依賴的 jar 包

<!-- 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>

2.在mapper.xml中使用對應的緩存便可

<mapper namespace = 「org.acme.FooMapper」 >
   <cache type = 「org.mybatis.caches.ehcache.EhcacheCache」 />
</mapper>

3.編寫ehcache.xml文件,若是在加載時未找到/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:爲緩存路徑,ehcache分爲內存和磁盤兩級,此屬性定義磁盤的緩存位置。參數解釋以下:
      user.home – 用戶主目錄
      user.dir – 用戶當前工做目錄
      java.io.tmpdir – 默認臨時文件路徑
    -->
   <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"/>
   <!--
      defaultCache:默認緩存策略,當ehcache找不到定義的緩存時,則使用這個緩存策略。只能定義一個。
    -->
   <!--
     name:緩存名稱。
     maxElementsInMemory:緩存最大數目
     maxElementsOnDisk:硬盤最大緩存個數。
     eternal:對象是否永久有效,一但設置了,timeout將不起做用。
     overflowToDisk:是否保存到磁盤,當系統當機時
     timeToIdleSeconds:設置對象在失效前的容許閒置時間(單位:秒)。僅當eternal=false對象不是永久有效時使用,可選屬性,默認值是0,也就是可閒置時間無窮大。
     timeToLiveSeconds:設置對象在失效前容許存活時間(單位:秒)。最大時間介於建立時間和失效時間之間。僅當eternal=false對象不是永久有效時使用,默認是0.,也就是對象存活時間無窮大。
     diskPersistent:是否緩存虛擬機重啓期數據 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
     diskSpoolBufferSizeMB:這個參數設置DiskStore(磁盤緩存)的緩存區大小。默認是30MB。每一個Cache都應該有本身的一個緩衝區。
     diskExpiryThreadIntervalSeconds:磁盤失效線程運行時間間隔,默認是120秒。
     memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理內存。默認策略是LRU(最近最少使用)。你能夠設置爲FIFO(先進先出)或是LFU(較少使用)。
     clearOnFlush:內存數量最大時是否清除。
     memoryStoreEvictionPolicy:可選策略有:LRU(最近最少使用,默認策略)、FIFO(先進先出)、LFU(最少訪問次數)。
     FIFO,first in first out,這個是你們最熟的,先進先出。
     LFU, Less Frequently Used,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面所講,緩存的元素有一個hit屬性,hit值最小的將會被清出緩存。
     LRU,Least Recently Used,最近最少使用的,緩存的元素有一個時間戳,當緩存容量滿了,而又須要騰出地方來緩存新的元素的時候,那麼現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。
  -->

</ehcache>

1四、Mybatis詳細的執行流程

img

相關文章
相關標籤/搜索