介紹
- 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&useUnicode=true&characterEncoding=UTF-8&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 {
...
}
其他配置
- typeHandlers(型別處理器)
- objectFactory(物件工廠)
- plugins 外掛
- mybatis-generator-core
- mybatis-plus
- 通用mapper
對映器 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)
測試環境搭建:
- 匯入lombok
- 新建實體類Teacher,Student
- 建立Mapper介面
- 建立Mapper.xml檔案
- 在核心配置檔案中繫結註冊我們的Mapper介面或者檔案 【方式很多,隨心選】
- 測試查詢是否能夠成功
@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>
小結
- 關聯 - association 【多對一】
- 集合 - collection 【一對多】
- javaType & ofType
- JavaType用來指定實體類中的型別
- 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
快取失效的情況:
- 查詢不同的東西
- 增刪改操作,可能會改變原來的資料,所以必定會重新整理快取
- 查詢不同的Mapper.xml
- 手動清理快取(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"/>