一、MyBatisPlus
- MyBatisPlus 能大量的节省我们些CRUD的时间,所有的CRUD都能通过MyBatisPlus来自动完成
- MyBatis Plus 简称 MP 是一个MyBatis的增强工具包,制作增强不做改变,为简化开发,提高生产率而生
- MyBatis 官网
- MyBatis文档
二、快速入门
- 初始化工程
- 创建一个springboot项目,选上lombok,spring web
导入mybatis plus 依赖
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.0.5</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>
创建一个表,随便创建一个用户表
编写 application.properties 配置文件
# MySQL 5 配置spring.datasource.username=rootspring.datasource.password=0000spring.datasource.url=jdbc:mysql://localhost:3306/mpspring.datasource.driver-class-name=com.mysql.jdbc.Driver

使用MyBatis之后
- 创建实体类 User
- 创建UserMapper接口 继承 BaseMapper
【这个Mapper就是Dao】 - 在UserMapper接口上添加@Repository 注解
- 在Application主方法类 上添加
- //扫描Mapper文件
- @MapperScan(“com.yixuexi._02_spring_boot_mybatisplus.dao”)
- 使用
1. 细节
- MyBatis和MyBatis-spring 依赖就不要加入到项目中了。MyBatisPlus自动维护
在真实开发中,表的字段一般都会有 version(乐观锁) deleted(逻辑删除) gmt_create(创建时间),gmt_modified(修改时间)
2. 坑:
springboot的MySQL start 的mysql依赖版本是 8.0 版本,和电脑上的不匹配的话会报错,所以最好别选mysqlStart,自己添加mysql5.0的依赖
- 需要在主启动类上去扫描我们的mapper包下所有的接口
- @MapperScan(“com.yixuexi._02_spring_boot_mybatisplus.dao”)
- 如果是MySQL8.0的话,那么spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
3. 配置日志
因为现在所有的SQL是不可见的,我们希望通过日志的方式将SQL输出出来
# 配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
三、insert update
1. insert 插入
@Testvoid insertPlus(){User user = new User();user.setId(7);user.setAge(18);user.setName("仝子瑜");user.setEmail("2298320493@qq.com");// 如果不设置id的话,会帮我们自动生成id值 这个id值很大很大int insert = userMapper.insert(user);// 输出受影响的行数System.out.println(insert);}
为什么不设置id,自动插入的值会是很大很大的呢? 123561315643213216541
- 数据库插入的id的默认值为:全局的唯一id
- 主键生成策略: 雪花算法,uuid,redis 等等等
- 雪花算法: https://www.cnblogs.com/haoxinyue/p/5208136.html
2. 我们需要配置主键自增
- 在实体类主键字段上添加 @TableId(type = IdType.AUTO)
- 数据库字段一定要是自增的 auto_increment
- 上面设置了后,插入的User对象中,就算id属性赋值了,也会按照它的自增
- 其余的IdType源码解释

一旦手动输入[ IdType.INPUT ]之后 就需要自己写ID,如果不写就是NULL【其实这个也可以用来自增,因为实体类是null,到数据库里面会根据数据库的值进行自增】
3.update更新
@Testvoid updatePlus(){User user = new User();user.setId(1);user.setEmail("2298320493@qq.com");user.setName("zhangsan");user.setAge(15);// 注意!这里传入的值是一个User对象,通过这个user对象的id进行更新userMapper.updateById(user);}
update的SQL语句都是MyBatisPlus自己的动态SQL ,通过对属性的ifnull 进行判断。
4. 自动填充
- 创建时间,修改时间!这些操作一般都是自动化完成的,我们不希望手动更新!
- 阿里巴巴开发手册:所有的数据库表:gmt_create【create_time】 ,gmt_modified 【update_time】几乎所有的表都要配置上,而且需要自动化!
方式一:数据库级别 (工作中不建议使用)
- 在创建表时添加两个字段 create_time update_time
- 字段类型 datetime
-
方式二:代码级别
删除数据库字段的默认值
- 实体类的字段属性上需要增加注解
```java
/**
- 字段插入时,自动填充 / @TableField(fill = FieldFill.INSERT) private Date createTime; /*
- 字段更新时,自动填充 */ @TableField(fill = FieldFill.UPDATE) private Date updateTime;
3. 编写处理器 处理注解【人家已经写好了,我们稍微改改】- 官网地址: [https://baomidou.com/guide/auto-fill-metainfo.html](https://baomidou.com/guide/auto-fill-metainfo.html)- 一定要记得在处理器上面添加@Component注解,将组件添加到容器中```java/*** @date: 2021/1/5 0:02* @author: 易学习* @Component: 一定不要忘记把组件添加到容器中*/@Componentpublic class MyDateObjectHandler implements MetaObjectHandler {/*** 插入时的填充策略* @param metaObject*/@Overridepublic void insertFill(MetaObject metaObject) {/*** setFieldValByName:填充你要指定的值* 第一个参数:要填充的字段名* 第二个参数:要填充的值* 第三个参数:metaObject* 这里要写两个 把更新的字段也写上*/this.setFieldValByName("createTime",new Date(),metaObject);this.setFieldValByName("updateTime",new Date(),metaObject);}/*** 更新时的填充策略* @param metaObject*/@Overridepublic void updateFill(MetaObject metaObject) {// 和上面一样,只是不需要设置 createTime了this.setFieldValByName("updateTime",new Date(),metaObject);}}
四、乐观锁 & 悲观锁
乐观锁:顾名思义十分乐观,他总是认为不会出现问题,无论干什么都不会去上锁!如果出现了问题就再次更新值测试。
悲观锁:顾名思义十分悲观,他总是认为总是出现问题,无论干什么都会去上锁!再去上锁。
乐观锁实现方式:
- 取出记录时,获取当前version
- 更新时,带上这个version
- 执行更新时, SQL: set version = newVersion where version = oldVersion
- 如果version不对,就更新失败
乐观锁:现查询,获得版本号 version = 1
— A 线程
update user set name = ‘仝子瑜’,version = version + 1 where id = 22 and version = 1
—- B 线程,抢先修改,这个时候 version = 2 导致A 修改失败
update user set name = ‘张三’,version = version + 1 where id = 22 and version = 1
1. 测试MP中的乐观锁
- 给数据库中增加version 字段 int 类型 默认值为1
- 实体类添加对应的字段,并且加上@version注解,表明这是乐观锁
- 注册组件(最新版也需要注册,3.0.5需要注册组件) ```java /**
- @date: 2021/1/5 19:01
- @author: 易学习
- @Configuration: 表明这个是一个配置类,这样的话,之前注册的处理器的扫描 @MapperScan()也可以放到这里
- @MapperScan: 扫描处理器,这里扫描的是之前那个 createTime 和 updateTime
- @EnableTransactionManagement: 事务控制 */
@MapperScan(“com.yixuexi.mybatisplus.mapper”) @EnableTransactionManagement @Configuration public class MyBatisPlusConfig {
/*** 注册乐观锁插件*/@Beanpublic OptimisticLockerInterceptor optimisticLockerInterceptor(){return new OptimisticLockerInterceptor();}
}
4. 测试```java/*** 测试乐观锁,成功案例*/@Testvoid happyTest(){// 1.查询用户信息User user1 = userMapper.selectById(1);// 2.修改用户信息user1.setName("马保国");user1.setAge(69);user1.setEmail("mabaoguo@163.com");// 3.执行更新操作userMapper.updateById(user1);}
/*** 测试乐观锁,失败案例*/@Testvoid happyTest2(){// 线程1User user1 = userMapper.selectById(1);user1.setName("马保国");user1.setAge(69);user1.setEmail("mabaoguo@163.com");// 线程2 模拟另外一个线程执行了插队操作User user2 = userMapper.selectById(1);user2.setName("旭旭宝宝");user2.setAge(35);// 线程2 抢先更新userMapper.updateById(user2);// 线程1执行更新操作// 如果没有乐观锁,就会覆盖上面的旭旭宝宝,成为马保国// 因为设置了乐观锁 没有覆盖 马保国没有被进行更新,因为where的version=? 不成立userMapper.updateById(user1);}
五、查询
1. 查询
/*** * selectById(id)测试根据id查询一个*/@Testvoid testSelectById(){User user = userMapper.selectById(1);System.out.println(user);}/*** selectBatchIds(集合) 测试查询批量*/@Testvoid testSelectBatchIds(){List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));System.out.println(users);}/*** 条件查询:*/@Testvoid testSelectByMap(){HashMap<String,Object> userHashMap = new HashMap<>();//自定义要查询的条件userHashMap.put("name","张五");List<User> users = userMapper.selectByMap(userHashMap);System.out.println(users);}
2. 分页查询
mp中也集成了分页插件
官网: https://baomidou.com/guide/interceptor.html#%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F-%E4%BB%A5%E5%88%86%E9%A1%B5%E6%8F%92%E4%BB%B6%E4%B8%BE%E4%BE%8B
1. 加入组件
导入分页插件,在自己创建的MyBatisPlus配置类里面 (3.4.0版本)
/*** 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));return interceptor;}@Beanpublic ConfigurationCustomizer configurationCustomizer() {return configuration -> configuration.setUseDeprecatedExecutor(false);}
(3.0.5版本) 分页插件组件
@Beanpublic PaginationInterceptor paginationInterceptor(){return new PaginationInterceptor();}
2. 测试分页查询
/*** 测试分页插件*/@Testvoid testPage(){//创建一个Page对象// 有参构造参数:第一个:当前页。第二个:一次查几个Page<User> page = new Page(1,3);//通过page的对象getxxx()方法可以获得其余属性System.out.println(page.getTotal());// 调用selectPage()方法 第一个是page 对象,第二个是wrapper,没有就写nullIPage<User> userIPage = userMapper.selectPage(page,null);System.out.println(userIPage);}
六、删除

@Testvoid testDeleteByMap(){Map<String,Object> map = new HashMap();// 根据条件删除,条件是姓名为张三的记录map.put("name","张三");userMapper.deleteByMap(map);}
1. 逻辑删除
- 物理删除: 直接从数据库中移除
- 逻辑删除: 没有从数据库中删除,而是通过一个字段来让他失效 deleted= 1
- 官网 : https://baomidou.com/guide/logic-delete.html#%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95
- 在数据表中增加deleted字段【默认值等于 0】
- 实体类中增加deleted属性 添加@TableLogic注解
- 添加组件【3.0.5需要 3.1.1以上版本不需要】 ```java /**
- 逻辑删除组件 */ @Bean public ISqlInjector sqlInjector(){ return new LogicSqlInjector(); } ```
配置逻辑删除在application.properties中
# 配置逻辑删除# 没有删除的值为0mybatis-plus.global-config.db-config.logic-delete-value=1# 删除的后的 属性为1mybatis-plus.global-config.db-config.logic-not-delete-value=0
测试删除
- 此时查询也查不到 deleted为1 的用户了
SELECT id,name,age,email,deleted,version,create_time,update_time FROM user WHERE id=? AND deleted=0 查询会自动在后面拼接一个 and deleted = 0.

七、条件构造器
用来写复杂的SQL
Wapper 是一个接口,底下有很多的实现类,查询用QueryWrapper类
具体方法看官网: https://baomidou.com/guide/wrapper.html#abstractwrapper
ge(“字段名”,值) 大于等于
/*** @date: 2021/1/5 23:56* @author: 易学习*/@SpringBootTestpublic class WrapperTest {@Autowiredprivate UserMapper userMapper;/*** 1. 查询name不为空,并且邮箱不为空的用户,并且年龄>=12* isNotNull("字段") 该字段不为空* ge("字段",值) 大于等于*/@Testvoid test1(){QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.isNotNull("name").isNotNull("email").ge("age",12);List<User> users = userMapper.selectList(wrapper);System.out.println(users);}/*** 2. 查询名字为 张五的记录*/@Testvoid test2(){QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.eq("name","张五");List user = userMapper.selectList(wrapper);System.out.println(user);}/*** 3. 查询年龄在18-20之间的用户数量*/@Testvoid test3(){QueryWrapper<User> wrapper = new QueryWrapper<>();wrapper.between("age",18,20);Integer integer = userMapper.selectCount(wrapper);System.out.println(integer);}/*** 4. 模糊查询,名字里面没有五的*/@Testvoid test4(){QueryWrapper wrapper = new QueryWrapper();wrapper.notLike("name","五");List list = userMapper.selectMaps(wrapper);System.out.println(list);}}
