??????創(chuàng)作不易,各位看官點(diǎn)贊收藏.
MyBatis 學(xué)習(xí)筆記
簡(jiǎn)介:MyBatis 是一款優(yōu)秀的持久層框架,它支持自定義 SQL、存儲(chǔ)過程以及高級(jí)映射。MyBatis 免除了幾乎所有的 JDBC 代碼以及設(shè)置參數(shù)和獲取結(jié)果集的工作。MyBatis 可以通過簡(jiǎn)單的 XML 或注解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對(duì)象)為數(shù)據(jù)庫中的記錄。簡(jiǎn)單來說,mybatis就是用來操作數(shù)據(jù)庫的,可以使程序猿更加容易書寫 dao 層。
1、Mybatis Demo 程序
搭建環(huán)境:導(dǎo)入依賴、創(chuàng)建數(shù)據(jù)庫表
user(id,name,address)
。
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!-- 數(shù)據(jù)庫連接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!-- log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
編寫配置文件:mybatis-config.xml,XML 配置文件中包含了對(duì) MyBatis 系統(tǒng)的核心設(shè)置,包括獲取數(shù)據(jù)庫連接實(shí)例的數(shù)據(jù)源(DataSource)以及決定事務(wù)作用域和控制方式的事務(wù)管理器(TransactionManager)。
<?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">
<!--mybatis的主配置文件-->
<configuration>
<!-- 數(shù)據(jù)庫配置環(huán)境-->
<!-- default和下面的id名稱要一樣,默認(rèn)使用,可以定義多個(gè)環(huán)境-->
<environments default="mysql">
<!-- 配置mysql的環(huán)境-->
<environment id="mysql">
<!-- 配置事務(wù)類型為jdbc-->
<transactionManager type="JDBC"/>
<!-- 配置數(shù)據(jù)池連接源-->
<!-- type有三種類型
1、POOLED是使用數(shù)據(jù)庫連接池的類型
2、UNPOOLED是不適用數(shù)據(jù)庫連接池的類型
3、JNDI是JNDI類型的數(shù)據(jù)源 JndiDataSource
-->
<dataSource type="POOLED">
<!-- 配置數(shù)據(jù)庫的4個(gè)基本信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis?characterEncoding=utf-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 引入映射文件-->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
編寫 Mapper 接口:
public interface UserMapper {
// 查詢所有的用戶
List<User> findAllUser();
}
編寫 mapper.xml:
<?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會(huì)綁定一個(gè)mapper接口,可以理解為實(shí)現(xiàn)這個(gè)接口,必須寫全限定類名-->
<mapper namespace="com.jx.app.mybatis.mapper.UserMapper">
<!-- id是接口中的方法名稱,resultType是實(shí)體的全限定類名,標(biāo)簽體中寫sql語句-->
<select id="findAllUser" resultType="com.jx.app.mybatis.entity.User">
select * from user
</select>
</mapper>
編寫 Mybatis 工具類:
public class MybatisUtils {
// 成員變量
private static SqlSessionFactory sqlSessionFactory;
// 靜態(tài)代碼初始化
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 getSqlSession(){
return sqlSessionFactory.openSession();
}
}
測(cè)試查詢結(jié)果:
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.findAllUser();
System.out.println(users);
}
2、Mybatis 核心配置文件
environments:配置數(shù)據(jù)庫連接配置環(huán)境。
<!-- default和下面的id名稱要一樣,默認(rèn)使用,可以定義多個(gè)環(huán)境-->
<environments default="mysql">
<!-- 配置mysql的環(huán)境,id是唯一標(biāo)識(shí)不能重復(fù)-->
<environment id="mysql">
<!-- 配置事務(wù)管理方式:JDBC(原生事務(wù))、MANAGED()-->
<transactionManager type="JDBC"/>
<!-- 配置數(shù)據(jù)池連接源-->
<!-- type有三種類型
1、POOLED是使用數(shù)據(jù)庫連接池的類型
2、UNPOOLED是不適用數(shù)據(jù)庫連接池的類型
3、JNDI使用上下文的數(shù)據(jù)源
-->
<dataSource type="POOLED">
<!-- 配置數(shù)據(jù)庫的4個(gè)基本信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql:///mybatis?characterEncoding=utf-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
propertes:可以引入 properties 文件,然后通過某種方式去訪問文件中的值。
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///mybatis?characterEncoding=utf-8&serverTimezone=UTC
username=root
password=123456
<!-- 引入文件,然后可以在下面通過 ${key值} 獲取對(duì)應(yīng)的value-->
<properties resource="jdbc.properties"/>
settings:Mybatis 的全局配置,還有很多配置,可以去官網(wǎng)上查看。
<settings>
<!-- 字段名與屬性名駝峰命名映射:user_name == userName-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 指定 mybatis 中的日志輸出-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
typeAliases:取別名標(biāo)簽,給實(shí)體類取一個(gè)別名,在映射結(jié)果時(shí)就可以使用別名就不再使用全限定類名。
<typeAliases>
<!-- 在編寫 mapper 映射文件時(shí)就可以使用 User 來代替這個(gè)全限定類名,不設(shè)置 alias 別名就是類名-->
<typeAlias type="com.jx.app.mybatis.entity.User" alias="User"/>
<!-- 把指定包下的所有類都起別名,別名都是類名-->
<package name="com.jx.app.mybatis.entity"/>
</typeAliases>
mappers:引入 mapper 映射文件。
<!-- 引入映射文件-->
<mappers>
<!-- 需要引入 mapper 文件存放在 resources 目錄下-->
<mapper resource="mapper/UserMapper.xml"/>
<!-- 通過包名進(jìn)行引入:1、mapper 文件名需要與接口一樣,2、mapper 存放的目錄結(jié)構(gòu)也必須和接口保持一致-->
<package name="com.jx.app.mybatis.mapper"/>
</mappers>
3、Mybatis Mapper 傳參映射
? 在 mapper 映射文件獲取接口的參數(shù)有兩種方式,${}
和 #{}
。
-
${}
: 本質(zhì)是字符串拼接,將參數(shù)與 SQL 語句進(jìn)行簡(jiǎn)單拼接,這種方式可能存在 SQL 注入問題,使用這種方式需要注意字符串的單引號(hào)拼接。 -
#{}
:本質(zhì)是占位符方式來拼接 SQL,自動(dòng)給參數(shù)加上單引號(hào),這種方式防止 SQL 注入問題。
單個(gè)字面量參數(shù):
// 參數(shù) name 是單個(gè)字面量
User getUserByName(String name);
<select id="getUserByName" resultType="com.jx.app.mybatis.entity.User">
SELECT *
FROM user u
WHERE u.user_name = #{name}
<!-- name參數(shù)需要和接口參數(shù)名保持一致-->
</select>
多個(gè)字面量參數(shù):
// 存在多個(gè)參數(shù)
User getUser(String id, String name);
<!-- 如果存在多個(gè)參數(shù),那么 mybatis 會(huì)把參數(shù)存儲(chǔ)在一個(gè) map 中,key:[arg0,arg1...,param1,param2...]
可以通過對(duì)應(yīng)的鍵獲取參數(shù)值,但是不能通過參數(shù)名去獲取,這兩種 key 可以混用,順序就是指定的下標(biāo)
-->
<select id="getUser" resultType="com.jx.app.mybatis.entity.User">
SELECT *
FROM user u
<!-- WHERE u.id = #{arg0} AND u.user_name = #{arg1}-->
WHERE u.id = #{param1} AND u.user_name = #{param2}
</select>
map 傳參:
// 參數(shù)是一個(gè)map
User getUserByMap(Map<String,String> map);
<!-- 參數(shù)是一個(gè) map,可以直接根據(jù) key 獲取對(duì)應(yīng)的值-->
<select id="getUserByMap" resultType="com.jx.app.mybatis.entity.User">
SELECT *
FROM user u
WHERE u.id = #{id} AND u.user_name = #{username}
</select>
實(shí)體類傳參:
// 參數(shù)是一個(gè)是實(shí)體類
int insert(User user);
<!-- 參數(shù)是實(shí)體類,可以通過類的屬性名獲取對(duì)應(yīng)的屬性值-->
<insert id="insert">
INSERT INTO user values(null,#{userName},#{password},#{money})
</insert>
@param
注解:可以命名參數(shù),標(biāo)識(shí)參數(shù)后依然后把參數(shù)放入 map 中,可以通過命名參數(shù)的 key 來獲取參數(shù)值。
// @Param 將參數(shù)進(jìn)行取別名命名
User getUser(@Param("id") String id, @Param("name") String name);
<!-- 使用 @param 注解后可以直接使用對(duì)應(yīng)的命名 key 獲取對(duì)應(yīng)的值-->
<select id="getUser" resultType="com.jx.app.mybatis.entity.User">
SELECT *
FROM user u
WHERE u.id = #{id} AND u.user_name = #{name}
</select>
4、Mybatis 查詢結(jié)果
查詢結(jié)果為實(shí)體對(duì)象:
// 接口返回值為實(shí)體對(duì)象
User getUserByName(String name);
<!-- resultType:屬性需要指定返回值的全限定類名或者是別名-->
<select id="getUserByName" resultType="com.jx.app.mybatis.entity.User">
SELECT *
FROM user u
WHERE u.user_name = #{name}
</select>
注意:返回值是一個(gè)實(shí)體類對(duì)象時(shí),返回結(jié)果只能是一條數(shù)據(jù)或者 null,如果查詢出來是多條數(shù)據(jù)就會(huì)報(bào)錯(cuò)。
查詢結(jié)果為集合:
// 接口返回值為集合類型,并指定對(duì)應(yīng)泛型
List<User> getUserByName(@Param("name") String name);
<!-- resultType:需要指定集合中泛型對(duì)應(yīng)類的全限定類名或者別名-->
<select id="getUserByName" resultType="com.jx.app.mybatis.entity.User">
SELECT * FROM user u
</select>
注意:如果查詢出來一條或多條結(jié)果就會(huì)封裝到集合中,如果沒有結(jié)果就會(huì)返回空集合但是不會(huì)返回 null。
查詢結(jié)果為 map 集合:
// 接口返回值是map類型
Map<String,Object> getUserToMap();
<!-- 將查詢的接口以字段名為key,然后字段值為 value 的 map-->
<select id="getUserToMap" resultType="java.util.Map">
SELECT * FROM user u
</select>
注意:查詢結(jié)果為 map 只能查詢一條數(shù)據(jù),查詢出多條就會(huì)報(bào)錯(cuò),而且如果某個(gè)字段是 null,這個(gè)字段不會(huì)封裝到 map 中。
多條結(jié)果封裝成 map:
/**
* 如果查詢多條數(shù)據(jù)也想封裝成 map,@MapKey 可以指定查詢出來那個(gè)字段可以作為 key,
* 然后這一條數(shù)據(jù)又會(huì)封裝成一個(gè) map 作為 value 存儲(chǔ)進(jìn)去
*/
@MapKey("id")
Map<String, Map<String,Object>> getUserToMap();
獲取自增主鍵:在插入數(shù)據(jù)時(shí),可以獲取插入數(shù)據(jù)后對(duì)應(yīng)的自動(dòng)遞增的 id。
// 插入元素
int insert(User user);
<!--
useGeneratedKeys:設(shè)置是否能夠獲取到自增主鍵
keyProperty:獲取到的主鍵的值存儲(chǔ)到參數(shù)實(shí)體的哪個(gè)屬性
-->
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user values(null,#{userName},#{password},#{money})
</insert>
resultMap 字段關(guān)系映射:查詢出來的結(jié)果與實(shí)體類上的字段名稱不一致,我們就需要通過 resultMap 屬性去自定義映射關(guān)系。
方式一:字段起別名,可以在 SQL 語句中把字段名通過起別名方式與屬性名一一對(duì)應(yīng)。
方式二:使用 resultMap 映射,將查詢出來字段名與對(duì)象屬性名進(jìn)行一個(gè)自定義映射。
<!-- 設(shè)置返回值類型為 resultMap,并且創(chuàng)建一個(gè)映射-->
<select id="getUserList" resultMap="UserListMap">
SELECT * FROM user u
</select>
<!-- 關(guān)系映射,id屬性:需要和映射返回值相同,type屬性:就是映射的實(shí)體對(duì)象類型-->
<!-- id:數(shù)據(jù)庫主鍵字段,result:普通字段。
column屬性:查詢數(shù)據(jù)庫對(duì)應(yīng)字段名,property屬性:需要映射的屬性名稱。
-->
<resultMap id="UserListMap" type="com.jx.app.mybatis.entity.User">
<id column="id" property="id"/>
<result column="user_name" property="userName"/>
<result column="password" property="password"/>
<result column="money" property="money"/>
</resultMap>
注意:如果查詢出來字段名與屬性名對(duì)應(yīng)不上,不會(huì)報(bào)錯(cuò),只是他們對(duì)應(yīng)屬性值都是對(duì)象默認(rèn)值。
5、Mybatis 關(guān)系映射處理
兩個(gè)實(shí)體:Student 和 Teacher,一個(gè)學(xué)生有一個(gè)老師,一個(gè)老師有多個(gè)學(xué)生。
@Data
public class Student {
private String id;
private String studentName;
private Teacher teacher;
}
@Data
public class Teacher {
private String id;
private String teacherName;
private List<Student> students;
}
5.1、多對(duì)一關(guān)系映射處理
方式一:使用連接查詢查詢對(duì)學(xué)生對(duì)應(yīng)的老師信息,然后使用級(jí)聯(lián)屬性映射。
<!-- 使用連接查詢獲取學(xué)生信息以及老師信息-->
<select id="getStudentById" resultMap="StudentMap">
SELECT * FROM student s
LEFT JOIN teacher t ON s.t_id = t.id
WHERE s.id = #{id}
</select>
<!-- 使用級(jí)聯(lián)方式進(jìn)行屬性映射-->
<resultMap id="StudentMap" type="com.jx.app.mybatis.entity.Student">
<id column="id" property="id"/>
<result column="student_name" property="studentName"/>
<result column="t_id" property="teacher.id"/>
<result column="teacher_name" property="teacher.teacherName"/>
</resultMap>
方式二:使用連接查詢查詢對(duì)學(xué)生對(duì)應(yīng)的老師信息,然后通過 resultMap 進(jìn)行關(guān)系映射。
<!-- 使用連接查詢獲取學(xué)生信息以及老師信息-->
<select id="getStudentById" resultMap="StudentMap">
SELECT * FROM student s
LEFT JOIN teacher t ON s.t_id = t.id
WHERE s.id = #{id}
</select>
<!-- 使用 association 關(guān)系映射-->
<resultMap id="StudentMap" type="com.jx.app.mybatis.entity.Student">
<id column="id" property="id"/>
<result column="student_name" property="studentName"/>
<!-- property:需要映射的屬性名,javaType:這個(gè)屬性的 java 類型是什么-->
<association property="teacher" javaType="com.jx.app.mybatis.entity.Teacher">
<id column="t_id" property="id"/>
<result column="teacher_name" property="teacherName"/>
</association>
</resultMap>
方式三:通過子查詢?nèi)ゲ樵兝蠋熜畔?,然后通過 resultMap 進(jìn)行關(guān)系映射。
<!-- 先獲取學(xué)生信息-->
<select id="getStudentById" resultMap="StudentMap">
SELECT * from student WHERE id = #{id}
</select>
<resultMap id="StudentMap" type="com.jx.app.mybatis.entity.Student">
<id property="id" column="id"/>
<result property="studentName" column="student_name"/>
<!-- select:子查詢的唯一標(biāo)識(shí)(命名空間+id),column:給子查詢的查詢參數(shù)-->
<association property="teacher" select="com.jx.app.mybatis.mapper.UserMapper.getTeacher" column="t_id"/>
</resultMap>
<!-- 子查詢-->
<select id="getTeacher" resultType="com.jx.app.mybatis.entity.Teacher">
SELECT * from teacher where id = #{t_id}
</select>
注意:子查詢有一個(gè)好處就是可以設(shè)置關(guān)聯(lián)對(duì)象的延遲加載,只有在使用到延遲對(duì)象時(shí)才會(huì)去執(zhí)行對(duì)應(yīng)的子查詢 SQL 語句然后返回結(jié)果。
- 需要開啟 mybatis 的全局懶加載設(shè)置,
<setting name="lazyLoadingEnabled" value="true"/>
。 - association 有一個(gè) fetchType 屬性手動(dòng)控制懶加載,值有 lazy(懶加載)、eager(立即加載),如果不設(shè)置這個(gè)屬性默認(rèn)是懶加載。
5.2、一對(duì)多關(guān)系映射處理
方式一:通過連接查詢出老師對(duì)應(yīng)的所有學(xué)生信息,然后通過 resultMap 進(jìn)行關(guān)系映射。
<!-- 連接查詢-->
<select id="getTeacherById" resultMap="TeacherMap">
SELECT * FROM teacher t
LEFT JOIN student s ON t.id = s.t_id
WHERE t.id = #{id}
</select>
<resultMap id="TeacherMap" type="com.jx.app.mybatis.entity.Teacher">
<id property="id" column="id"/>
<result property="teacherName" column="teacher_name"/>
<!-- 查詢出來學(xué)生信息進(jìn)行集合映射,ofType:集合泛型中的屬性-->
<collection property="students" ofType="com.jx.app.mybatis.entity.Student">
<id property="id" column="s_id"/>
<result property="studentName" column="student_name"/>
</collection>
</resultMap>
方式二:分步查詢老師信息和學(xué)生信息,然后通過 resultMap 映射關(guān)聯(lián)。
<select id="getTeacherById" resultMap="TeacherMap">
SELECT * FROM teacher t
WHERE t.id = #{id}
</select>
<resultMap id="TeacherMap" type="com.jx.app.mybatis.entity.Teacher">
<id property="id" column="id"/>
<result property="teacherName" column="teacher_name"/>
<!-- 查詢出來學(xué)生信息進(jìn)行集合映射,ofType:集合泛型中的屬性-->
<collection property="students" ofType="com.jx.app.mybatis.entity.Student"
select="com.jx.app.mybatis.mapper.UserMapper.getStudent"
column="id">
</collection>
</resultMap>
<select id="getStudent" resultType="com.jx.app.mybatis.entity.Student">
select * from student where t_id = #{tid}
</select>
6、Mybatis 動(dòng)態(tài) SQL
? 根根據(jù)特定的條件動(dòng)態(tài)拼接 SQL 語句的功能,它就是解決在拼接 SQL 時(shí)字符串問題。
if 標(biāo)簽:會(huì)根據(jù)標(biāo)簽的 test 屬性對(duì)應(yīng)表達(dá)式返回 true 或者 false 決定是否拼接到 SQL 語句中。
// 使用 if 進(jìn)行多條件查詢
List<User> DynamicSQL(User user);
<select id="DynamicSQL" resultType="com.jx.app.mybatis.entity.User">
select * from user
where 1=1
<!-- 如果查詢條件有 username 就會(huì)在 SQL 中進(jìn)行拼接-->
<if test="userName != null and userName != ''">
and user_name = #{userName}
</if>
<if test="money != null and money != ''">
and money = #{money}
</if>
</select>
where 標(biāo)簽:動(dòng)態(tài)生成 where 條件語句,根據(jù)拼接 SQL 在前面添加或去掉 and、or 關(guān)鍵字,如果沒有條件就不會(huì)有 where 關(guān)鍵字。
<select id="DynamicSQL" resultType="com.jx.app.mybatis.entity.User">
select * from user
<!-- 會(huì)動(dòng)態(tài)生成 where 關(guān)鍵字,并且去除或添加開頭的 and、or 關(guān)鍵字-->
<where>
<!-- 如果查詢條件有 username 就會(huì)在 SQL 中進(jìn)行拼接-->
<if test="userName != null and userName != ''">
and user_name = #{userName}
</if>
<if test="money != null and money != ''">
or money = #{money}
</if>
</where>
</select>
trim 標(biāo)簽:如果拼接 SQL 字符串有內(nèi)容可以在內(nèi)容前后添加內(nèi)容,如果沒有內(nèi)容也沒有任何效果。
<select id="DynamicSQL" resultType="com.jx.app.mybatis.entity.User">
select * from user
<!--
prefix|suffix:在內(nèi)容的前面|后面添加指定內(nèi)容
prefixOverrides|suffixOverrides:在內(nèi)容的前面|后面去掉指定內(nèi)容
-->
<trim prefix="where" suffix="" prefixOverrides="and|or" suffixOverrides="">
<if test="userName != null and userName != ''">
and user_name = #{userName}
</if>
<if test="money != null and money != ''">
or money = #{money}
</if>
</trim>
</select>
chose…when…otherwise 標(biāo)簽:相當(dāng)于 if…eles if…else,指定條件選擇一個(gè)進(jìn)行執(zhí)行,其它內(nèi)容不執(zhí)行。
<select id="DynamicSQL" resultType="com.jx.app.mybatis.entity.User">
select * from user
<where>
<!-- 只選擇一個(gè)條件進(jìn)行執(zhí)行,如果 when 都不滿足則執(zhí)行 otherwise 中的內(nèi)容-->
<choose>
<when test="userName != null and userName != ''">
user_name = #{userName}
</when>
<when test="money != null and money != ''">
money = #{money}
</when>
<otherwise>
id = #{id}
</otherwise>
</choose>
</where>
</select>
注意:這個(gè)條件選擇執(zhí)行只會(huì)滿足一個(gè),要么是 when 中的一個(gè)要么就是 otherwise。
foreach 標(biāo)簽:如果參數(shù)是集合類型,可以通過標(biāo)簽遍歷集合中內(nèi)容,例如進(jìn)行批量刪除、增加等操作。
// 批量刪除
int deleteBatch(@Param("ids") List<String> ids);
<delete id="deleteBatch">
delete from user
<where>
id in
<!-- collection:操作的集合參數(shù),item:遍歷出來的每個(gè)元素,open:指定開始內(nèi)容,
close:指定結(jié)束內(nèi)容,index:遍歷到的下標(biāo),separator:指定每個(gè)元素之間的分隔符
-->
<foreach collection="ids" item="id" close=")" open="(" separator="," index="i">
#{id}
</foreach>
</where>
</delete>
sql 標(biāo)簽:對(duì)常用的 SQL 語句進(jìn)行一個(gè)抽取,如果在其它地方使用直接引用。
<!-- id:唯一標(biāo)識(shí) sql 標(biāo)簽,在引用時(shí)通過 id 引入-->
<sql id="CommonSQL">
id,user_name,password,money
</sql>
<!-- 通過 include 標(biāo)簽進(jìn)行引入動(dòng)態(tài)的 sql-->
<select id="selectAll" resultType="com.jx.app.mybatis.entity.User">
select <include refid="CommonSQL"/> from user
</select>
7、Mybatis 緩存
一級(jí)緩存:這個(gè)級(jí)別緩存是 SqlSession 級(jí)別的緩存,通過同一個(gè) SqlSession 對(duì)象去查詢數(shù)據(jù),那么在第二次查詢就會(huì)去緩存中獲取而不會(huì)去查詢數(shù)據(jù)庫,這種緩存是默認(rèn)開啟的。
@Test
public void test4(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper1 = sqlSession.getMapper(UserMapper.class);
List<User> allUser1 = userMapper1.findAllUser();
System.out.println(allUser1);
// 即使是不同 mapper 對(duì)象,但是是同一個(gè) sqlSession,那么第二次就會(huì)去緩存中取數(shù)據(jù)
UserMapper userMapper2 = sqlSession.getMapper(UserMapper.class);
List<User> allUser2 = userMapper2.findAllUser();
System.out.println(allUser2);
}
一級(jí)緩存失效:
- 使用不同的 SqlSession 進(jìn)行查詢數(shù)據(jù)。
- 同一個(gè) SqlSession 但是查詢條件不同。
- 同一個(gè) SqlSession 的兩次查詢之間執(zhí)行了其它的增、刪、改等操作。
- 同一個(gè) SqlSession 的兩次查詢之間手動(dòng)清空了緩存。
// 手動(dòng)清空緩存
sqlSession.clearCache();
二級(jí)緩存:二級(jí)緩存是 SqlSessionFactory 級(jí)別的緩存,通過同一個(gè) SqlSessionFactory 創(chuàng)建的 SqlSession 去查詢結(jié)果會(huì)被緩存,那么第二次去執(zhí)行相同查詢就會(huì)去緩存中查詢,二級(jí)緩存需要手動(dòng)開啟。
- 需要在 mybatis 核心配置文件中開啟緩存。
<settings>
<!-- 開啟全局緩存,默認(rèn)是開啟的-->
<setting name="cacheEnabled" value="true"/>
</settings>
- 在 mapper 映射文件中開啟緩存。
<!-- 開啟緩存-->
<cache />
@Test
public void test5(){
SqlSession sqlSession1 = MybatisUtils.getSqlSession();
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
List<User> users1 = mapper1.selectAll();
System.out.println(users1);
sqlSession1.commit();
// 同一個(gè) SqlSessionFactory 獲取的不同 SqlSession 查詢結(jié)果會(huì)被緩存
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
List<User> users2 = mapper2.selectAll();
System.out.println(users2);
}
注意事項(xiàng):
- 必須是同一個(gè) SqlSessionFactory 的 SqlSession 的二級(jí)緩存才會(huì)生效。
- 只有每一個(gè)將 SqlSession 查詢后提交或者關(guān)閉,數(shù)據(jù)才會(huì)被緩存。
- 查詢的結(jié)果對(duì)象必須實(shí)現(xiàn)序列化,不然使用緩存會(huì)報(bào)錯(cuò)。
- 兩次相同查詢之間,執(zhí)行了增、刪、改操作,一級(jí)緩存、二級(jí)緩存都會(huì)失效。
二級(jí)緩存相關(guān)設(shè)置:在 mapper 映射文件中的 cache 標(biāo)簽可以設(shè)置以下屬性。
<!-- 相關(guān)設(shè)置:
eviction(緩存回收策略)=LRU(最近最少使用,默認(rèn)值) | FIFO(先進(jìn)先出)
flushInterval:緩存刷新時(shí)間,單位秒,不設(shè)置就是不刷新就只有執(zhí)行增刪改語句才會(huì)刷新
size:緩存對(duì)象數(shù)目,防止內(nèi)存溢出
readOnly(是否只讀)=true(緩存返回是同一個(gè)對(duì)象,且這個(gè)對(duì)象不能修改,修改會(huì)報(bào)錯(cuò))
false(返回的緩存中的一個(gè)拷貝對(duì)象,這個(gè)對(duì)象可以修改,默認(rèn)值)
-->
<cache eviction="LRU" flushInterval="1000" size="10" readOnly="false"/>
Mybatis 緩存查詢順序:
- 先查詢二級(jí)緩存,二級(jí)緩存的范圍比一級(jí)緩存大一些。
- 如果二級(jí)緩存沒有命中就回去查詢一級(jí)緩存。
- 如果一級(jí)緩存也沒有命中,就會(huì)去查詢數(shù)據(jù)庫。
- SqlSession 關(guān)閉后,會(huì)將一級(jí)緩存的數(shù)據(jù)寫入二級(jí)緩存。
第三方緩存工具 EHCache:
- 導(dǎo)入依賴:
<!-- ehcache和mybatis整合包-->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>
- 編寫 ehcache 配置文件:
<?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分為內(nèi)存和磁盤兩級(jí),
此屬性定義磁盤的緩存位 置。
參數(shù)解釋如下:
user.home – 用戶主目錄
user.dir – 用戶當(dāng)前工作目錄
java.io.tmpdir – 默認(rèn)臨時(shí)文件路徑
-->
<diskStore path="E:\ehcache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="true"
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"/>
<!--name:緩存名稱。
maxElementsInMemory:緩存最大數(shù)目
maxElementsOnDisk:硬盤最大緩存?zhèn)€數(shù)。
eternal:對(duì)象是否永久有效,一但設(shè)置了,timeout將不起作用。
overflowToDisk:是否保存到磁盤,當(dāng)系統(tǒng)當(dāng)機(jī)時(shí)
timeToIdleSeconds:設(shè)置對(duì)象在失效前的允許閑置時(shí)間(單位:秒)。 僅當(dāng) eternal=false對(duì)象不是永久有效時(shí)使用,可選屬性,默認(rèn)值是0,也就是可閑置時(shí)間無窮大。
timeToLiveSeconds:設(shè)置對(duì)象在失效前允許存活時(shí)間(單位:秒)。最大時(shí)間介于創(chuàng)建 時(shí)間和失效時(shí)間之間。僅當(dāng)eternal=false對(duì)象不是永久有效時(shí)使用,默認(rèn)是0.,也就是對(duì)象存 活時(shí)間無窮大。
diskPersistent:是否緩存虛擬機(jī)重啟期數(shù)據(jù) Whether the disk store persists between restarts of the Virtual Machine. The default value is false.diskSpoolBufferSizeMB:這個(gè)參數(shù)設(shè)置DiskStore(磁盤緩存)的緩存區(qū)大小。默 認(rèn)是30MB。每個(gè)Cache都應(yīng)該有自己的一個(gè)緩沖區(qū)。
diskExpiryThreadIntervalSeconds:磁盤失效線程運(yùn)行時(shí)間間隔,默認(rèn)是120秒。
memoryStoreEvictionPolicy:當(dāng)達(dá)到maxElementsInMemory限制時(shí),Ehcache將 會(huì)根據(jù)指定的策略去清理內(nèi)存。
默認(rèn)策略是LRU(最近最少使用)。你可以設(shè)置為FIFO(先進(jìn)先 出)或是LFU(較少使用)。
clearOnFlush:內(nèi)存數(shù)量最大時(shí)是否清除。 memoryStoreEvictionPolicy:
可選策略有:
LRU(最近最少使用,默認(rèn)策略)、
FIFO(先進(jìn)先出)、
LFU(最少訪問次數(shù))。
FIFO,first in first out,這個(gè)是大家最熟的,先進(jìn)先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一點(diǎn)就是講一直以 來最少被使用的。如上面所講,緩存的元素有一個(gè)hit屬性,hit值最小的將會(huì)被清出緩存。 L'
RU,Least Recently Used,最近最少使用的,緩存的元素有一個(gè)時(shí)間戳,當(dāng)緩存容 量滿了,而又需要騰出地方來緩存新的元素的時(shí)候,那么現(xiàn)有緩存元素中時(shí)間戳離當(dāng)前時(shí)間最遠(yuǎn)的 元素將被清出緩存。 -->
</ehcache>
- 使用三方緩存工具:在 mapper 映射文件中修改。
<!-- type:指定緩存類型,不指定就是使用 mybatis 的緩存-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
8、Mybatis 分頁插件
導(dǎo)入依賴:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.3</version>
</dependency>
設(shè)置 mybatis 插件:文章來源:http://www.zghlxwxcb.cn/news/detail-613762.html
<plugins>
<!-- 設(shè)置分頁插件攔截器-->
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
使用方式:文章來源地址http://www.zghlxwxcb.cn/news/detail-613762.html
@Test
public void test6(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 開啟分頁,在下一個(gè)查詢語句會(huì)進(jìn)行分頁攔截,參數(shù)一:頁碼,參數(shù)二:一頁數(shù)量
PageHelper.startPage(1,5);
List<User> users2 = mapper.selectAll();
// 參數(shù)是查詢后的 list 結(jié)果
PageInfo<User> pageInfo = new PageInfo<>(users2);
System.out.println(pageInfo.getList());
}
到了這里,關(guān)于Mybatis學(xué)習(xí)筆記,包含mybatis基本使用、關(guān)系映射、動(dòng)態(tài)SQL、分頁插件等等的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!