1、SpringBoot+老杜MyBatis
一、简单回顾一下MyBatis
二、快速入门
三、简易插入删除更改
四、查询
①、按其中一个字段查询
②、按所有字段进行查询
五、详解MyBatis核心配置(复习)
六、结合Web及SpringMVC
2、MyBatis小技巧
一、#{}与${}及模糊查询
二、别名机制与mapper标签
三、插入使用生成的主键值
3、MyBatis参数处理
一、单个简单类型参数
二、Map参数
①、插入信息
②、查询单个汽车信息
③、返回多个Map
④、返回Map,map>
三、实体类参数
四、多参数(@Param)
五、resultMap结果映射
①、使用resultMap进行结果映射(常用)
②、开启驼峰命名规范自动映射
六、获取总记录条数
4、动态SQL(注:使用了驼峰命名规范)
一、if标签
二、where标签
三、trim标签
四、set标签
五、choose where otherwise
六、foreach标签
①批量删除
②批量添加
七、sql、include标签
5、高级映射及延迟加载
一、多对一
二、多对一延迟加载
三、一对多
四、一对多延迟加载
6、MyBatis缓存机制
一、一级缓存
二、二级缓存
7、MyBatis使用PageHelper
一、limit分页
二、PageHelper插件
舞台再大 你不上台 永远是个观众
平台再好 你不参与 永远是局外人
能力再大 你不行动 只能看别人成功
没有人会在乎你付出多少努力 撑得累不累 摔得痛不痛
他们只会看你最后站在什么位置 然后羡慕或鄙夷
1、SpringBoot+老杜MyBatis
一、简单回顾一下MyBatis
-
核心对象包括以下三个:
-
SqlSessionFactoryBulider
-
SqlSessionFactory
-
SqlSession
-
SqlSessionFactoryBuilder –> SqlSessionFactory –> SqlSession
-
-
关于MyBatis的事务管理机制(两种)
-
JDBC表示事务管理器
-
MANAGED表示事务事务管理器
-
JDBC事务管理器: MyBatis框架自己管理事务,自己采用原生的JDBC代码去管理事务: conn.setAutoCommit(false); 开启事务。 ….业务处理… conn.commit(); 手动提交事务 使用JDBC事务管理器的话,底层创建的事务管理器对象:JdbcTransaction对象。
如果你编写的代码是下面的代码: SqlSession sqlSession = sqlSessionFactory.openSession(true); 表示没有开启事务。因为这种方式压根不会执行:conn.setAutoCommit(false); 在JDBC事务中,没有执行conn.setAutoCommit(false);那么autoCommit就是true。 如果autoCommit是true,就表示没有开启事务。只要执行任意一条DML语句就提交一次。
MANAGED事务管理器: MyBatis不再负责事务的管理了。事务管理交给其它容器来负责。例如:spring。 我不管事务了,你来负责吧。对于我们当前的单纯的只有mybatis的情况下,如果配置为:MANAGED 那么事务这块是没人管的。没有人管理事务表示事务压根没有开启。没有人管理事务就是没有事务。
-
JDBC中的事务: 如果你没有在JDBC代码中执行:conn.setAutoCommit(false);的话,默认的autoCommit是true。
-
重点: 以后注意了,只要你的autoCommit是true,就表示没有开启事务。 只有你的autoCommit是false的时候,就表示开启了事务。
在SpringBoot+MyBatis项目中就不用写事务相关的东西了,但是用到业务层Service就需要了
二、快速入门
第一步:引入依赖
junit
junit
test
org.projectlombok
lombok
第二步:编写yml配置文件(此处我将properties后缀改成了yml)
其中包含连接数据库以及MyBatis的核心配置信息(但在SpringBoot框架中无需用MyBatis原核心配置文件)
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/powernode
username: root
password: root
mybatis:
mapper-locations: classpath:mapper/*.xml
#目的是为了省略resultType里的代码量
type-aliases-package: com.chf.pojo
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
第三步:构建实体类(在pojo包下),与表中字段一一对应
第四步:创建接口,用来写方法
第五步:每一个实体类对应一个mapper映射文件,在resources的mapper包下写映射文件(SQL语句)
其实这里的Sql语句是有问题的,查询到控制台的有问题,这里做个伏笔后面会知道为什么。
第六步:先测试自己是否成功连接到了数据库,不然你不管怎么测试方法都不知道你为什么爆红
测试成功,开心开心(这里控制台输出的null是我埋下的伏笔,下面会讲)
三、简易插入删除更改
@Mapper
public interface CarMapper {
/**
* 插入汽车
* @return
* @param car
*/
int insert(Car car);
/**
* 按id删除车辆信息
* @param id
* @return
*/
int delete(Long id);
/**
* 更新车辆信息
* @param car
* @return
*/
int update(Car car);
}
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
delete from t_car where id = #{dasdad}
update t_car set
car_num=#{carNum},
brand=#{brand},
guide_price=#{guidePrice},
produce_time=#{produceTime},
car_type=#{carType}
where id=#{id}
@SpringBootTest
public class Mybatis001IntroduceApplicationTests {
@Autowired
private CarMapper carMapper;
@Test
void testInsert(){
Car car = new Car(null,"111","奔驰",30.00,"2022-10-2","新能源");
int count = carMapper.insert(car);
System.out.println((count == 1 ? "插入成功" : "插入失败"));
}
@Test
void testDelete(){
int count = carMapper.delete(4L);
System.out.println((count == 1 ? "删除成功" : "删除失败"));
}
@Test
void testUpdate(){
Car car = new Car(6L,"1111","奔驰",30.00,"2022-10-2","新能源");
int count = carMapper.update(car);
System.out.println((count == 1 ? "更新成功" : "更新失败"));
}
}
四、查询
①、按其中一个字段查询
通过控制台你会仔细的发现:除了id和brand其他皆为null。
原因就在于:属性名与表名不一致造成的,所以我们应该编写Sql语句就可以完成查询
②、按所有字段进行查询
这也是我在快速入门那里留下的伏笔,其实那个select也要进行修改
五、详解MyBatis核心配置(复习)
这里就当复习了,因为是看的老杜讲解的,更加细致。
@SpringBootTest
public class ConfigurationTest{
@Test
void testEnvironment() throws Exception{
//获取SqlSessionFactory对象(采用默认方式获取)
SqlSessionFactoryBuilder ssf = new SqlSessionFactoryBuilder();
//采用这种方式获取的就是默认的环境
SqlSessionFactory sqlSessionFactory = ssf.build(Resources.getResourceAsStream("MyBatisConfig.xml"));
//这种方式通过id获取的是指定的环境
SqlSessionFactory sqlSessionFactory = ssf.build(Resources.getResourceAsStream("MyBatisConfig.xml"),"mybatisDB");
}
}
六、结合Web及SpringMVC
这里老杜的是使用MVC架构模式,然后优化使用了动态代理写了两个工具类
但我是基于SpringBoot框架的基础上去复习老杜的MyBatis,所以会使用到SpringMVC去实现老杜的课程
一个项目从前往后写才知道具体需要实现的功能是什么(老杜教的)
修改成功和失败的页面就不截图展示了
项目目录如下以及超简易页面以及数据库表结构
①根据表结构去编写实体类做到与表中字段一一对应
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Account {
private Long id;
private String actno;
private Double balance;
}
②根据网页所知功能需求是银行转账,在Mapper接口编写方法
@Mapper
public interface AccountMapper {
/**
* 根据账号查询账户信息
* @param actno
* @return
*/
Account selectByActno(String actno);
/**
* 更新账户信息
* @param account
* @return
*/
int updateByActno(Account account);
}
③根据Mapper接口的方法在映射文件中写Sql语句
④根据Mapper接口所需方法在业务层中实现
public interface AccountService {
/**
* 根据账号查询账户信息
* @param actno
* @return
*/
Account selectByActno(String actno);
/**
* 更新账户信息
* @param account
* @return
*/
int updateByActno(Account account);
/**
* 账户转账业务。
* @param fromActno 转出账号
* @param toActno 转入账号
* @param money 转账金额
*/
void transfer(String fromActno, String toActno, double money)
throws MoneyNotEnoughException, TransferException;
}
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
public Account selectByActno(String actno) {
Account account = accountMapper.selectByActno(actno);
return account;
}
@Override
public int updateByActno(Account account) {
int count = accountMapper.updateByActno(account);
return count;
}
@Override
@Transactional
public void transfer(String fromActno,String toActno,double money)
throws MoneyNotEnoughException,TransferException {
Account fromAct = selectByActno(fromActno);
if(fromAct.getBalance()
⑤根据想抛出的异常去编写异常类
public class MoneyNotEnoughException extends Exception{
public MoneyNotEnoughException(){}
public MoneyNotEnoughException(String msg){
super(msg);
}
}
public class TransferException extends Exception{
public TransferException(){}
public TransferException(String msg){
super(msg);
}
}
⑥数据层和业务层方法实现后在表示层编写
@Controller
public class AccountController {
@Autowired
private AccountService accountService;
@PostMapping("/bank")
public String transfer(String fromActno, String toActno, double money) {
double m = Double.parseDouble(String.valueOf(money));
try {
accountService.transfer(fromActno,toActno,m);
return "redirect:/success.html";
} catch (MoneyNotEnoughException | TransferException e) {
e.printStackTrace();
return "redirect:/error.html";
}
}
}
2、MyBatis小技巧
一、#{}与${}及模糊查询
这里就不多写了,详细可以看我博客另外一条文章:花了几天整理了学完的Mybatis框架(内含源码及面试题)_慢慢ovo的博客-CSDN博客
放一点笔记出来品品
select
id,
car_num carNum,
brand,
guide_price guidePrice,
produce_time produceTime,
car_type carType
from
t_car
order by
produce_time ${ascOrDesc}
select
id,
car_num carNum,
brand,
guide_price guidePrice,
produce_time produceTime,
car_type carType
from
t_car
where
car_type=#{carType}
delete from t_car
where id in (${ids})
select
id,
car_num carNum,
brand,
guide_price guidePrice,
produce_time produceTime,
car_type carType
from
t_car
where
brand like "%"#{brand}"%"
二、别名机制与mapper标签
mybatis:
#基于SpringBoot的mapper标签
mapper-locations: classpath:mapper/*.xml
#基于SpringBoot的别名机制用于配合xml中的resultType
type-aliases-package: com.chf.pojo
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
三、插入使用生成的主键值
@Mapper
public interface CarMapper{
/**
* 插入车辆信息并且使用生成的主键值
* @param car
* @return
*/
int insertCarUseGeneratedKeys(Car car);
}
3、MyBatis参数处理
一、单个简单类型参数
简单类型包括:
-
七种数据类型(除了boolean)以及他们的包装类
-
String
-
java.util.Date
-
java.sql.Date
@Mapper
public interface StudentMapper {
/**
* 当接口的方法的参数只有一个,并且参数的数据类型都是简单类型
* 根据id、name、birth、sex查询
*/
List selectById(Long id);
List selectByName(String name);
List selectByBirth(Date birth);
List selectBySex(Character sex);
}
parameterType属性的作用: 告诉MyBatis框架这个方法的参数类型是什么类型 MyBatis框架自身带有类型自动推断机制,所以大部分情况下parameterType属性都是可以省略不写的
二、Map参数
①、插入信息
@Mapper
public interface StudentMapper{
/**
* 保存学生信息,通过Map参数,以下是单个参数,但是参数的类型不是简单类型,是Map集合
* @param map
* @return
*/
int insertStudentByMap(Map map);
}
insert into
t_student
values
(null,#{姓名},#{年龄},#{身高},#{生日},#{性别})
②、查询单个汽车信息
@Mapper
public interface CarMapper{
/**
* 根据id获取汽车信息,将汽车信息放到Map集合中
* @param id
* @return
*/
Map selectByIdRetMap(Long id);
}
select
id,
car_num carNum,
brand,
guide_price guidePrice,
produce_time produceTime,
car_type carType
from
t_car
where
id = #{id}
③、返回多个Map
查询结果大于等于1条数据,则可以返回一个存储Map集合的List集合,List
@Mapper
public interface CarMapper{
/**
* 查询所有的Car信息返回一个放Map集合的List集合
* @return
*/
List
select
id,
car_num carNum,
brand,
guide_price guidePrice,
produce_time produceTime,
car_type carType
from
t_car
④、返回Map
通过Car的id做Key,以后取出对应的Map集合时更方便
@Mapper
public interface CarMapper{
/**
* 查询所有的Car返回一个大Map结合
* Map集合的key是每条记录的主键值
* Map集合的value的每条记录
* @return
*/
@MapKey("id")
Map> selectAllRetMap();
}
select
id,
car_num carNum,
brand,
guide_price guidePrice,
produce_time produceTime,
car_type carType
from
t_car
{
1={carType=燃油车, carNum=1001, guidePrice=10.00, produceTime=2022-10-1, id=1, brand=宝马520Li},
2={carType=新能源, carNum=1002, guidePrice=55.00, produceTime=2022-10-2, id=2, brand=奔驰E300L},
6={carType=新能源, carNum=1111, guidePrice=30.00, produceTime=2022-10-3, id=6, brand=奔驰},
7={carType=新能源, carNum=111, guidePrice=30.00, produceTime=2022-10-2, id=7, brand=奔驰},
10={carType=新能源, carNum=111, guidePrice=30.00, produceTime=2022-10-2, id=10, brand=奔驰},
11={carType=新能源, carNum=111, guidePrice=30.00, produceTime=2022-10-8, id=11, brand=奔驰}
}
三、实体类参数
@Mapper
public interface StudentMapper{
/**
* 保存学生信息,通过POJO参数,Student是单个参数,但不是简单类型
* @param student
* @return
*/
int insertStudentByPOJO(Student student);
}
insert into
t_student
values
(null,#{name},#{age},#{height},#{birth},#{sex})
四、多参数(@Param)
不需要使用arg0、arg1、param1、param2等等,直接使用@Param注解增强可读性
需求:根据name和age查询学生信息
@Mapper
public interface StudentMapper{
/**
* 这是多参数查询
* 根据name和sex查询Student信息
* 如果是多个参数的话,MyBatis框架底层的做法如下:
* MyBatis框架会自动创建一个Map集合并且Map集合是以这种方式存储参数的
* map.put("arg0",name);/map.put("param1",name);
* map.put("arg1",sex);/map.put("param2",sex);
*
* 使用Param注解指定Sql语句中的#{}命名
* @param name
* @param sex
* @return
*/
List selectByNameAndSex(
@Param("nnn") String name,
@Param("sss") Character sex);
}
五、resultMap结果映射
①、使用resultMap进行结果映射(常用)
查询结果的列名和java对象的属性名对应不上的做法?
-
第一种方式:as 给列起别名 as可以省略不写,我们前面的做法就是如此
-
第二种方式:使用resultMap进行结果映射
-
第三种方式:是否开启驼峰命名自动映射(设置settings)
在一对标签中resultType和resultMap两者只能有一个 当查询要返回对象,
而且属性和字段不一致的时候用resultMap。
@Mapper
public interface CarMapper{
/**
* 查询所有的Car信息,使用resultMap标签进行结果映射
* @return
*/
List selectAllByResultMap();
}
select
id,
car_num,
brand,
guide_price,
produce_time,
car_type
from
t_car
②、开启驼峰命名规范自动映射
使用这种方式的前提是:属性名遵循Java的命名规范,数据库表的列名遵循SQL的命名规范。
Java命名规范:首字母小写,后面每个单词首字母大写,遵循驼峰命名方式。
SQL命名规范:全部小写,单词之间采用下划线分割。
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.chf.pojo
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#开启驼峰自动映射
map-underscore-to-camel-case: true
@Mapper
public interface CarMapper{
List selectAllByResultMapTwo();
}
select
id,
car_num,
brand,
guide_price,
produce_time,
car_type
from
t_car
六、获取总记录条数
@Mapper
public interface CarMapper{
/**
* 获取Car的总记录条数
* @return
*/
Long selectTotal();
}
select
count(*)
from
t_car
4、动态SQL(注:使用了驼峰命名规范)
什么是动态SQL?
-
SQL的内容是变化的, 可以根据条件获取到不同的SQL语句 主要是where部分发生变化。 动态SQL的实现, 使用的是MyBatis提供的标签
为什么使用动态SQL
-
使用动态SQL可以解决某些功能的使用 例如使用条件查询某个商品 输入价格,地区等等进行筛选,如果使用静态SQL可能会查询出来的是一个空内容 但使用动态SQL可以很好的解决这种问题
动态SQL的标签:
一、if标签
@Mapper
public interface CarMapper {
/**
* 多条件查询
* @param brand 品牌
* @param guidePrice 指导价
* @param carType 汽车类型
* @return
*/
List selectByMultiCondition(@Param("brand") String brand,
@Param("guidePrice") Double guidePrice,
@Param("carType") String carType);
}
select
id,car_num,brand,guide_price,produce_time,car_type
from
t_car
where
1 = 1
and brand like "%"#{brand}"%"
and guide_price >= #{guidePrice}
and car_type like "%"#{carType}"%"
二、where标签
where标签的作用:让where子句更加动态智能。
-
所有条件都有空时,where标签保证不会生成where子句。
-
自动去除某些条件前面多余的and或or
@Mapper
public interface CarMapper {
/**
* 使用where标签,让where子句更加的智能
* @param brand
* @param guidePrice
* @param carType
* @return
*/
List selectByMultiConditionWithWhere(@Param("brand") String brand,
@Param("guidePrice") Double guidePrice,
@Param("carType") String carType);
}
select
id,car_num,brand,guide_price,produce_time,car_type
from
t_car
and brand like "%"#{brand}"%"
and guide_price >= #{guidePrice}
and car_type like "%"#{carType}"%"
三、trim标签
@Mapper
public interface CarMapper {
/**
* 使用trim标签
* @param brand
* @param guidePrice
* @param carType
* @return
*/
List selectByMultiConditionWithTrim(@Param("brand") String brand,
@Param("guidePrice") Double guidePrice,
@Param("carType") String carType);
}
select
id,car_num,brand,guide_price,produce_time,car_type
from
t_car
brand like "%"#{brand}"%" and
guide_price >= #{guidePrice} and
car_type like "%"#{carType}"%"
四、set标签
主要使用在update语句当中,用于生成set关键字,同时去掉最后多余的”,”
比如我们只更新提交的不为空的字段,如果提交的数据是空或者””,那么这个字段我们将不更新。
@Mapper
public interface CarMapper {
/**
* 使用set标签进行更新
* @param car
* @return
*/
int updateBySet(Car car);
}
update
t_car
car_num = #{carNum},
brand = #{brand},
guide_price = #{guidePrice},
produce_time = #{produceTime},
car_type = #{carType}
where
id = #{id}
五、choose where otherwise
这三个标签是在一起使用的
Mapper映射语法格式:
等同于Code语法格式:
if(){
}else if(){
}else if(){
}else{
}
@Mapper
public interface CarMapper {
/**
* 使用choose when otherwise标签
* @param brand
* @param guidePrice
* @param carType
* @return
*/
List selectByChoose(@Param("brand") String brand,
@Param("guidePrice") Double guidePrice,
@Param("carType") String carType);
}
select
id,car_num,brand,guide_price,produce_time,car_type
from
t_car
brand like "%"#{brand}"%"
guide_price >= #{guidePrice}
car_type like "%"#{carType}"%"
六、foreach标签
①批量删除
@Mapper
public interface CarMapper {
/**
* 根据id批量删除 foreach
* @param ids
* @return
*/
int deleteByIds(@Param("ids") Long[] ids);
}
delete from
t_car
where
id in
#{aaa}
②批量添加
@Mapper
public interface CarMapper {
/**
* 批量插入,一次插入多条Car信息
* @param cars
* @return
*/
int insertBatch(@Param("cars") List cars);
}
insert into
t_car
values
(null,#{car.carNum},#{car.brand},#{car.guidePrice},#{car.produceTime},#{car.carType})
七、sql、include标签
sql标签用来声明sql片段
include标签用来将声明的sql片段包含到某个sql语句当中
作用:代码复用、易维护
在我跟着老杜学的MyBatis中。他提过一句查询语句最好不要使用星号,因为这会使MySQL索引失效从而导致查询性能下降。所以我上面的笔记没有使用到星号,都是用具体字段进行查询。
id,
car_num,
brand,
guide_price,
produce_time,
car_type
select
from
t_car
5、高级映射及延迟加载
一、多对一
多种方式,常见的包括三种:
-
第一种方式:一条SQL语句,级联属性映射
-
第二种方式:一条SQL语句,association
-
第三种方式(常用):两条SQL语句,分步查询。 优点:可复用、支持懒加载
表的结构如下:
两个实体类如下:
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student { //Student是多的一方
private Integer sid;
private String sname;
private Class clazz; //clazz是一的一方
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Class { //教室类
private Integer cid;
private String cname;
}
第一种方式:级联属性映射
@Mapper
public interface StudentMapper {
/**
* 根据id获取学生信息,同时获取学生关联的班级信息
* @param id 学生的id
* @return 学生对象,但是学生对象当中含有班级对象
*/
Student selectById(Integer id);
}
select
s.sid,s.sname,c.cid,c.cname
from
t_stu s
left join
t_class c
on
s.cid = c.cid
where
s.sid = #{sid}
第二种方式:association
@Mapper
public interface StudentMapper {
/**
* 一条SQL语句,association
* @param id
* @return
*/
Student selectByIdAssociation(Integer id);
}
select
s.sid,s.sname,c.cid,c.cname
from
t_stu s
left join
t_class c
on
s.cid = c.cid
where
s.sid = #{sid}
第三种方式:分步查询
@Mapper
public interface StudentMapper {
/**
* 分步查询第一步:先根据学生的sid查询学生的信息
* @param id
* @return
*/
Student selectByIdStep1(Integer id);
}
@Mapper
public interface ClassMapper {
/**
* 分步查询第二步:根据cid获取班级信息
* @param id
* @return
*/
Class selectByIdStep2(Integer id);
}
select
sid,sname,cid
from
t_stu
where
sid = #{sid}
select
cid,cname
from
t_class
where
cid = #{cid}
二、多对一延迟加载
实际开发中的模式:
把全局的延迟加载打开,如果某个映射文件不需要那么就在association标签里使用fetchType=”eager”关闭
select
sid,sname,cid
from
t_stu
where
sid = #{sid}
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.chf.pojo
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
#实际开发中,大部分都是需要使用延迟加载的
#延迟加载的全局开关,默认值false为不开启
lazy-loading-enabled: true
三、一对多
一对多的实现,通常是在一的一方中有List集合属性。
在Class(教室)类中添加List studentList属性。
一对多的实现通常包括两种实现方式:
-
第一种方式:collection
-
第二种方式:分步查询
两个实体类如下:
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
private Integer sid;
private String sname;
private Class clazz;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Class { //教室类
private Integer cid;
private String cname;
private List studentList;
}
第一种方式:collection
select
c.cid,c.cname,s.sid,s.sname
from
t_class c
left join
t_stu s
on
c.cid = s.cid
where
c.cid = #{cid}
注意:控制台输出的clazz=null是没有问题的
第二种方式:分步查询
@Mapper
public interface ClassMapper {
/**
* 分步查询第一步:根据班级编号获取班级信息
* @param cid
* @return
*/
Class selectByStep1(Integer cid);
}
@Mapper
public interface StudentMapper {
/**
* 根据班级编号查询学生信息
* @param cid
* @return
*/
List selectByStep2(Integer cid);
}
select
cid,cname
from
t_class
where
cid = #{cid}
select
sid,sname
from
t_stu
where
cid = #{cid}
四、一对多延迟加载
与上面的多对一延迟加载相同,可以回去重新看一下。
6、MyBatis缓存机制
缓存:cache
缓存的作用:通过减少IO的方式来提高程序的执行效率。
MyBatis的缓存:将select语句的查询结果放到缓存(内存)当中,下一次还是这条select语句的话,直接从缓存中取,不需要查询数据库。一方面是减少了IO,另一方面不再执行繁琐的查找算法,效率大大提升。
MyBatis缓存包括:
-
一级缓存:将查询到的数据存储到SqlSession中。
-
二级缓存:将查询到的数据存储到SqlSessionFactory中。
-
其他集成第三方的缓存:比如EhCache【Java语言开发的】、Memcache【c语言开发的】等。
缓存只针对于DQL语句,也就是说缓存机制只对应select语句
一、一级缓存
一级缓存是默认开启的,不需要做任何配置(后半句指在纯MyBatis框架中)。
它的作用范围是在同一个SqlSession中,即在同一个SqlSession中共享。
原理:只要使用同一个SqlSession对象执行同一条SQL语句就会走缓存
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Car {
private Long id;
private String carNum;
private String brand;
private Double guidePrice;
private String produceTime;
private String carType;
}
@Mapper
public interface CarMapper {
/**
* 根据id获取Car信息
* @param id
* @return
*/
Car selectById(Long id);
}
id,car_num,brand,guide_price,produce_time,car_type
select
from
t_car
where id = #{id}
我们会发现在SpringBoot结合MyBatis中没有自动开启一级缓存机制,查询相同的id使用了两次查询。但是我们在方法名上添加@Transactional注解就会发现控制台发生了变化:只执行了一次查询语句。也就是说添加了@Transactional注解就能够使用一级缓存,换言之就是同一个SqlSession。
简单回顾一下在纯MyBatis框架中如何使一级缓存失效:
只要在第一次DQL和第二次DQL之间做了两件事中的任意一件就会使一级缓存清空。
-
1、执行了SqlSession的clearCache()方法,这是手动清空缓存
-
2、执行了INSERT或DELETE或UPDATE语句,不管是操作哪张表都会清空缓存
二、二级缓存
二级缓存的范围是SqlSessionFactory
使用二级缓存需要具备以下几个条件:
-
1、在核心配置文件添加cache-enabled: true(全局性地开启或关闭所以映射器配置文件已配置的任何缓存)
但这是默认开启的,所以可以不用添加
-
2、在需要的mapper映射文件中的里添加
-
3、使用二级缓存的实体类对象必须是可序化的,也就是必须实现java.io.Serializable接口
-
4、纯MyBatis中需要将SqlSession对象关闭或提交之后,一级缓存才会被写入二级缓存中,此时二级缓存才可用
id,car_num,brand,guide_price,produce_time,car_type
select
from
t_car
where
id = #{id}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Car implements Serializable {
private Long id;
private String carNum;
private String brand;
private Double guidePrice;
private String produceTime;
private String carType;
}
二级缓存的失效:只要两次查询之间出现了增删改操作,当然这样同样使一级缓存失效
7、MyBatis使用PageHelper
这是我在之前学习MyBatis中没有学习到的东西,由于学MyBatisPlus的时候接触到感觉陌生所以这里就重新学了。
一、limit分页
回顾MySQL的limit后面两个数字:
-
第一个数字:startIndex(起始下标,下标从0开始)
-
第二个数字:pageSize(每页显示的记录条数)
假设已知页码pageNum,还有每页显示的记录条数pageSize,第一个数字如何动态获取?
-
startIndex = (pageSize – 1) * pageSize
@Mapper
public interface CarMapper {
/**
* 分页查询
* @param startIndex 起始下标
* @param pageSize 每页显示的记录条数
* @return
*/
List selectByPage(@Param("startIndex") int startIndex,
@Param("pageSize") int pageSize);
}
id,car_num,brand,guide_price,produce_time,car_type
select
from
t_car
limit
#{startIndex},#{pageSize}
二、PageHelper插件
使用PageHelper插件进行分页更加的快捷。
直接引入依赖即可,不需要配置核心配置文件
com.github.pagehelper
pagehelper-spring-boot-starter
1.2.12
这个极其重要,需要在核心启动类Application中的@SpringBootApplication注解后面添加
@SpringBootApplication(exclude = PageHelperAutoConfiguration.class)
接下来就可以进行我们的测试了。
PageInfo
{
pageNum=1, pageSize=3, size=3, startRow=0, endRow=2, total=3, pages=1,
list=[Car(id=1, carNum=1001, brand=宝马520Li, guidePrice=10.0, produceTime=2022-10-1, carType=燃油车), Car(id=2, carNum=1002, brand=奔驰E300L, guidePrice=55.0, produceTime=2022-10-2, carType=新能源), Car(id=6, carNum=1111, brand=奔驰, guidePrice=30.0, produceTime=2022-10-3, carType=燃油车)], prePage=0, nextPage=0, isFirstPage=true, isLastPage=true, hasPreviousPage=false, hasNextPage=false, navigatePages=3, navigateFirstPage=1, navigateLastPage=1, navigatepageNums=[1]
}