本文主要是介绍一文搞定MybatisPlus,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Mybatis简介
MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
(来自官网)
体验Mybatisplus
1.创建SpringBoot工程,导入mysql,lombok,spring web
2.改setting里面的encoding为utf-8
3.pom文件中引入依赖
<!-- druid数据源--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency>
<!-- mybatisplus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version></dependency>
4.yml文件中设置数据源相关参数
4.编写实体类
package com.example.entity;
import lombok.Data;
@Data
public class User {private Long id;private String name;private String password;private Integer age;private String tel;
}
5.编写mapper接口
继承BaseMapper,指定泛型为User
加上@Mapper注解
tips:如何让springboot的启动类扫到mapper文件夹里面的mapper接口
1.加上@Mapper注解,同时需要保证mapper文件夹在启动类所在包或其子包中(也就是启动类所在的层次要比被扫描的文件夹更高或同级)
2.启动类加上@MapperScan,指定要扫描的文件夹
6.写一个测试类,进行测试
@SpringBootTest
public class TestUserMapper {@Autowiredprivate UserMapper userMapper;@Testvoid testSelectAll(){List<User> users = userMapper.selectList(null);System.out.println("users:"+users);}
}
ps:如果是第二种方式扫描mapper接口,userMapper会爆红,这是因为UserMapper是一个接口,不能实例化对象 。只有在服务器启动IOC容器初始化后,由框架创建DAO接口的代理对象来注入。 现在服务器并未启动,所以代理对象也未创建,IDEA查找不到对应的对象注入,所以提示报红 一旦服务启动,就能注入其代理对象,所以该错误提示不影响正常运行。
Mybatisplus标准CURD以及分页
增
@Testvoid testInsert(){//增User user = new User();user.setName("李四");user.setAge(18);user.setPassword("1599");user.setTel("18959657411");int i = userMapper.insert(user);System.out.println("新增了"+i+"条数据");}
删
@Testvoid testDelete(){//删int i = userMapper.deleteById(1827551884282552322L);System.out.println("删除了"+i+"条数据");}
改
@Testvoid testUpdate(){//改User user = new User();user.setId(1L);user.setName("Tom·A·Cat");int i = userMapper.updateById(user);System.out.println("修改了"+i+"条数据");}
ps:修改的时候,只修改实体对象中有值的字段 。如果是我们以前用的mybatis做开发,没有指定password,age和tel,这些字段会被覆盖为null,mybatisplus的优势就体现出来了。
查
@Testvoid testSelect(){//查User user = userMapper.selectById(1L);System.out.println(user);}
分页
官网:分页插件 | MyBatis-Plus
首先要添加一个分页拦截器
package com.example.config;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MybatisPlusConfig {/*** 添加分页插件*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加// 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbTypereturn interceptor;}
}
使用分页功能
@Testvoid testPage(){//1.创建IPage对象(Page实现了IPage接口),设置分页参数Page<User> page = new Page<>(1,3);//当前页码为1 每页5条数据//2.执行分页查询userMapper.selectPage(page,null);//没有指定条件//3.获取分页结果System.out.println("当前页码:"+page.getCurrent());System.out.println("每页显示数:"+page.getSize());System.out.println("一共多少页:"+page.getPages());System.out.println("一共多少条数据:"+page.getTotal());System.out.println("数据:"+page.getRecords());}
*****************************************************
DQL编程控制
条件查询
我们需要依靠wrapper来构建要查询的条件
构建条件查询的三种方式
年龄小于10岁的
1.QueryWrapper
//1.QueryWrapper@Testvoid testQuery01(){QueryWrapper<User> qw = new QueryWrapper<>();qw.lt("age",10);System.out.println(userMapper.selectList(qw));}
2.lambda方式的QueryWrapper
//2.lambda方式的QueryWrapper@Testvoid testQuery02(){QueryWrapper<User> qw = new QueryWrapper<>();qw.lambda().lt(User::getAge,10);System.out.println(userMapper.selectList(qw));}
解决了字段可能写错的问题 。
3.LambdaQueryWrapper
//3.LambdaQueryWrapper@Testvoid testQuery03(){LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();lqw.lt(User::getAge,10);System.out.println(userMapper.selectList(lqw));}
不用 .lambda()了,代码更加简洁。
多条件查询
年龄大于10且小于20的 (and)
//年龄大于10且小于20@Testvoid testQuery04(){LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();lqw.gt(User::getAge,10).lt(User::getAge,20);//支持链式编程System.out.println(userMapper.selectList(lqw));}
年龄小于10或大于20的 (or)
//年龄小于10或大于20@Testvoid testQuery05(){LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();lqw.lt(User::getAge,10).or().gt(User::getAge,20);// or()System.out.println(userMapper.selectList(lqw));}
null条件判定
需求
用户输入两个年龄(minAge和maxAge),查询用户指定年龄范围里的User,但是如果用户只想查看18岁以下的User,只填写了maxAge为18,那么minAge为null,这时候要怎样让minAge不加入条件呢?MybatisPlus早就想到了这一点。
**************************************************************
写一个AgeField实体类来模拟前端传来的minAge和maxAge
package com.example.entity;
import lombok.Data;
@Data
//模拟前端传过来的最小年龄和最大年龄
public class AgeField {private Integer minAge;private Integer maxAge;
}
测试代码
/*** null判定*/@Testvoid testQuery06(){AgeField af = new AgeField();af.setMinAge(10);af.setMaxAge(20);LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();lqw.gt(af.getMinAge()!=null,User::getAge,af.getMinAge()).lt(af.getMaxAge()!=null,User::getAge,af.getMaxAge());List<User> users = userMapper.selectList(lqw);System.out.println(users);System.out.println(users.size());}
参数condition:为真时该条件才加入查询语句
参数column:表中字段
参数val:传入的字段值
数据库
共15条记录
测试结果
传入10和20 ,查到两条数据
现在我们把这两条注掉,两条的条件都为false,不加入查询条件,相当于select * from user
// af.setMinAge(10);
// af.setMaxAge(20);
查到15条数据,也就是user表中的所有数据,符合预期。
查询投影
user表中新增一个sex字段,同时修改实体类
查询结果包含模型类中部分属性
LambdaQueryMapper写法
@Test//查询结果包含模型类中部分属性void testQuery07(){LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();//查询年龄在10~20之间的所有用户的id,name,age信息lqw.select(User::getId,User::getName,User::getAge);lqw.gt(User::getAge,10);lqw.lt(User::getAge,20);System.out.println(userMapper.selectList(lqw));}
QueryMapper写法
@Testvoid testQuery08(){QueryWrapper<User> qw = new QueryWrapper<>();qw.select("id","name","age");qw.gt("age",10);qw.lt("age",20);System.out.println(userMapper.selectList(qw));}
测试结果
查询结果包含模型类中未定义属性
聚合和分组,无法使用lambda表达式完成,正如MP官网所说的,MybatisPlus只是对Mybatis进行增强,MP无法做到的事情我们还是要老老实实地回到mapper接口里面写sql。
聚合查询
@Test//聚合void testQuery09(){QueryWrapper<User> qw = new QueryWrapper<>();//这里也可以连着写,为了方便看,用逗号分开qw.select("count(*) as count","max(age) as maxAge","min(age) as minAge","sum(age) as sumAge","avg(age) as avgAge");//用selectMaps方法,返回一个map,这个map的key为聚合字段,value为聚合结果List<Map<String, Object>> maps = userMapper.selectMaps(qw);System.out.println(maps);}
分组查询
相当于 select sex,count(*) from user group by sex;
@Test//分组void testQuery10(){//按性别分组QueryWrapper<User> qw = new QueryWrapper<>();qw.select("sex,count(*) as count");qw.groupBy("sex");List<Map<String, Object>> maps = userMapper.selectMaps(qw);System.out.println(maps);}
深入体验查询条件的设置
条件构造器 | MyBatis-Plus
等值查询
@Testvoid testQuery11(){//等值查询 模拟登录校验LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();lqw.eq(User::getName,"张三").eq(User::getPassword,"zs666");User loginUser = userMapper.selectOne(lqw);//只查一个用selectOne就可以System.out.println(loginUser);}
范围查询
@Testvoid testQuery12(){//范围查询 lt le gt ge betweenLambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();lqw.between(User::getAge,10,20);userMapper.selectList(lqw);}
模糊查询
@Testvoid testQuery13(){/*** 模糊查询 like* %:匹配任意字符* _:匹配任意单个字符* *********************** 官网中并没有看到匹配任意单个字符要怎么写,* 如果要匹配 刘亦菲、刘二菲、刘三菲...这样的名字可能还是要自己写sql (like 刘_菲)*/LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();//lqw.like(User::getName,"o"); //名字里带o的 like %o%//lqw.likeRight(User::getName,"杨"); // like 杨%//lqw.likeLeft(User::getName,"at"); // like %atlqw.notLike(User::getName,"月"); //not like %月%userMapper.selectList(lqw);}
排序查询
1.
@Testvoid testQuery14(){//排序查询LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();/*** 参数:condition isAsc columns* conidtion:条件,为true时 orderby 加入查询语句* isAsc:是否为升序* columns:按哪个列排序*///按年龄降序,年龄一样按id降序lqw.orderBy(true,false,User::getAge,User::getId);userMapper.selectList(lqw);}
2.
@Testvoid testQuery15(){LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();//按年龄降序,按id升序//注意这里哪个字段写在前面,它的优先级就更高lqw.orderByDesc(User::getAge);lqw.orderByAsc(User::getId);userMapper.selectList(lqw);}
字段映射和表名映射
问题1:表字段与编码属性设计不同步
实体类里面是pwd,数据库里面是password
解决:
问题2:编码中添加了数据库中未定义的属性
问题3:采用默认查询开放了更多的字段查看权限
pwd是敏感数据,如果进行查询,返回的json数据中携带了pwd,这是很危险的,因此我们需要在查询中将pwd默认隐藏
@Testvoid testQuery16(){LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();userMapper.selectList(lqw);}
默认的查询是查不出来pwd的
@Testvoid testQuery17(){LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();lqw.select(User::getPwd);userMapper.selectList(lqw);}
指定要查pwd,这样是可以查出来的
问题4:表名与编码开发设计不同步
DML编程控制
id生成策略
MP通过@TableId控制id的生成策略
ctrl+鼠标左键,进入IdType源码
/*** 数据库ID自增* <p>该类型请确保数据库设置了 ID自增 否则无效</p>*/AUTO(0),/*** 该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)*/NONE(1),/*** 用户输入ID* <p>该类型可以通过自己注册自动填充插件进行填充</p>*/INPUT(2),/* 以下3种类型、只有当插入对象ID 为空,才自动填充。 *//*** 分配ID (主键类型为number或string),* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(雪花算法)** @since 3.3.0*/ASSIGN_ID(3),/*** 分配UUID (主键类型为 string)* 默认实现类 {@link com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator}(UUID.replace("-",""))*/ASSIGN_UUID(4),/*** @deprecated 3.3.0 please use {@link #ASSIGN_ID}*/@DeprecatedID_WORKER(3),/*** @deprecated 3.3.0 please use {@link #ASSIGN_ID}*/@DeprecatedID_WORKER_STR(3),/*** @deprecated 3.3.0 please use {@link #ASSIGN_UUID}*/@DeprecatedUUID(4);
AUTO(0)
id自增,同时数据库中要设置id自增
@TableId(type = IdType.AUTO)private Long id;
ALTER TABLE tb_user AUTO_INCREMENT=16;
@Testvoid testInsert01(){User user = new User();user.setName("李薇");user.setPwd("12637489");user.setAge(50);user.setSex(1);user.setTel("959559999");user.setId(20L);userMapper.insert(user);}
指定它的id为20,insert到数据库的id仍是16
删除这条数据,重新insert一次
id为17,由此知道AUTO_INCREMENT不会因为数据的删除而回退
NONE(1)
该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
@TableId(type = IdType.NONE)private Long id;
执行两次 testInsert01 ,指定id为20,25,结果如下
INPUT(2)
需要自己输入id
@TableId(type = IdType.INPUT)private Long id;
执行两次 testInsert01 ,指定id为30,35,结果如下
ASSIGN_ID(3)<——过时的ID_WORKER和ID_WORKER_STR合并
雪花算法生成id(可兼容数值型和字符串型)
@TableId(type = IdType.ASSIGN_ID)private Long id;
指定id为40,这样生成的id为40
@Testvoid testInsert01(){User user = new User();user.setName("李薇");user.setPwd("12637489");user.setAge(50);user.setSex(1);user.setTel("959559999");user.setId(40L);userMapper.insert(user);}
不指定id,按照雪花算法生成id
// user.setId(45L);
雪花算法生成的id的格式
ASSIGN_UUID(4)<——过时的UUID
以UUID生成算法作为id生成策略
需要表中主键的id为varchar(32),
因为生成的uuid为32位,形如b098c2c603a317326af11fe64371e916
@TableId(type = IdType.ASSIGN_UUID)private String id;
执行1次添加
多数据删除
@Testvoid testDelete01(){//根据id删除多条数据ArrayList<Long> list = new ArrayList<>();list.add(1827910579805876225L);list.add(40L);userMapper.deleteBatchIds(list);}
成功删除两条数据
逻辑删除
问题分析
实战演练
1.数据库表中添加一个deleted字段,默认0
2.实体类添加属性
//0表示正常值 1表示该数据已被逻辑删除@TableLogic(value = "0",delval = "1")private Integer deleted;
3.测试
3.1先进行一次查询所有,注意现在数据库里面的记录的deleted字段都是0
@Testvoid testLogic(){userMapper.selectList(null);}
查出来所有的19条记录(注意这个sql,我们的代码中并没有显示地设定任何查询条件)
3.2 删除第一条数据再进行一次查询所有
@Testvoid testLogic(){userMapper.deleteById(1L);userMapper.selectList(null);}
id为1的记录已被逻辑删除,这里只有18条记录
deleted字段为1,表示被逻辑删除了
3.3 指定查询条件查一下
@Testvoid testLogic02(){userMapper.selectById(1L);}
他会自动把逻辑删除字段加到查询条件里
4.设置逻辑删除字段的另一种方式:在yml文件中配置
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志global-config:banner: off # 关闭MybatisPlus启动图标db-config:table-prefix: tb_ #表名前缀logic-delete-field: deleted #逻辑删除字段名logic-not-delete-value: 0 #正常值logic-delete-value: 1 #逻辑删除后的值
乐观锁
实现思路
0.添加拦截器
@Configuration
public class MybatisPlusConfig {/*** 添加分页插件*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());//添加乐观锁的拦截器interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加// 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbTypereturn interceptor;}
}
1.表中添加字段version,用作版本控制
给默认值为1,长度为int的最大值11位
2.实体类中添加属性
@Versionprivate Integer version;
3.测试
测试1
起始数据
执行更新语句
@Testvoid testLock01(){User user = new User();user.setId(1L);user.setName("tomcat");userMapper.updateById(user);}
发现version并没有改变,这是因为我们并没有拿到过version
重新执行如下语句
@Testvoid testLock02(){User user = userMapper.selectById(1L);user.setName("tomkitty");userMapper.updateById(user);}
我们先查询id为1的用户拿到了该用户的实体(当然包括它的version)这样一来更新就会自动校验version
测试2
模拟两个请求对同一个记录进行修改
void testLock03(){User user1 = userMapper.selectById(2L);User user2 = userMapper.selectById(2L);user1.setName("JerryA");user2.setName("JerryB");userMapper.updateById(user2);//version被修改成2userMapper.updateById(user1);//它还以为是version=1 结果和数据库的version对不上 修改失败}
最终结果
代码生成器
1.新创建一个springboot项目
引入mysql lombok,web(会生成controller) 即可
2.在pom文件中加入代码生成器需要的依赖
<!--代码生成器--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.4.1</version></dependency><!--velocity模板引擎--><dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.3</version></dependency>
3.创建代码生成类
package com.example;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;public class CodeGenerator {public static void main(String[] args) {//1.获取代码生成器的对象AutoGenerator autoGenerator = new AutoGenerator();//2.设置数据库相关配置DataSourceConfig ds = new DataSourceConfig();ds.setDriverName("com.mysql.cj.jdbc.Driver");ds.setUrl("jdbc:mysql://localhost:3306/mybatisplus_db?serverTimezone=UTC");ds.setUsername("root");ds.setPassword("root");autoGenerator.setDataSource(ds);//3.设置全局配置GlobalConfig gc = new GlobalConfig();//代码生成位置(默认D盘的根目录) user.dir=D:/java_workspacegc.setOutputDir("D:/java_workspace/MybatisPlusProject02/src/main/java/com/example");//设置生成完毕后是否打开生成代码所在的目录(默认打开)gc.setOpen(false);gc.setAuthor("chen");//设置作者gc.setFileOverride(true);//是否覆盖原来生成的文件gc.setMapperName("%sMapper"); //设置名字格式为为 UserMapper,AccountMapper.....gc.setIdType(IdType.ASSIGN_ID); //设置id生成策略autoGenerator.setGlobalConfig(gc);//4.设置包名相关配置PackageConfig pc = new PackageConfig();//pc.setParent("com.baomidou");pc.setParent("mpGenerator");//设置生成的包名pc.setEntity("entity");//设置实体类包名pc.setMapper("mapper");//设置mapper接口文件夹的名字autoGenerator.setPackageInfo(pc);//5.策略设置StrategyConfig sc = new StrategyConfig();sc.setInclude("tb_user");//设置参与代码生成的表sc.setTablePrefix("tb_");//设置数据库表前缀,这样它生成的类前面就不会带Tb_了sc.setRestControllerStyle(true);//设置控制器为rest风格的控制器sc.setVersionFieldName("version");//设置乐观锁字段名sc.setLogicDeleteFieldName("deleted");//设置逻辑删除字段名,仍然需要自己指定逻辑删除的值sc.setEntityLombokModel(true);//启用lombokautoGenerator.setStrategy(sc);//6.执行代码生成autoGenerator.execute();}
}
4.运行代码生成类中的main方法,查看结果
开启Mybatisplus日志
yml文件里面配置
#查看MP执行的sql语句
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
关闭Spring初始化日志,SpringBoot图标,MybatisPlus图标
这篇关于一文搞定MybatisPlus的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!