文章目录
- SpringBoot整合MyBatis-Plus详解(二)
- MyBatis-Plus简介
- 条件构造器和常用接口⭐
- Wrapper介绍
- QueryWrapper(Mapper接口提供的)和QueryChainWrapper(Service接口提供的)
- 案例1:组装查询条件
- 案例2:组装排序条件
- 案例3:组装删除条件
- 案例4:条件的优先级
- 案例5:指定查询的字段(默认是查询全部字段)
- 案例6:实现子查询
- UpdateWrapper
- condition
- 未使用condition
- 使用condition来化简上面的代码(相当于if语句)
- LambdaQueryWrapper
- LambdaUpdateWrapper
- MyBatis Plus插件⭐
- 插件1:MyBatis Plus分页插件
- 编写MyBatis Plus插件配置类
- 测试分页
- xml自定义分页
- 在UserMapper中自定义接口方法
- UserMapper.xml中编写SQL
- 测试分页
- 插件2:MyBatis Plus乐观锁插件
- 模拟修改冲突
- 数据库中增加商品表和插入数据
- 新建实体类
- 新建mapper接口
- 测试
- Mybatis Plus实现乐观锁
- 给乐观锁标记属性加上@Version注解
- 在配置类中配置乐观锁插件
- 测试修改冲突
- MyBatis Plus代码生成器⭐
- 引入maven依赖
- 编写代码快速生成方法(生成t_user表的代码)
- MyBatisX插件⭐
- 在IDEA上安装MyBatisX插件
- MyBatisX插件功能1:Mapper和XML之间跳转
- MyBatisX插件功能2:代码生成
SpringBoot整合MyBatis-Plus详解(二)
项目所在仓库
MyBatis-Plus简介
简介
- MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为
简化开发、提高效率而生
MyBatis-Plus特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
- 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
MyBatis-Plus支持的数据库
- MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb
MyBatis-Plus的架构设计
条件构造器和常用接口⭐
Wrapper介绍
- Wrapper : 条件构造抽象类,最顶端父类
- AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
- QueryWrapper : 查询条件封装
- UpdateWrapper : Update 条件封装
- AbstractLambdaWrapper : 使用Lambda 语法
- LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
- LambdaUpdateWrapper : Lambda 更新封装Wrapper
- AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
- eq相等 、 ne不相等、 gt大于、 lt小于 、 ge大于等于 、 le 小于等于
QueryWrapper(Mapper接口提供的)和QueryChainWrapper(Service接口提供的)
案例1:组装查询条件
@Testvoid test01(){//查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)//方式1:QueryWrapper
// QueryWrapper<User> queryWrapper=new QueryWrapper<>();
//
// queryWrapper.like("username","a")
// .between("age",20,30)
// .isNotNull("email");
//
//
// List<User> users = userMapper.selectList(queryWrapper);
//
// users.forEach(System.out::println);//方式2:QueryChainWrapperQueryChainWrapper<User> chainWrapper = userService.query().like("username", "a").between("age", 20, 30).isNotNull("email");List<User> users = chainWrapper.list();users.forEach(System.out::println);}
案例2:组装排序条件
@Testvoid test02(){//按年龄降序查询用户,如果年龄相同则按id升序排列// SELECT uid,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,uid ASC//方式1:QueryWrapper
// QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// queryWrapper.orderByDesc("age")
// .orderByAsc("uid");
//
// List<User> users = userMapper.selectList(queryWrapper);
//
// users.forEach(System.out::println);//方式2:QueryChainWrapperQueryChainWrapper<User> userQueryChainWrapper = userService.query().orderByDesc("age").orderByAsc("uid");List<User> userList = userQueryChainWrapper.list();userList.forEach(System.out::println);}
案例3:组装删除条件
@Testvoid test03(){//删除email为空的用户//DELETE FROM t_user WHERE (email IS NULL)//QueryWrapperQueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.isNull("email");userMapper.delete(queryWrapper);}
案例4:条件的优先级
@Testvoid test04(){//将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改//UPDATE t_user SET age=?, email=? WHERE (username LIKE ? AND (age > ? OR email IS NULL))//lambda表达式内的逻辑优先运算QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.like("username","a").and(i -> i.gt("age", 20).or().isNull("email"));User user = new User();user.setAge(18);user.setEmail("666333@qq.com");userMapper.update(user,queryWrapper);}
案例5:指定查询的字段(默认是查询全部字段)
@Testvoid test05(){//查询用户信息的username和age字段//SELECT username,age FROM t_user WHERE is_deleted=0//方式1:QueryWrapper
// QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//
// queryWrapper
// .select("username","age"); //指定查询的字段(默认是查找全部字段)
//
// List<User> users = userMapper.selectList(queryWrapper);
//
// users.forEach(System.out::println);//方式2:QueryChainWrapperList<User> list = userService.query().select("username", "age") //指定查询的字段(默认是查找全部字段).list(); //list方法直接拿到结果集list.forEach(System.out::println);}
案例6:实现子查询
@Testvoid test06(){//查询id小于等于3的用户信息//SELECT uid,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (uid IN (select uid from t_user where uid <= 3))//方式1:QueryWrapper
// QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// queryWrapper.inSql("uid","select uid from t_user where uid <= 3");
//
// List<User> users = userMapper.selectList(queryWrapper);
//
// users.forEach(System.out::println);//方式2:QueryChainWrapperList<User> userList = userService.query().inSql("uid", "select uid from t_user where uid <= 3").list();userList.forEach(System.out::println);}
UpdateWrapper
@Test
void test07()
{//好处:当我们使用了UpdateWrapper时,不需要传入entity了,可以通过set方法修改值//UPDATE t_user SET username=?,age=? WHERE is_deleted=0 AND (uid = ?)UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();updateWrapper.set("username","新名字111").set("age",32).eq("uid",5L);userMapper.update(null,updateWrapper);}
condition
未使用condition
@Test
void test08()
{String username=null;Integer smallAge=15;Integer bigAge=30;//SELECT uid,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (age >= ? AND age <= ?)QueryWrapper<User> queryWrapper = new QueryWrapper<>();if (StringUtils.isNotBlank(username)) { //如果用户名不为空,则添加下面的条件queryWrapper.like("username",username);}if(smallAge != null){queryWrapper.ge("age",smallAge);}if(bigAge != null){queryWrapper.le("age",bigAge);}userMapper.selectList(queryWrapper);}
使用condition来化简上面的代码(相当于if语句)
@Test
void test09()
{String username=null;Integer smallAge=15;Integer bigAge=30;//SELECT uid,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (age >= ? AND age <= ?)QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.like(StringUtils.isNotBlank(username),"username",username).ge(smallAge!=null,"age",smallAge).le(bigAge!=null,"age",bigAge);userMapper.selectList(queryWrapper);}
LambdaQueryWrapper
- 好处就是:使用lambda表达式来代替表的字段,防止我们在开发中写表的字段错误。
@Test
void test10(){String username=null;Integer smallAge=15;Integer bigAge=30;LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.like(StringUtils.isNotBlank(username),User::getName,username);lambdaQueryWrapper.ge(smallAge!=null,User::getAge,smallAge);lambdaQueryWrapper.le(bigAge!=null,User::getAge,bigAge);userMapper.selectList(lambdaQueryWrapper);}
LambdaUpdateWrapper
- 好处就是:使用lambda表达式来代替表的字段,防止我们在开发中写表的字段错误。
@Test
void test11(){LambdaUpdateWrapper<User> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();lambdaUpdateWrapper.set(User::getName,"新名字222").set(User::getAge,55).eq(User::getUid,5L);userMapper.update(null,lambdaUpdateWrapper);}
MyBatis Plus插件⭐
插件1:MyBatis Plus分页插件
编写MyBatis Plus插件配置类
package com.boot.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;/*** @author youzhengjie 2022-08-17 20:44:12*///Mybatis-Plus配置类
@Configuration
public class MybatisPlusConfiguration {//mybatis-plus插件都要添加到这个mybatis-plus拦截器上面去。@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();//配置分页插件PaginationInnerInterceptor,指定数据库类型为MYSQLmybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return mybatisPlusInterceptor;}}
测试分页
@Testvoid testPage01(){//例如:total(总的数据记录数)=10,size(每一页的记录数大小)=5//那么就可以分成(10/5=2)页,分别是第1页和第2页,current就是当前页,因为只有2页,所以current只能写1或者2.//SELECT uid,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 LIMIT ?,?Page<User> userPage = new Page<>(2,5);//第一种写法:
// userMapper.selectPage(userPage,null);//第二种写法:userService.page(userPage,null);//获取分页数据List<User> list = userPage.getRecords();list.forEach(System.out::println);System.out.println("当前页:"+userPage.getCurrent());System.out.println("每页显示的条数:"+userPage.getSize());System.out.println("总记录数:"+userPage.getTotal());System.out.println("总页数:"+userPage.getPages());System.out.println("是否有上一页:"+userPage.hasPrevious());System.out.println("是否有下一页:"+userPage.hasNext());}
xml自定义分页
在UserMapper中自定义接口方法
package com.boot.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.boot.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;/*** @author youzhengjie 2022-08-15 18:42:11*/@Mapper
@Repository
// BaseMapper的泛型就是我们需要操作的实体类User
public interface UserMapper extends BaseMapper<User> {/*** 根据年龄查询用户列表,分页显示* @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页(注意page必须放在第一位)* @param age 年龄*/Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);}
UserMapper.xml中编写SQL
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.boot.dao.UserMapper"><select id="selectPageVo" resultType="com.boot.entity.User">select * from t_user where age=#{age}</select></mapper>
测试分页
@Test
void testPage02(){Page<User> userPage = new Page<>(1,5);//调用刚刚我们自定义的方法//select * from t_user where age=? LIMIT ?userMapper.selectPageVo(userPage,20);//获取分页数据List<User> list = userPage.getRecords();list.forEach(System.out::println);System.out.println("当前页:"+userPage.getCurrent());System.out.println("每页显示的条数:"+userPage.getSize());System.out.println("总记录数:"+userPage.getTotal());System.out.println("总页数:"+userPage.getPages());System.out.println("是否有上一页:"+userPage.hasPrevious());System.out.println("是否有下一页:"+userPage.hasNext());}
插件2:MyBatis Plus乐观锁插件
模拟修改冲突
数据库中增加商品表和插入数据
CREATE TABLE t_product
(id BIGINT(20) NOT NULL COMMENT '主键ID',name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',number INT(11) DEFAULT 1 COMMENT '商品数量',version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',PRIMARY KEY (id)
);INSERT INTO t_product (id, name, number) VALUES (1, 'iphone13 pro', 0);
新建实体类
package com.boot.entity;import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;import java.io.Serializable;/*** @author youzhengjie 2022-08-17 21:56:17*/@TableName("t_product")
//lombok注解
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Product implements Serializable {@TableId("id")private Long id;@TableField("name")private String name;@TableField("number")private int number;private int version;}
新建mapper接口
package com.boot.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.boot.entity.Product;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;/*** @author youzhengjie 2022-08-17 22:12:21*/
@Mapper
@Repository
public interface ProductMapper extends BaseMapper<Product> {}
测试
- 测试前先查看商品库存:
- 开始执行测试代码:
@Testvoid test03(){//A用户获取到的Product p1 = productMapper.selectById(1L);//B用户获取到的Product p2 = productMapper.selectById(1L);//A用户修改库存p1.setNumber(p1.getNumber()+20);productMapper.update(p1,null);//B用户修改库存p2.setNumber(p2.getNumber()+30);productMapper.update(p2,null);}
- 查看输出日志:
- 再次查看数据库:
Mybatis Plus实现乐观锁
给乐观锁标记属性加上@Version注解
在配置类中配置乐观锁插件
package com.boot.config;import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @author youzhengjie 2022-08-17 20:44:12*///Mybatis-Plus配置类
@Configuration
public class MybatisPlusConfiguration {//mybatis-plus插件都要添加到这个mybatis-plus拦截器上面去。@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();//配置分页插件PaginationInnerInterceptor,指定数据库类型为MYSQLmybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//配置乐观锁插件mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return mybatisPlusInterceptor;}}
测试修改冲突
@Testvoid test03(){//A用户获取到的Product p1 = productMapper.selectById(1L);//B用户获取到的Product p2 = productMapper.selectById(1L);//A用户修改库存p1.setNumber(p1.getNumber()+20);productMapper.update(p1,null);//B用户修改库存p2.setNumber(p2.getNumber()+30);productMapper.update(p2,null);}
- 查看输出日志:
- 查看数据库:
MyBatis Plus代码生成器⭐
引入maven依赖
<!-- mybatis-plus代码生成器--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.2</version></dependency>
<!-- freemarker依赖 (mybatis-plus指定freemarker引擎时一定要引入这个依赖,不然会报错) --><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.31</version></dependency>
编写代码快速生成方法(生成t_user表的代码)
- 查看我们的t_user表:
- 开始执行代码生成程序(通过t_user表生成代码):
package com.boot.generate;import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;import java.util.Collections;/*** @author youzhengjie 2022-08-20 00:28:34* mybatis-plus代码生成器程序*/
public class FastGeneratorTest {public static void main(String[] args) {FastAutoGenerator//mysql的数据源配置.create("jdbc:mysql://localhost:3306/mybatis_plus-db?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false", "root", "18420163207").globalConfig(builder -> {builder.author("youzhengjie") // 设置作者.enableSwagger() // 开启 swagger 模式.fileOverride() // 覆盖已生成文件.outputDir("D://mybatis-plus-generator"); // 指定输出目录}).packageConfig(builder -> {builder.parent("com.boot") // 设置父包名.moduleName("gen") // 设置父包模块名.pathInfo(Collections.singletonMap(OutputFile.xml, "D://mybatis-plus-generator")); // 设置mapperXml生成路径}).strategyConfig(builder -> {builder//设置需要生成的表名(该表必须要在我们指定的mysql中存在,mybatis-plus才会根据这个表来生成代码).addInclude("t_user")// 设置过滤表前缀.addTablePrefix("t_", "c_");}).templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板.execute(); //执行}}
- 输出日志:
- 查看刚刚生成的代码结构:
MyBatisX插件⭐
在IDEA上安装MyBatisX插件
MyBatisX插件功能1:Mapper和XML之间跳转
- 随便进入一个Mapper接口(然后点击跳转):
- 点击XML文件的图标也可以实现跳转。
MyBatisX插件功能2:代码生成
- 1:随便进入Mapper接口,输出你想生成的方法:
- 2:查看生成的方法和代码: