Mybatis学习笔记
1 Mybatis原理
一般都是一个数据库对应一个SqlSessionFactory对象。
什么是框架?
- 框架其实就是对通用代码的封装,提前写好了一堆接口和类型,
MyBatis的特点:
- MyBatis是一个支持定制化SQL,存储过程以及高级映射得到优秀的持久层框架。
- MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
- MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和ava的POJO(Plain Old」Java Objects,普通的ava对象)映射成数据库中的记录。
- MyBatis是一个半自动的ORM(Object Relation Mapping)框架。
1.1 关于Mybatis中的事务管理机制
在mybatis-config.xml中,可以通过以下的配置进行mybatis的事务管理。
<transactionManager type="JDBC"/>
2 SeqSession工具类封装
package utils;
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;
public class SqlSessionUtil {
// 一个sqlSessionFactory对应一个environment,一个environment通常一个数据库
private static SqlSessionFactory sqlSessionFactory;
static {
try {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private SqlSessionUtil() {}
/**
* 获取会话对象
* @return
* @throws IOException
*/
public static SqlSession openSession() throws IOException {
SqlSession sqlSession = sqlSessionFactory.openSession();
return sqlSession;
}
}
3 使用MyBatis完成CURD
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import pojo.Car;
import utils.SqlSessionUtil;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CarMapperTest {
@Test
void testInsertCar() throws IOException {
SqlSession sqlSession = SqlSessionUtil.openSession();
Map<String, Object> map = new HashMap<>();
map.put("k1", "103");
map.put("k2", "奔驰E300L");
map.put("k3", 50.3);
map.put("k4", "2020-10-01");
map.put("k5", "燃油车");
int count = sqlSession.insert("insertCar", map);
System.out.println(count);
sqlSession.commit();
sqlSession.close();
}
@Test
void testSelectCarByPojo() throws IOException {
SqlSession sqlSession = SqlSessionUtil.openSession();
Car car = new Car(null, "555", "奥迪A8", 78.9, "2024-10-01", "燃油车");
int count = sqlSession.insert("insertCar", car);
System.out.println(count);
sqlSession.commit();
sqlSession.close();
}
@Test
void testDeleteById() throws IOException {
SqlSession sqlSession = SqlSessionUtil.openSession();
int count = sqlSession.delete("deleteById", 113);
sqlSession.commit();
sqlSession.close();
System.out.println(count);
}
@Test
public void testUpdateCarByPOJO() throws IOException {
// 准备数据
Car car = new Car();
car.setId(106);
car.setCarNum("102");
car.setBrand("比亚迪汉");
car.setGuidePrice(30.23);
car.setProduceTime("2018-09-10");
car.setCarType("电车");
// 获取SqlSession对象
SqlSession sqlSession = SqlSessionUtil.openSession();
// 执行SQL语句
int count = sqlSession.update("updateById", car);
sqlSession.commit();
sqlSession.close();
System.out.println("更新了几条记录:" + count);
}
@Test
public void testSelectCarById() throws IOException {
// 获取SqlSession对象
SqlSession sqlSession = SqlSessionUtil.openSession();
// 执行SQL语句
Object car = sqlSession.selectOne("selectCarById", 1);
System.out.println(car);
}
@Test
public void testSelectCarAll() throws IOException {
// 获取SqlSession对象
SqlSession sqlSession = SqlSessionUtil.openSession();
// 执行SQL语句
List<Object> cars = sqlSession.selectList("selectCarAll");
// 输出结果
cars.forEach(car -> System.out.println(car));
}
}
<?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">
<mapper namespace="pojo.Car">
<insert id="insertCar">
INSERT INTO t_car (car_num, brand, guide_price, produce_time, car_type) VALUES (#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType});
</insert>
<delete id="deleteById">
delete from t_car where id=#{id}
</delete>
<update id="updateById">
update t_car set
car_num = #{carNum}, brand = #{brand},
guide_price = #{guidePrice}, produce_time = #{produceTime},
car_type = #{carType}
where id = #{id}
</update>
<select id="selectCarById" resultType="pojo.Car">
select
id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType
from
t_car
where
id = #{id}
</select>
<!--虽然结果是List集合,但是resultType属性需要指定的是List集合中元素的类型。-->
<select id="selectCarAll" resultType="pojo.Car">
<!--记得使用as起别名,让查询结果的字段名和java类的属性名对应上。-->
select
id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType
from
t_car
</select>
</mapper>
4 MyBatis的核心配置文件
<?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>
<environments default="development">
<!--一个sqlSessionFactory对应一个environment,一个environment通常对应一个数据库-->
<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/powernode"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="CarMapper.xml"/>
<mapper resource="CarMapper2.xml"/>
</mappers>
</configuration>
- configuration:根标签,表示配置信息。
- environments:环境(多个),以“s”结尾表示复数,也就是说mybatis的环境可以配置多个数据源。
- default属性:表示默认使用的是哪个环境,default后面填写的是environment的id。default的值只需要和environment的id值一致即可。
- environment:具体的环境配置(主要包括:事务管理器的配置 + 数据源的配置)
- id: 给当前环境一个唯一标识,该标识用在environments的default后面,用来指定默认环境的选择。
- transactionManager:配置事务管理器。
- type属性:指定事务管理器具体使用什么方式,可选值包括两个
- JDBC:使用JDBC原生的事务管理机制。底层原理:事务开启conn.setAutoCommit(false); ...处理业务...事务提交conn.commit();
- MANAGED:交给其它容器来管理事务,比如WebLogic、JBOSS等。如果没有管理事务的容器,则没有事务。没有事务的含义:只要执行一条DML语句,则提交一次。
- type属性:指定事务管理器具体使用什么方式,可选值包括两个
- dataSource:指定数据源
- type属性:用来指定具体使用的数据库连接池的策略,可选值包括三个
- UNPOOLED:采用传统的获取连接的方式,虽然也实现Javax.sql.DataSource接口,但是并没有使用池的思想。
- POOLED:采用传统的javax.sql.DataSource规范中的连接池,mybatis中有针对规范的实现。
- JNDI:采用服务器提供的JNDI技术实现,来获取DataSource对象,不同的服务器所能拿到DataSource是不一样。如果不是web或者maven的war工程,JNDI是不能使用的。
- type属性:用来指定具体使用的数据库连接池的策略,可选值包括三个
- mappers:在mappers标签中可以配置多个sql映射文件的路径。
- mapper:配置某个sql映射文件的路径。
- resource属性:使用相对于类路径的资源引用方式。
- url属性:使用完全限定资源定位符(URL)方式。
1 MyBatis小技巧
1.1 #{}与${}的区别
注意:查询语句不需要commit,其他三种语句则需要commit
// 使用#{}输出的信息
2024-09-19 21:23:23.560 [main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - ==> Preparing: select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where car_type = ?
2024-09-19 21:23:23.650 [main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - ==> Parameters: 新能源(String)
2024-09-19 21:23:23.849 [main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - <== Total: 3
// 使用${}报的错误
2024-09-19 21:21:55.038 [main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - ==> Preparing: select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where car_type = 新能源
2024-09-19 21:21:55.116 [main] DEBUG c.p.mybatis.mapper.CarMapper.selectByCarType - ==> Parameters:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: Unknown column '新能源' in 'where clause'
### The error may exist in CarMapper.xml
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where car_type = 新能源
### Cause: java.sql.SQLSyntaxErrorException: Unknown column '新能源' in 'where clause'
#{}底层使用PreparedStatement:先进行SQL语句的编译,然后给SQL语句的占位符?进行赋值,没有SQL注入的风险。
${}底层使用Statement,SQL无预编译,直接拼接参数,有SQL注入的攻击风险。
- 所有参数位置,都应该使用
#{}。 - 需要动态表名等,才使用
${}。例如要动态输入表名。
优先使用
#{},避免SQL注入的风险
1.2 什么时候使用${}
Preparing: select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car order by produce_time ?
Parameters: ASC(String)
如果需要将SQL语句的关键字放到SQL语句中,只能使用
${}
1.3 拼接表名
向SQL语句中拼接表明,就需要使用${}
1.4 批量删除
delete from t_car where id in(${ids})
1.5 模糊查询
1.6 别名机制
<typeAliases>
<!--别名自己指定的 -->
<!--<typeAlias type="com.powernode.mybatis.pojo.Car" alias="Car"/>
<typeAlias type="com.powernode.mybatis.pojo.Log" alias="log"/>-->
<!--采用默认的别名机制-->
<!--<typeAlias type="com.powernode.mybatis.pojo.Car"/>
<typeAlias type="com.powernode.mybatis.pojo.Log"/>-->
<!--将指定包下的所有类全部自动起别名,适合于包下有很多类的情况-->
<package name="com.powernode.mybatis.pojo"/>
</typeAliases>
<select id="selectByCarType" resultType="Car">
select id,
car_num as carNum,
brand,
guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
from
t_car
where
car_type = #{carType}
</select>
所有别名不区分大小写,namespace不能使用别名机制
1.7 mybaits-config.xml文件中的mappers标签
mapper标签的属性有三个
resource:从类的根路径下开始查找资源,采用这种方式的话,配置文件需要放到类路径当中才行。url:绝对路径的方式。这种方式下配置文件可以放到任意路径下,只要提供一个绝对路径即可。(这种方式使用极少,因为移植性太差。)class:写mapper接口的全限定接口名(带有包名)。- 如果你class指定是:com.powernode.mybatis,mapper.CarMapper,那么mybatis框架会自动去com/powernode/mybatis/mapper目录下查找CarMapper.xml文件。也就是说:如果你采用这种方式,那么你必须保证CarMapper.xmL文件和CarMapper接口必须在同一个目录下,并且名字一致。
<mappers>
<!--<mapper resource="CarMapper.xml"/>
<mapper resource="LogMapper.xml"/>-->
<!--<mapper class="com.powernode.mybatis.mapper.CarMapper"/>
<mapper class="com.powernode.mybatis.mapper.LogMapper"/>-->
<!--这种方式在实际开发中最常用-->
<package name="com.powernode.mybatis.mapper"/>
</mappers>
1.8 IDEA配置文件模板
File -> Setting -> Editor -> File and Code Templates -> +
1.9 插入数据时自动获取生成的主键
<!--
useGeneratedKeys="true" 表示使用自动生成的主键值
keyProperty="id" 表示主键值赋值给对象的哪个属性
-->
<insert id="insertCarUserGenerateKeys" useGeneratedKeys="true" keyProperty="id">
insert into t_car(id, car_num, brand, guide_price, produce_time, car_type)
values (null, #{carNum}, #{brand}, #{guidePrice}, #{produceTime}, #{carType})
</insert>
2 MyBatis参数处理
| 单个参数-普通类型 | getEmploy(Long id) | #{变量名} |
|---|---|---|
| 单个参数- List类型 | getEmploy(List | #{变量名[0]} |
| 单个参数-对象类型 | addEmploy(Employ e) | #{对象中属性名} |
| 单个参数- Map类型 | addEmploy(Map<String,Object> m) | #{map中属性名} |
| 多个参数-无@Param | getEmploy(Long id,String name) | #{变量名}//新版兼容 |
| 多个参数-有@Param | getEmploy(@Param(“id”)Long id, @Param(“name”)String name) | #{param指定的名} |
| 扩展: | getEmploy(@Param(“id”)Long id, @Param(“ext”)Map<String,Object> m, @Param(“ids”)List @Param(“emp”)Employ e) | #{id}、 #{ext.name}、#{ext.age}, #{ids[0]}、#{ids[1]}, #{e.email}、#{e.age} |
2.1 单个简单类型参数
2.2 Map参数
2.3 POJO类(实体类参数)
2.4 多参数
<select id="selectByNameAndGender" resultType="Student">
<!-- select * from t_student where name = #{arg0} and gender = #{arg1} -->
select * from t_student where name = #{param1} and gender = #{param2}
</select>
2.5 @Param注解(命名参数)
List<Student> selectByNameAndGender2(@Param("name") String name, @Param("gender") Character gender);
3 查询语句专题
3.1 返回Car
3.2 返回List<Car>
3.3 返回Map
3.4 返回List<Map>
3.5 返回Map(String, Map)
3.6 resultMap结果映射
查询结果的列名和java对象的属性名对应不上怎么办?
-
使用
as给列起别名select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where id = #{id} -
使用resultMap进行结果映射
<resultMap id="carResultMap" type="car"> <!--如果有主键,建议这里配置一个id标签,注意:这不是必须的。 但是官方的解释是什么呢?这样的配置可以让mybatis提高效率。--> <id property="id" column="id"/> <result property="carNum" column="car_num" javaType="string" jdbcType="VARCHAR"/> <!--如果column和property是一样的,这个可以省略。--> <!--<!<result property="brand" column="brand"/> --> <result property="guidePrice" column="guide_price"/> <result property="produceTime" column="produce_time"/> <result property="carType" column="car_type"/> </resultMap> -
是否开启驼峰命名自动映射(配置setttings)
注意:使用自动映射的前提是Java和MySQL的命名要符合规范
在MyBatis的核心配置文件mybatis-config.xml编写如下的代码
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
3.7 返回总记录条数
<select id="selectTotal" resultType="long">
select count(*) from t_car
</select>
4 动态SQL
4.1 if 标签
<?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">
<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
<!--
1. if标签中test属性是必须的。
2. if标签中test属性的值是false或者trUe。
3. 如果test是true,则if标签中的sgL语句就会拼接。反之,则不会拼接。
4. test属性中可以使用的是:
当使用了@Param注解,那么test中要出现的是@Parami注解指定的参数名。@Param("brand"),那么这里只能使用brand
当没有使用@Param注解,那么test中要出现的是:param1 param2 param3 arg0 arg1 arg2...
当使用了P0J0,那么test中出现的是P0J0类的属性名。
5. 在mybatis的动态SQL当中,不能使用&&,只能使用and
-->
<select id="selectByMultiCondition" resultType="car">
select * from t_car where 1=1
<if test="brand != null and brand != ''">
and brand like "%"#{brand}"%"
</if>
<if test="guidePrice != null and guidePrice != ''">
and guide_price > #{guidePrice}
</if>
<if test="carType != null and carType != ''">
and car_type = #{carType}
</if>
</select>
</mapper>
4.2 where 标签
where标签的作用:让where子句更加动态智能。
- 所有条件都为空时,wheret标签保证不会生成where子句。
- 自动去除某些条件前面多余的and或or。
<select id="selectByMultiConditionWithWhere" resultType="car">
select * from t_car
<!--where标签是专门负责where子句动态生成的 -->
<where>
<if test="brand != null and brand != ''">
and brand like "%"#{brand}"%"
</if>
<if test="guidePrice != null and guidePrice != ''">
and guide_price > #{guidePrice}
</if>
<if test="carType != null and carType != ''">
and car_type = #{carType}
</if>
</where>
</select>
4.3 trim 标签
trim标签的属性:
- prefix:在trim标签中的语句前添加内容
- suffix:在trim标签中的语句后添加内容
- prefixOverrides:前缀覆盖掉(去掉)
- suffixOverrides:后缀覆盖掉(去掉)
<!--
prefix:加前缀
suffix:加后缀
prefixOverrides:删除前缀
suffixOverrides:删除后缀
prefix="where" 是在trim标签所有内容的前面添加where
suffixOverrides="and|or" 把trim标签中内容的后缀and或or去掉
-->
<select id="selectByMultiConditionWithTrim" resultType="car">
select * from t_car
<trim prefix="where" suffixOverrides="and|or">
<if test="brand != null and brand != ''">
brand like "%"#{brand}"%" or
</if>
<if test="guidePrice != null and guidePrice != ''">
guide_price > #{guidePrice} and
</if>
<if test="carType != null and carType != ''">
and car_type = #{carType}
</if>
</trim>
</select>
4.4 set 标签
主要使用在updatei语句当中,用来生成set关键字,同时去掉最后多余的,
比如我们只更新提交的不为空的字段,如果提交的数据是空或者"”,那么这个字段我们将不更新。
<update id="updateBySet">
update t_car
<set>
<if test="carNum != null and carNum != ''">car_num = #{carNum},</if>
<if test="brand != null and brand != ''">brand = #{brand},</if>
<if test="guidePrice != null and guidePrice != ''">guide_price = #{guidePrice},</if>
<if test="produceTime != null and produceTime != ''">produce_time = #{produceTime},</if>
<if test="carType != null and carType != ''">car_type = #{carType},</if>
</set>
where id = #{id}
</update>
4.5 choose when otherwise 标签
这个三个标签一般同时使用,类似于Java中的if..else if...else语句
<select id="selectByChoose" resultType="car">
select * from t_car
<where>
<choose>
<when test="brand != null and brand != ''">
brand like "%"#{brand}"%"
</when>
<when test="guidePrice != null and guidePrice != ''">
guide_price > #{guidePrice}
</when>
<otherwise>
car_type = #{carType}
</otherwise>
</choose>
</where>
</select>
4.6 foreach标签
用于循环数组或集合,动态生成sql,
4.6.1 批量删除
第一种写法
<!--
foreach标签的属性:
collection:指定数组或者集合
item:代表数组或集合中的元素
separator:循环之间的分隔符
open:foreach循环拼接的所有sqL语句的最前面以什么开始
close:foreach循环拼接的所有sqL语句的最后面以什么结束
-->
<delete id="deleteByIds">
<!--
第一种写法
delete from t_car where id in (
<foreach collection="ids" item="id" separator=",">
#{id}
</foreach>
)
-->
delete from t_car where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
第二种写法
<delete id="deleteByIds2">
delete from t_car where
<foreach collection="ids" item="id" separator="or">
id=#{id}
</foreach>
</delete>
4.6.2 批量添加
<insert id="insertBatch">
insert into t_car values
<foreach collection="cars" item="car" separator=",">
(null, #{car.carNum}, #{car.brand}, #{car.guidePrice}, #{car.produceTime}, #{car.carType})
</foreach>
</insert>
4.7 sql标签与include标签
sql标签用来声明sql片段 include标签用来将声明的sql片段包含到某个sql语句当中 作用:代码复用。易维护。
<!--声明一个sql片段-->
<sql id="carColumnNameSql">
id,
car_num as carNum,
brand,
guide_price as guidePrice,
produce_time as produceTime,
car_type as carType
</sql>
<select id="selectById" resultType="car">
select
<!--将声明的sql片段包含进来-->
<include refid="carColumnNameSql"/>
from t_car where id = #{id}
</select>
5 MyBatis的高级映射及延迟加载
5.1 多对一
多种方式,常见的包括三种:
- 一条SQL语句,级联属性映射。
- 一条SQL语句,association。
- 两条SQL语句,分步查询。(这种方式常见,优点一是可复用,优点二是支持懒加载)
5.1.1 第一种方式:级联属性映射
<resultMap id="studentResultMap" type="Student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<result property="clazz.cid" column="cid"/>
<result property="clazz.cname" column="cname"/>
</resultMap>
<select id="selectById" resultMap="studentResultMap">
select s.sid,
s.sname,
c.cid,
c.cname
from t_stu s
left join t_clazz c on s.cid = c.cid
where s.sid = #{sid}
</select>
5.1.2 第二种方式:级联属性映射
<resultMap id="studentResultMapAssociation" type="student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
<!--
association:一个Student对象关联一个Clazz对象
property:提供要映射的POJO属性名
javaType:指定要映射的java类型
-->
<association property="clazz" javaType="clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
</association>
</resultMap>
<select id="selectByIdAssociation" resultMap="studentResultMapAssociation">
select s.sid,
s.sname,
c.cid,
c.cname
from t_stu s
left join t_clazz c on s.cid = c.cid
where s.sid = #{sid}
</select>
5.1.3 第三种方式:分步查询
5.2 一对多
有两种实现方式
- collection(了解一些即可)
- 分布查询
5.2.1 第一种方式 collection
<resultMap id="clazzResultMap" type="Clazz">
<id property="cid" column="cid"/>
<result property="cname" column="cname"/>
<collection property="students" ofType="student">
<id property="sid" column="sid"/>
<result property="sname" column="sname"/>
</collection>
</resultMap>
<select id="selectByCollection" resultMap="clazzResultMap">
select c.cid, c.cname, s.sid, s.sname
from t_clazz c
left join t_stu s on c.cid = s.cid
where c.cid = #{cid}
</select>
5.2.2 第二种方式:分布查询
6 MyBatis的缓存
缓存通常是我我们程序开发中优化程序的重要手段。
缓存的作用:通过减少IO的方式,来提高程序的执行效率。
mybatis的缓存:将selecti语句的查询结果放到缓存(内存)当中,下一次还是这条selecti语句的话,直接从缓存中取,不再查数据库。一方面是减少了IO。另一方面不再执行繁琐的查找算法。效率大大提升。
mybatis缓存包括:
- 一级缓存:将查询到的数据存储到SqlSession中。
- 二级缓存:将查询到的数据存储到SqlSessionFactory中。
- 或者集成其它第三方的缓存:比如EhCache【Uava语言开发的】、Memcache【C语言开发的】等。
缓存只针对于
DQL语句,也就是说缓存机制只对应select语句。
6.1 一级缓存
一级缓存默认是开启的,不需要做任何配置。
原理:只要便用同一个SqlSession对象执行同一条SQL语句,就会走缓存。
以下两种情况一级缓存会失效:
- 执行了sqlSession的clearCache()方法,这是手动清空缓存,
- 执行了INSERT或DELETE或UPDATE语句。不管你是操作哪张表的,都会清空一级缓存
6.2 二级缓存
二级缓存的范围是SqlSessionFactory。
使用二级缓存需要具备以下几个条件:
<setting name="cacheEnabled" value=true>全局性地开启或关闭所有映射器配置文件中已配置的任何缓存,默认就是true,无需设置。- 在需要便用二级缓存的SqlMapper.xml文件中添加配置(标签):
<cache/> - 便用二级缓存的实体类对象必须是可序列化的,也就是必须实现
java.io.Serializable接口。 - SqlSession对像关闭或提交之后,一级缓存中的数据才会被写入到二级缓存当中,此时二级缓存才可用。
二级缓存的失效:只要两次查询之间出现了增删改操作。二级缓存就会失效。【一级缓存也会失效】
6.3 MyBatis缓存查询的顺序
- 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
- 如果二级缓存没有命中,再查询一级缓存。
- 如果一级缓存也没有命中,则查询数据库。
- SqlSession关闭之后,一级缓存中的数据会写入二级缓存。
6.4 MyBatis集成EhCache(了解)
注意:只能使用其他的第三方技术来代替二级缓存,但是一级缓存是无法代替的。
- 第一步:引入jar包
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.3.0</version>
</dependency>
- 第二步:创建EHCache的配置文件ehcache.xml
<?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: 持久化到磁盘上时的存储位置-->
<diskStore path="D:\\aspring\\ehcache"/>
<!--
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<cache
name="cacheSpace"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>
- 第三步:设置二级缓存的类型(不设置的话,默认使用MyBatis内置的缓存):在SQL映射文件(例如:CarMapper.xml)中添加如下代码。
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
- 第四步:正常使用。
7 MyBati的逆向工程
所谓的逆向工程是:根据数据库表逆向生成java的pojo类,SqlMapper.xml文件,以及Mapper接口类等。
- 第一步:引入jar包和对应的插件(添加依赖)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.powernode</groupId>
<artifactId>mybatis-010-generator1</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.16</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>9.0.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.11.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.6</version>
</dependency>
</dependencies>
<!--配置mybatis逆向工程的插件-->
<!--定制构建过程-->
<build>
<!--可配置多个插件-->
<plugins>
<!--其中的一个插件:mybatis逆向工程插件-->
<plugin>
<!--插件的GAV坐标-->
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.1</version>
<!--允许覆盖-->
<configuration>
<overwrite>true</overwrite>
</configuration>
<!--插件的依赖-->
<dependencies>
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.2</version>
</dependency>
<!--mysql驱动依赖-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>9.0.0</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
- 第二步:创建MyBatis的核心配置文件
- 第三步:创建逆向工程的配置文件,配置文件名是固定的:
generatorConfig.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!--
targetRuntime有两个值:
MyBatis3Simple:生成的是基础版,只有基本的增删改查。
MyBatis3:生成的是增强版,除了基本的增删改查之外还有复杂的增删改查。一般使用增强版
-->
<context id="DB2Tables" targetRuntime="MyBatis3Simple">
<!--防止生成重复代码-->
<plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>
<commentGenerator>
<!--是否去掉生成日期-->
<property name="suppressDate" value="true"/>
<!--是否去除注释-->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--连接数据库信息-->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis"
userId="root"
password="mysqlmm0424">
</jdbcConnection>
<!-- javaBean的生成策略:生成pojo包名和位置 -->
<javaModelGenerator targetPackage="com.powernode.mybatis.pojo" targetProject="src/main/java">
<!--是否开启子包-->
<property name="enableSubPackages" value="true"/>
<!--是否去除字段名的前后空白-->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- 生成SQL映射文件的包名和位置 -->
<sqlMapGenerator targetPackage="com.powernode.mybatis.mapper" targetProject="src/main/resources">
<!--是否开启子包-->
<property name="enableSubPackages" value="true"/>
</sqlMapGenerator>
<!-- 生成Mapper接口的包名和位置 -->
<javaClientGenerator
type="xmlMapper"
targetPackage="com.powernode.mybatis.mapper"
targetProject="src/main/java">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- 表名和对应的实体类名
tableName设置为*号:表示对应所有表,此时不写domainObjectName
-->
<table tableName="t_car" domainObjectName="Car"/>
<table tableName="t_stu" domainObjectName="Student"/>
</context>
</generatorConfiguration>
- 第四步:使用插件直接生成相应的文件。

8 MyBatis分页插件
- 第一步:添加依赖。
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>6.1.0</version>
</dependency>
- 第二步:在MyBatis的核心配置文件中配置分页插件。
<!--MyBatis的分页拦截器-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
- 第三步:开始编写代码来使用分页插件。
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
import com.powernode.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import java.util.List;
public class CarMapperTest {
@Test
public void testSelectAll() {
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
// 在执行DQL语句之间,开启分页功能
int pageNum = 2;
int pageSize = 3;
PageHelper.startPage(pageNum, pageSize);
List<Car> cars = mapper.selectAll();
// cars.forEach(System.out::println);
// 封装分页信息对象
// navigatePages一般为奇数
PageInfo<Car> carPageInfo = new PageInfo<>(cars, 3);
System.out.println(carPageInfo);
sqlSession.close();
}
@Test
public void testSelectByPage() {
// 每页显示的条数
int pageSize = 3;
// 显示第几页:页码
int pageNum = 3;
int startIndex = (pageNum -1) * pageSize;
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = mapper.selectByPage(startIndex, pageSize);
sqlSession.close();
cars.forEach(System.out::println);
}
}