MyBatis 學習

一、Mybatis 概念

  • Mybatis 是一款優秀得持久層框架,它支援自定義 SQL、存儲過程以及高級映射。MyBatis 免除了幾乎所有得 JDBC 程式碼以及設置參數和獲取結果集的工作。Mybatis 可以通過簡單的 XML 或註解來配置和映射原始類型、介面和 Java POJO 為資料庫中的記錄。
  • ORM (Object Relational Mapping):對象關係映射
  • Mybatis 是半自動花的 ORM 框架,依賴一個配置文件,將資料庫和 Java POJO 中的實體類的關係解耦,可以在內部自由編寫 SQL 語句。
  • 中文文檔://mybatis.org/mybatis-3/zh/index.html
  • Github://github.com/mybatis/mybatis-3

二、Mybatis 配置開發

2.1、開發步驟

1、pom 文件導入相關依賴
maven 倉庫獲取相關依賴://mvnrepository.com/
2、編寫核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration       
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"    
   "//mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>   
<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.xml"/>  
 </mappers>
</configuration>

3、編寫 MyBatis 工具類

  • 中文官方文檔有示範程式碼,其主要作用是從 XML 文件中構建 SqlSessionFactory 的實例。
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();
  }
}

4、創建實體類

  • 與資料庫中定義的 欄位進行映射

5、定義 Dao 層的介面

6、編寫 Mapper 配置文件

  • 介面實現類由原來的UserDaoImpl轉變成一個Mapper配置文件。

2.2、核心配置文件解析

1、常用標籤

