Mybatis-初見
介紹
- 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"
"//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"
"//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"/>