介紹

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

示例

搭建環境

<!--匯入依賴-->
<dependencies>
<!--mysqlq驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
</dependencies>

建立一個模組

編寫mybatis的核心配置檔案 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核心配置檔案-->
<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?userSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
</configuration>

編寫mybatis工具類

//sqlSessionFactory --> sqlSession
public class MybatisUtils { static SqlSessionFactory sqlSessionFactory = null; static {
try {
//使用Mybatis第一步 :獲取sqlSessionFactory物件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
} //既然有了 SqlSessionFactory,顧名思義,我們可以從中獲得 SqlSession 的例項.
// SqlSession 提供了在資料庫執行 SQL 命令所需的所有方法。
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}

編寫程式碼

namespace中的包名要和Dao/Mapper介面的包名一致

  • id:就是對應的namespace中的方法名;
  • resultType : Sql語句執行的返回值;
  • parameterType : 引數型別;
public interface UserDao {
public List<User> getUserList();
} <?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介面-->
<mapper namespace="com.zwt.dao.UserDao">
<select id="getUserList" resultType="com.kuang.pojo.User">
select * from USER
</select>
</mapper> @Test
public void test(){ //1.獲取SqlSession物件
SqlSession sqlSession = MybatisUtils.getSqlSession();
//2.執行SQL
// 方式一:getMapper
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
for (User user : userList) {
System.out.println(user);
} //關閉sqlSession
sqlSession.close();
}

CURD

注意:增刪改查一定要提交事務:

sqlSession.commit();

萬能Map

假設,我們的實體類,或者資料庫中的表,欄位或者引數過多,我們應該考慮使用Map!

//用萬能Map插入使用者
public void addUser2(Map<String,Object> map); <!--物件中的屬性可以直接取出來 傳遞map的key-->
<insert id="addUser2" parameterType="map">
insert into user (id,name,password) values (#{userid},#{username},#{userpassword})
</insert>

Map傳遞引數,直接在sql中取出key即可! 【parameter=“map”】

物件傳遞引數,直接在sql中取出物件的屬性即可! 【parameter=“Object”】

只有一個基本型別引數的情況下,可以直接在sql中取到

多個引數用Map , 或者註解!

配置解析

  • mybatis-config.xml
  • Mybatis的配置檔案包含了會深深影響MyBatis行為的設定和屬性資訊。
configuration(配置)
properties(屬性)
settings(設定)
typeAliases(類型別名)
typeHandlers(型別處理器)
objectFactory(物件工廠)
plugins(外掛)
environments(環境配置)
environment(環境變數)
transactionManager(事務管理器)
dataSource(資料來源)
databaseIdProvider(資料庫廠商標識)
mappers(對映器)

環境配置 environments

MyBatis 可以配置成適應多種環境

不過要記住:儘管可以配置多個環境,但每個 SqlSessionFactory 例項只能選擇一種環境

學會使用配置多套執行環境!

MyBatis預設的事務管理器就是JDBC ,連線池:POOLED

屬性 properties

我們可以通過properties屬性來實現引用配置檔案

這些屬性可以在外部進行配置,並可以進行動態替換。

你既可以在典型的 Java 屬性檔案中配置這些屬性,也可以在 properties 元素的子元素中設定。【db.poperties】

類型別名 typeAliases

  • 類型別名可為 Java 型別設定一個縮寫名字。 它僅用於 XML 配置.
  • 意在降低冗餘的全限定類名書寫。
<!--可以給實體類起別名-->
<typeAliases>
<typeAlias type="com.zwt.pojo.User" alias="User"/>
</typeAliases>
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases> @Alias("author")
public class Author {
...
}

其他配置

對映器 mappers

MapperRegistry:註冊繫結我們的Mapper檔案;

方式一:【推薦使用】

<!--每一個Mapper.xml都需要在MyBatis核心配置檔案中註冊-->
<mappers>
<mapper resource="com/zwt/dao/UserMapper.xml"/>
</mappers>

方式二:使用class檔案繫結註冊

<!--每一個Mapper.xml都需要在MyBatis核心配置檔案中註冊-->
<mappers>
<mapper class="com.zwt.dao.UserMapper"/>
</mappers> 注意點: 介面和他的Mapper配置檔案必須同名
介面和他的Mapper配置檔案必須在同一個包下

方式三:使用包掃描進行注入

<mappers>
<package name="com.kuang.dao"/>
</mappers>

作用域和生命週期

SqlSessionFactoryBuilder:

  • 一旦建立了SqlSessionFactory,就不再需要它了
  • 區域性變數

SqlSessionFactory:

  • 說白了就可以想象為:資料庫連線池
  • qlSessionFactory一旦被建立就應該在應用的執行期間一直存在,沒有任何理由丟棄它或重新建立一個例項
  • 因此SqlSessionFactory的最佳作用域是應用作用域(ApplocationContext)。
  • 最簡單的就是使用單例模式或靜態單例模式。

SqlSession:

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

分頁

用分頁是為了減少資料的處理量

使用Limit分頁

//分頁
List<User> getUserByLimit(Map<String,Integer> map); <!--分頁查詢-->
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from user limit #{startIndex},#{pageSize}
</select> @Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",1);
map.put("pageSize",2);
List<User> list = mapper.getUserByLimit(map);
for (User user : list) {
System.out.println(user);
}
}

RowBounds分頁

//分頁2
List<User> getUserByRowBounds(); <!--分頁查詢2-->
<select id="getUserByRowBounds">
select * from user limit #{startIndex},#{pageSize}
</select> public void getUserByRowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
//RowBounds實現
RowBounds rowBounds = new RowBounds(1, 2);
//通過Java程式碼層面實現分頁
List<User> userList = sqlSession.selectList("com.zwt.dao.UserMapper.getUserByRowBounds", null, rowBounds);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}

分頁外掛

註解開發

//註解在介面上實現
@Select("select * from user")
List<User> getUsers(); //需要在核心配置檔案中繫結介面
<mappers>
<mapper class="com.zwt.dao.UserMapper"/>
</mappers>

本質:反射機制實現

底層:動態代理

MyBatis詳細執行流程

註解CURD

//方法存在多個引數,所有的引數前面必須加上@Param("id")註解
@Delete("delete from user where id = ${uid}")
int deleteUser(@Param("uid") int id);

關於@Param( )註解

  • 基本型別的引數或者String型別,需要加上
  • 引用型別不需要加
  • 如果只有一個基本型別的話,可以忽略,但是建議大家都加上
  • 我們在SQL中引用的就是我們這裡的@Param()中設定的屬性名

多對一and一對多(處理)

多個學生一個老師;

多對一

alter table student ADD CONSTRAINT fk_tid foreign key (tid) references teacher(id)

測試環境搭建:

  1. 匯入lombok
  2. 新建實體類Teacher,Student
  3. 建立Mapper介面
  4. 建立Mapper.xml檔案
  5. 在核心配置檔案中繫結註冊我們的Mapper介面或者檔案 【方式很多,隨心選】
  6. 測試查詢是否能夠成功
@Data
public class Student {
private int id;
private String name;
private int tid;
} @Data
public class Student {
private int id;
private String name;
private int tid;
}
//按照查詢巢狀處理
<!--
思路:
1. 查詢所有的學生資訊
2. 根據查詢出來的學生的tid尋找特定的老師 (子查詢)
-->
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<!--複雜的屬性,我們需要單獨出來 物件:association 集合:collection-->
<collection property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
</resultMap>
<select id="getTeacher" resultType="teacher">
select * from teacher where id = #{id}
</select> //按照結果巢狀處理
<!--按照結果進行查詢-->
<select id="getStudent2" 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">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="teacher">
<result property="name" column="tname"></result>
</association>
</resultMap>
  • 子查詢 (按照查詢巢狀)
  • 聯表查詢 (按照結果巢狀)

一對多

<!--按結果巢狀查詢-->
<select id="getTeacher" resultMap="StudentTeacher">
SELECT s.id sid, s.name sname,t.name tname,t.id tid FROM student s, teacher t
WHERE s.tid = t.id AND tid = #{tid}
</select>
<resultMap id="StudentTeacher" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!--複雜的屬性,我們需要單獨處理 物件:association 集合:collection
javaType=""指定屬性的型別!
集合中的泛型資訊,我們使用ofType獲取
-->
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>

小結

  1. 關聯 - association 【多對一】
  2. 集合 - collection 【一對多】
  3. javaType & ofType
    1. JavaType用來指定實體類中的型別
    2. ofType用來指定對映到List或者集合中的pojo型別,泛型中的約束型別

動態SQL

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

所謂的動態SQL,本質上還是SQL語句,只是我們可以在SQL層面,去執行一個邏輯程式碼

例如IF

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

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

建議:

  • 先在Mysql中寫出完整的SQL,再對應的去修改成我們的動態SQL實現通用即可

MyBatis快取

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

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

我們再次查詢的相同資料的時候,直接走快取,不走資料庫了

  • MyBatis包含一個非常強大的查詢快取特性,它可以非常方便的定製和配置快取,快取可以極大的提高查詢效率。
  • MyBatis系統中預設定義了兩級快取:一級快取和二級快取
  • 預設情況下,只有一級快取開啟(SqlSession級別的快取,也稱為本地快取)
  • 二級快取需要手動開啟和配置,他是基於namespace級別的快取。
  • 為了提高可擴充套件性,MyBatis定義了快取介面Cache。我們可以通過實現Cache介面來定義二級快取。

一級快取

  • 一級快取也叫本地快取:SqlSession

    • 與資料庫同一次會話期間查詢到的資料會放在本地快取中
    • 以後如果需要獲取相同的資料,直接從快取中拿,沒必要再去查詢資料庫
    @Test
public void test1() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user); System.out.println("====================================="); User user2 = mapper.getUserById(1);
System.out.println(user2 == user);
} //測試,開啟日誌後可以看到 僅僅連線了一次JDBC

快取失效的情況:

  1. 查詢不同的東西
  2. 增刪改操作,可能會改變原來的資料,所以必定會重新整理快取
  3. 查詢不同的Mapper.xml
  4. 手動清理快取(sqlSession.clearCache();)

二級快取

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

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

  • 工作機制

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

一級快取開啟(SqlSession級別的快取,也稱為本地快取)

  • 二級快取需要手動開啟和配置,他是基於namespace級別的快取。
  • 為了提高可擴充套件性,MyBatis定義了快取介面Cache。我們可以通過實現Cache介面來定義二級快取。

步驟:

<!--顯示的開啟全域性快取-->
<setting name="cacheEnabled" value="true"/> <!--在當前Mapper.xml中使用二級快取-->
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>

小結:

  • 只要開啟了二級快取,在同一個Mapper下就有效
  • 所有的資料都會放在一級快取中
  • 只有當前會話提交,或者關閉的時候,才會提交到二級快取中

快取原理

注意:

  • 只有查詢才有快取,根據資料是否需要快取(修改是否頻繁選擇是否開啟)useCache=“true”
    <select id="getUserById" resultType="user" useCache="true">
select * from user where id = #{id}
</select>

自定義快取-ehcache

Ehcache是一種廣泛使用的開源Java分散式快取。主要面向通用快取

<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency> //在mapper中指定使用我們的ehcache快取實現
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>