configuration(配置):核心根標籤
properties(屬性):<properties resource = 'xxxxx'> —— 引入資料庫連接的配置文件
settings(設置):配置 日誌
typeAliases(類型別名    :起別名
typeHandlers(類型處理器)
objectFactory(對象工廠)
plugins(插件)
environments(環境配置)
environment(環境變數)
transactionManager(事務管理器)
dataSource(數據源)
databaseIdProvider(資料庫廠商標識)
mappers(映射器) :引入映射配置文件
<!-- 注意元素節點的順序!順序不對會報錯 -->

2、Properties 優化

  • 資料庫這些屬性都是可外部配置且可動態替換的,既可以在典型的 Java 屬性文件中配置,亦可通過 properties 元素的子元素來傳遞。具體的官方文檔
  • 優化步驟
    • 第一步 ; 在資源目錄下新建一個db.properties
    • 第二步 : 將文件導入properties 配置文件,通過 導入
  • 配置文件優先順序問題:如果兩個文件有同一個欄位,優先使用外部配置文件的。

3、TypeAliases 優化

  • 類型別名是為 Java 類型設置一個短的名字。它只和 XML 配置有關,存在的意義僅在於用來減少類完全限定名的冗餘。
<!--配置別名,注意順序--><typeAliases>  
 <typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
<!--當這樣配置時,User可以用在任何使用com.kuang.pojo.User的地方。-->
  • 也可以指定一個包名,MyBatis 會在包名下面搜索需要的 Java Bean。它的默認別名就為這個類的類名,首字母小寫!
  • 若有註解,則別名為其註解值。

4、environments 環境配置

  • 配置 MyBatis 的多套運行環境,將 SQL 映射到多個不同的資料庫上,但必須指定其中一個默認運行環境(通過 default 指定)
  • MyBatis 默認的事務管理器是 JDBC,默認連接池是 POOLED

5、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>

2.3、Mapper 映射文件解析

1、常用標籤

mapper:核心根標籤
namespace 屬性:名稱空間
select :查詢功能標籤
id 屬性:唯一標識
resultType:指定結果映射對象類型
parameterType:指定參數映射對象類型(在 MyBatis 內部會自動識別參數,並推導其屬性,故該參數可以不配置)

2、namespace 詳解

  • namespace中文意思:命名空間,作用如下:
    • namespace的命名必須跟某個介面同名
    • 介面中的方法與映射文件中sql語句id應該一一對應
    • namespace和子元素的id聯合保證唯一 , 區別不同的mapper
    • 綁定DAO介面
    • namespace命名規則 : 包名+類名

2.4、作用域(Scope)和生命周期

1、生命周期

2、作用域

  • 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 的最佳的作用域是請求或方法作用域。

2.5、解決屬性名和欄位名不一致問題

1、為列名指定別名 , 別名和java實體類的屬性名一致

2、使用結果集映射->ResultMap 【推薦】

  • 自動映射
  • resultMap 元素是 MyBatis 中最重要最強大的元素。它可以讓你從 90% 的 JDBC ResultSets 數據提取程式碼中解放出來。
  • 實際上,在為一些比如連接的複雜語句編寫映射程式碼的時候,一份 resultMap 能夠代替實現同等功能的長達數千行的程式碼。
  • ResultMap 的設計思想是,對於簡單的語句根本不需要配置顯式的結果映射,而對於複雜一點的語句只需要描述它們的關係就行了。
    你已經見過簡單映射語句的示例了,但並沒有顯式指定 resultMap。比如:
<select id="selectUserById" resultType="map">
select id , name , pwd
  from user
  where id = #{id}
</select>
  • 上述語句只是簡單地將所有的列映射到 HashMap 的鍵上,這由 resultType 屬性指定。雖然在大部分情況下都夠用,但是 HashMap 不是一個很好的模型。你的程式更可能會使用 JavaBean 或 POJO(Plain Old Java Objects,普通老式 Java 對象)作為模型。
    ResultMap 最優秀的地方在於,雖然你已經對它相當了解了,但是根本就不需要顯式地用到他們。

  • 手動映射

1、返回值類型為resultMap

<select id="selectUserById" resultMap="UserMap"> 
 select id , name , pwd from user where id = #{id}
</select>

2、編寫resultMap,實現手動映射!

<resultMap id="UserMap" type="User">   <!-- id為主鍵 -->  
 <id column="id" property="id"/>   <!-- column是資料庫表的列名 , property是對應實體類的屬性名 --> 
  <result column="name" property="name"/>   
  <result column="pwd" property="password"/>
</resultMap>

三、註解開發

3.1、開發步驟

1、pom 文件導入相關依賴

2、編寫核心配置文件

  • 配置文件內容和配置開發一致
  • 映射器引入方法更改為通過類引入:

3、編寫 MyBatis 工具類

4、創建實體類與資料庫中欄位對應

5、編寫介面

6、在介面方法上利用註解聲明 SQL 語句。

3.2、常用註解

  • @Insert:新增
  • @Update:更新
  • @Delete:刪除
  • @Select :查詢
  • @Results:多個結果集封裝
  • @One:實現一對一結果集封裝
  • @Many:實現一對多結果集封裝

3.3、註解解析

1、@Param註解用於給方法參數起一個名字。以下是總結的使用原則:

  • 在方法只接受一個參數的情況下,可以不使用@Param。
  • 在方法接受多個參數的情況下,建議一定要使用@Param註解給參數命名。
  • 如果參數是 JavaBean , 則不能使用@Param。
  • 不使用@Param註解時,參數只能有一個,並且是Javabean。

2、# 與 $ 的區別

  • ‘#'{} 的作用主要是替換預編譯語句(PrepareStatement)中的佔位符? 【推薦使用】
INSERT INTO user (name) VALUES (#{name});
INSERT INTO user (name) VALUES (?);
  • ${} 的作用是直接進行字元串替換
INSERT INTO user (name) VALUES ('${name}');
INSERT INTO user (name) VALUES ('kuangshen');
  • 使用註解和配置文件協同開發,才是MyBatis的最佳實踐!

四、多對一處理

4.1、按找結果嵌套處理

<!--按查詢結果嵌套處理
思路:
   1. 直接查詢出結果,進行結果集的映射
-->
<select id="getStudents2" resultMap="StudentTeacher2" >
  select s.id sid, s.name sname , t.name tname
  from student s,teacher t
  where s.tid = t.id
</select>

<resultMap id="StudentTeacher2" type="Student"> 
  <id property="id" column="sid"/>  
 <result property="name" column="sname"/>  
<!--關聯對象property 關聯對象在Student實體類中的屬性-->  
 <association property="teacher" javaType="Teacher">  
     <result property="name" column="tname"/> 
  </association>
</resultMap>

4.2、按照查詢嵌套處理

<resultMap id="StudentTeacher" type="Student"> 
 <!--association關聯屬性 property屬性名 javaType屬性類型 column在多的一方的表中的列名-->  
 <association property="teacher"  column="{id=tid,name=tid}" javaType="Teacher" select="getTeacher"/>
</resultMap>
<!--這裡傳遞過來的id,只有一個屬性的時候,下面可以寫任何值
association中column多參數配置:
   column="{key=value,key=value}"   其實就是鍵值對的形式,key是傳給下個sql的取值名稱,value是片段一中sql查詢的欄位名。
-->
<select id="getTeacher" resultType="teacher">  
  select * from teacher where id = #{id} and name = #{name}
</select>

2、小結

  • 按照查詢進行嵌套處理就像SQL中的子查詢
  • 按照結果進行嵌套處理就像SQL中的聯表查詢

五、一對多的處理

5.1、按結果嵌套處理

1、TeacherMapper介面編寫方法

//獲取指定老師,及老師下的所有學生public Teacher getTeacher(int id);

2、編寫介面對應的Mapper配置文件

<mapper namespace="com.kuang.mapper.TeacherMapper">  
 <!--   思路:     
  1. 從學生表和老師表中查出學生id,學生姓名,老師姓名  
     2. 對查詢出來的操作做結果集映射       
    1. 集合的話,使用collection!          
     JavaType和ofType都是用來指定對象類型的    
     JavaType是用來指定pojo中屬性的類型     
     ofType指定的是映射到list集合屬性中pojo的類型。   --> 
  <select id="getTeacher" resultMap="TeacherStudent">
      select s.id sid, s.name sname , t.name tname, t.id tid
      from student s,teacher t
      where s.tid = t.id and t.id=#{id}
   </select> 

  <resultMap id="TeacherStudent" type="Teacher"> 
      <result  property="name" column="tname"/> 
      <collection property="students" ofType="Student"> 
        <result property="id" column="sid" />   
        <result property="name" column="sname" />  
        <result property="tid" column="tid" /> 
      </collection>   
</resultMap>
</mapper>

5.2、按照查詢嵌套處理

1、TeacherMapper介面編寫方法

public Teacher getTeacher2(int id);

2、編寫介面對應的Mapper配置文件

<select id="getTeacher2" resultMap="TeacherStudent2">
select * from teacher where id = #{id}
</select>

<resultMap id="TeacherStudent2" type="Teacher">   <!--column是一對多的外鍵 , 寫的是一的主鍵的列名-->
  <collection property="students" javaType="ArrayList" ofType="Student" column="id"
 select="getStudentByTeacherId"/>
</resultMap>

<select id="getStudentByTeacherId" resultType="Student">  
select * from student where tid = #{id}
</select>

3、小結

1、關聯-association

2、集合-collection

3、所以association是用於一對一和多對一,而collection是用於一對多的關係

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

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

六、動態 SQL

6.1、動態 SQL 之

1、標籤

  • < if >:條件判斷標籤
  • < if test = “判斷條件”> and 查詢條件拼接 (注意標點符號)

2、注意

  • 單獨使用 標籤,拼接的 and 語句會使得 SQL 語句報錯。

3、解決方案

  • :條件標籤
  • 如果有動態條件,則使用該標籤代替 where
  • 這個標籤會知道如果它包含的標籤中有返回值的話,它就插入一個『where』。此外,如果標籤返回的內容是以AND 或OR 開頭的,則它會剔除掉。

6.2、動態 SQL 之

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

6.3、動態 SQL 之

  • 有時候,我們不想用到所有的查詢條件,只想選擇其中的一個,查詢條件有一個滿足即可,使用 choose 標籤可以解決此類問題,類似於 Java 的 switch 語句
<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>

6.4、動態 SQL 之

<select id="queryBlogForeach" parameterType="map" resultType="blog"> 
 select * from blog
   <where>       <!--       collection:指定輸入對象中的集合屬性 (list——集合,array——數組)       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>

七、快取

7.1、快取概念

1、什麼是快取

  • 存在記憶體中的臨時數據
  • 將用戶經常查詢的數據放在快取(記憶體)中,用戶去查詢數據就不會從磁碟上(關係型資料庫數據文件)查詢,從快取中查詢,從而提高查詢效率,解決了高並發系統的性能問題。

2、為什麼使用快取

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

3、什麼樣的數據能使用快取?

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

7.2、一級快取

1、一級快取特性

  • 默認情況下,只用一級快取開啟。(SqlSession 級別的快取,也稱為本地快取)
  • Spring 中,一級快取會失效。
  • 與資料庫同一次會話期間查詢到的數據會放在本地快取中。
  • 以後如果需要獲取相同的數據,直接從快取中拿,不需要在去查詢資料庫。

2、一級快取失效的四種狀況

  • SqlSession 不同
    • 每個 SqlSession 中的快取相互獨立
  • SqlSession 相同,查詢條件不同
    • 當前快取中,不存在這個數據
  • SqlSession 相同,兩次查詢之間執行了增刪改操作
    • 增刪改操作會,清除快取
  • SqlSession 相同,手動清除一級快取

7.3、二級快取

1、二級快取特性

  • 二級快取也叫全局快取,一級快取作用域太低了,所以誕生了二級快取
  • 基於namespace級別的快取,一個名稱空間,對應一個二級快取;
  • 工作機制
    • 一個會話查詢一條數據,這個數據就會被放在當前會話的一級快取中;
    • 如果當前會話關閉了,這個會話對應的一級快取就沒了;但是我們想要的是,會話關閉了,一級快取中的數據被保存到二級快取中;
    • 新的會話查詢資訊,就可以從二級快取中獲取內容;
    • 不同的mapper查出的數據會放在自己對應的快取(map)中;

2、結論

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

3、快取原理圖

Tags: