Mybatis-plus流程图解和使用,一级和二级缓存禁用启用详解,map的k-v入库,自增id,分页查询等各种骚操作
Posted 宇宙磅礴而冷漠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis-plus流程图解和使用,一级和二级缓存禁用启用详解,map的k-v入库,自增id,分页查询等各种骚操作相关的知识,希望对你有一定的参考价值。
文章目录
介绍
官网地址
官网地址:https://mybatis.plus/guide/page.html
基本工作流程
图示:
(1)mybatis-config.xml是Mybatis的核心配置文件,通过其中的配置可以生成SqlSessionFactory,也就是SqlSession工厂
(2)基于SqlSessionFactory可以生成SqlSession对象
(3)SqlSession是一个既可以发送SQL去执行,并返回结果,类似于JDBC中的Connection对象,也是Mybatis中至关重要的一个对象。
(4)Executor是SqlSession底层的对象,用于执行SQL语句
(5)MapperStatement对象也是SqlSession底层的对象,用于接收输入映射(SQL语句中的参数),以及做输出映射(即将SQL查询的结果映射成相应的结果)
缓存机制
springboot里默认都启用两种缓存&禁用启用
mybatis源码显示一级缓存和二级缓存都开启了
public Configuration() {
//...
this.cacheEnabled = true;//二级
this.localCacheScope = LocalCacheScope.SESSION;//一级
//..
}
要使用二级缓存只需要配置cache标签,实体类实现序列化,在mapper文件中开启,二级缓存是以一个mapper为单位,该mapper下的所有操作都关系二级缓存
<mapper namespace="com.xx.mapper.XxxMapper" >
<cache/>
<!--xxx-->
</mapper>
如何关闭二级缓存?
1.普通去除->去掉mapper.xml里的cache标签就行了
2.根本去除->执行1,再配置yml
mybatis-plus.configuration.cache-enabled=false//源码配置默认true
如何关闭一级缓存?
1.普通去除->使用随机数生成不同sql
(int)Math.random()*1000 (int)Math.random()*10000
where #{random}=#{random}
2.根本去除->yml配置
mybatis.configuration.local-cache-scope=statement
代码验证一二级缓存
一级缓存验证:
配置详情:
采用默认配置,不做修改任何缓存配置。
代码:
@Autowired
private StudentDao studentDao;
@Test
public void c1(){
for (int i=0;i<3;i++){
List<Student> list=studentDao.findStudents();
System.out.println(list.hashCode());
}
}
查看日志:
结论:可以看到sql语句被执行三次,三次访问了数据库.
原因:一次访问数据库相当于一次service里的操作,虽然默认开启过了一级缓存,操作完一次会关闭,但是每次操作都是新的sqlsession,而一级缓存是session级别的。当调用SqlSession的修改,添加,删除,commit(),close()等方法时就会清空一级缓存.
修改代码加上@Transactional开启事物:
@Autowired
private StudentDao studentDao;
@Test
@Transactional
public void c1(){
for (int i=0;i<3;i++){
List<Student> list=studentDao.findStudents();
System.out.println(list.hashCode());
}
}
查看日志:
结论:只访问了一次数据库。
原因:同一个事物里的操作都属于一个sqlsession,因为默认开启过了一级缓存,数据会尝试从hashmap结构的缓存查找数据,当然会有数据的了。
如果再次修改代码,使用自己创建的sqlSession:
@Autowired
private SqlSessionFactory sqlSessionFactory;
@Test
public void c2(){
SqlSession sqlSession=sqlSessionFactory.openSession();
for (int i=0;i<3;i++){
List<Student> list=sqlSession.getMapper(StudentDao.class).findStudents();
System.out.println(list.hashCode());
}
}
查看日志:
结论:只执行一次数据库查询。
二级缓存验证:
首先mapper.xml不添加cache标签
代码:
@Test
public void c3(){
for (int i=0;i<3;i++){
List<Dept> list=StudentDao.findDept();
System.out.println(list.hashCode());
}
}
查看日志:
结论:没有进入一级缓存和二级缓存,访问了三次数据库。
在mapper.xml加入cache标签
代码不变
查看日志:
结论:只访问了一次数据库,进入了二级缓存.
一级缓存解释
SqlSession级别的缓存,实现在同一个会话中数据的共享
一级缓存的生命周期和SqlSession一致
当有多个SqlSession或者分布式环境下,数据库写操作会引起脏数据。
二级缓存解释
SqlSessionFactory级别的缓存,实现不同会话中数据的共享,是一个全局变量
可自定义存储源,如Ehcache
当开启缓存后,数据查询的执行的流程是:二级缓存>一级缓存>数据库
不同于一级缓存,二级缓存可设置是否允许刷新和刷新频率实现
plus复杂语句嵌套单表操作
1.pojo实体类与数据库表关联
实体类要求示例:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain=true)//实现链式加载
@TableName("user")//1.关联表名
public class User {
//对象与数据表完美映射,对象名称与表名,对象的属性与表字段
@TableId(type = IdType.AUTO)//主键自增,value = "id"可不写
private Integer id;
//@TableField(value = "name")//可以省略,但是名称必须相同
private String name;
private Integer age;
private String sex;
@TableField(exist = false)//入库丢弃,不属于数据库表字段
private String dept;
}
mapper接口示例:通常plus包含单表操作,自己可以不需要写方法
@Mapper
public interface UserMapper extends BaseMapper<User> {
List<User> findAll();
}
语句举例:delete from user where id=? and creatorId=? and (valid=? or startTime>?)
//and()语句相当于一个独立条件用()括起来。
UserMapper.delete(new QueryWrapper<FSTest>()
.eq("id",testId)
.eq("creatorId",creatorId)
.and(qw->qw.eq("valid", 0).or().gt("startTime", new Date())));
获取自增id
非plus版本:
1.插入pojo实体类,执行成功,该pojo会被赋值自增的id值
<insert id="insertAuto" parameterType="com.xx.pojo.Teacher" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into teacher (name,dept,password) values (#{name},#{dept},#{password})
</insert>
2.map类型,自增id会封装到map里
<insert id="insertAuto" parameterType="map">
insert into teacher (name,dept,password) values (#{name},#{dept},#{password})
<selectKey keyProperty="id" order="AFTER" keyColumn="id" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID() AS id
</selectKey>
</insert>
plus版本:
如果是使用自带封装的plus插入方法,成功执行会把id封装到pojo
如果是mapper.xml自定义方式则和非plus版本相同
sql联表优化查询
<!-- 方案 1
select c.*,p.name parentName
from sys_menus c left join sys_menus p
on c.parentId=p.id
-->
<!-- 方案 2 -->
select c.*,(
select p.name
from sys_menus p
where c.parentId=p.id
) parentName
from sys_menus c
PageHelper和Page分页
非plus版本->PageHaper
依赖:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>
代码:
PageHelper.startPage(pageCurrent, 15);
//firstTestMapper.doGetFirstTest(valid, notEnd,creatorId)返回一个list
PageInfo<FSTest> info=new PageInfo<>(firstTestMapper.doGetFirstTest(valid, notEnd,creatorId));
return new PageObject<>((int)info.getTotal(),info.getPages() ,15 ,info.getList() ,pageCurrent);
plus版本->Page
配置类:
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {
// 旧版
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
// 最新版
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
}
操作:
Page<Item> pageItem=new Page<>(pageCurrent,rowsNumber,true);//定义页
QueryWrapper<Item> queryWrapper=new QueryWrapper<>();
queryWrapper.orderByDesc("updated");
IPage<Item> iPage=itemMapper.selectPage(pageItem,queryWrapper );//返回页数据
return new EasyUITable(iPage.getTotal(),iPage.getRecords());
各种常用标签
foreach
foreach元素的属性主要有item,index,collection,open,separator,close。
item:集合中元素迭代时的别名
index:集合中元素迭代时的索引
open:常用语where语句中,表示以什么开始,比如以’('开始
separator:表示在每次进行迭代时的分隔符
close 常用语where语句中,表示以什么结束
传入list或array:
<select id="queryById" resultMap="BaseReslutMap" >
select * from user
where id in
<foreach collection="ids" item="id" index="index" open="(" separator="," close=")">
#{id}
</foreach>
</select>
if
<if test="ids!=null and ids.length!=0">
id in <!--(1,3,32,2)-->
<foreach collection="ids" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</if>
choose
<choose>
<when test="notEnd==null">
and second_test.id=#{testId}
</when>
<otherwise>
<--再嵌套-->
<choose>
<when test="notEnd==true">
and second_test.endTime > NOW()
</when>
<otherwise>
and second_test.endTime < NOW()
</otherwise>
</choose>
</otherwise>
</choose>
resultMap
property为pojo属性,column为数据库字段
<resultMap id="studentMap" type="Student">
<result property="className" column="class"/>
</resultMap>
模糊查询:
select * from user where name like concat('%',concat(#{name},'%'))
map-kv操作
把map的不确定的key和value全部作为值插入固定字段(list[map1,map2,map3,…])
例如: key=100,value=“hello” 分别插入id和world字段,最终传list到mapper.
List<Map> getMapList(Map<String, String> map){
Set<String> set=map.keySet();
List<Map> list=new ArrayList<>();
Iterator<String> iterator=set.iterator();
while (iterator.hasNext()){
Map map1=new HashMap();
String id=iterator.next();
String world=map.get(id);
map1.put("id",id );
map1.put("world", world);
list.add(map1);
}
return list;
}
mapper.xml配置:
<insert id="subTest">
insert into tableOne(id,world,isDone) values
<foreach collection="list" item="map" index="index" separator=",">
(#{map.id},#{map.world},#{isDone})
</foreach>
</insert>
以map形式出库
<select id="getMapList" resultType="java.util.Map">
select * from teacher
</select>
List<Map> getMapList();
以上是关于Mybatis-plus流程图解和使用,一级和二级缓存禁用启用详解,map的k-v入库,自增id,分页查询等各种骚操作的主要内容,如果未能解决你的问题,请参考以下文章