MyBatis-Plus是MyBatis的增强版,简称(MP),在MyBatis的基础上只做增强,不做改变,为简化而生,提高开发效率。
一、MyBatis-Plus简介 官网地址
GitHub
Gitee 文档发布地址
1.框架结构
2.特性
无侵入 :只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小 :启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
3.支持数据库
任何能使用 MyBatis
进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下,如果不在下列表查看分页部分教程 PR 您的支持。
MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb
达梦数据库,虚谷数据库,人大金仓数据库,南大通用(华库)数据库,南大通用数据库,神通数据库,瀚高数据库
二、快速开始 1.数据准备 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 CREATE DATABASE `mybatis_plus` ; use `mybatis_plus`; CREATE TABLE `user ` ( `id` bigint (20 ) NOT NULL COMMENT '主键ID' , `name` varchar (30 ) DEFAULT NULL COMMENT '姓名' , `age` int (11 ) DEFAULT NULL COMMENT '年龄' , `email` varchar (50 ) DEFAULT NULL COMMENT '邮箱' , PRIMARY KEY (`id`) ) ENGINE= InnoDB DEFAULT CHARSET= utf8; INSERT INTO user (id, name, age, email) VALUES (1 , 'Jone' , 18 , 'test1@baomidou.com' ), (2 , 'Jack' , 20 , 'test2@baomidou.com' ), (3 , 'Tom' , 28 , 'test3@baomidou.com' ), (4 , 'Sandy' , 21 , 'test4@baomidou.com' ), (5 , 'Billie' , 24 , 'test5@baomidou.com' );
2.工程环境准备
创建一个SpringBoot项目
导入pom依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.5.1</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <optional > true</optional > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <scope > runtime</scope > </dependency > </dependencies >
配置application.yml,添加数据源
1 2 3 4 5 6 7 8 9 10 spring: datasource: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false username: root password: 123456
注意:
MySQL5.7版本的url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false MySQL8.0版本的url: jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
编写实体类
1 2 3 4 5 6 7 8 9 10 @Data public class User { private Long id; private String name; private Integer age; private String email; }
编写mapper
1 2 3 @Repository public interface UserMapper extends BaseMapper <User > {}
@Repository 为了让idea不显示错误
BaseMapper是MyBatis-Plus提供的模板mapper,包含了基本的CRUD操作,泛型为要操作的实体类型
添加扫描
1 2 3 4 5 6 7 8 @SpringBootApplication @MapperScan("com.lz.mybatisplus.mapper") public class MybatisPlusApplication { public static void main (String[] args) { SpringApplication.run(MybatisPlusApplication.class, args); } }
测试
1 2 3 4 5 6 7 8 9 10 11 @SpringBootTest public class MybatisPlusTest { @Autowired UserMapper userMapper; @Test public void testQueryList () { List<User> users = userMapper.selectList(null ); users.stream().forEach(System.out::println); } }
三、基本CRUD方法 1.查询 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 T selectById (Serializable id) ;List<T> selectBatchIds (@Param("coll") Collection<? extends Serializable> idList) ;List<T> selectByMap (@Param("cm") Map<String, Object> columnMap) ;default T selectOne (@Param("ew") Wrapper<T> queryWrapper) { List<T> ts = this .selectList(queryWrapper); if (CollectionUtils.isNotEmpty(ts)) { if (ts.size() != 1 ) { throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records" , new Object[0 ]); } else { return ts.get(0 ); } } else { return null ; } } default boolean exists (Wrapper<T> queryWrapper) { Long count = this .selectCount(queryWrapper); return null != count && count > 0L ; } Long selectCount (@Param("ew") Wrapper<T> queryWrapper) ;List<T> selectList (@Param("ew") Wrapper<T> queryWrapper) ;List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper); List<Object> selectObjs (@Param("ew") Wrapper<T> queryWrapper) ;<P extends IPage<T>> P selectPage (P page, @Param("ew") Wrapper<T> queryWrapper) ; <P extends IPage<Map<String, Object>>> P selectMapsPage (P page, @Param("ew") Wrapper<T> queryWrapper) ;
2.增加
3.删除 1 2 3 4 5 6 7 8 9 int deleteById (Serializable id) ;int deleteById (T entity) ;int deleteByMap (@Param("cm") Map<String, Object> columnMap) ;int delete (@Param("ew") Wrapper<T> queryWrapper) ;int deleteBatchIds (@Param("coll") Collection<?> idList) ;
4.修改 1 2 3 int updateById (@Param("et") T entity) ;int update (@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper) ;
四、通用service
通用 Service CRUD 封装 IService 接口,进一步封装 CRUD 采用 get 查询单行
remove 删除
list 查询集合
page 分页
前缀命名方式区分 Mapper
层避免混淆,
泛型 T
为任意实体对象
建议如果存在自定义通用 Service 方法的可能,请创建自己的 IBaseService
继承 Mybatis-Plus
提供的基类
对象 Wrapper
为 条件构造器
1.save 1 2 3 4 5 6 boolean save (T entity) ;boolean saveBatch (Collection<T> entityList) ;boolean saveBatch (Collection<T> entityList, int batchSize) ;
2.saveOrUpdate 1 2 3 4 5 6 7 8 boolean saveOrUpdate (T entity) ;boolean saveOrUpdate (T entity, Wrapper<T> updateWrapper) ;boolean saveOrUpdateBatch (Collection<T> entityList) ;boolean saveOrUpdateBatch (Collection<T> entityList, int batchSize) ;
3.remove 1 2 3 4 5 6 7 8 boolean remove (Wrapper<T> queryWrapper) ;boolean removeById (Serializable id) ;boolean removeByMap (Map<String, Object> columnMap) ;boolean removeByIds (Collection<? extends Serializable> idList) ;
4.update 1 2 3 4 5 6 7 8 9 10 boolean update (Wrapper<T> updateWrapper) ;boolean update (T updateEntity, Wrapper<T> whereWrapper) ;boolean updateById (T entity) ;boolean updateBatchById (Collection<T> entityList) ;boolean updateBatchById (Collection<T> entityList, int batchSize) ;
五、常用注解 1.@TableName
在实体类类型上添加@TableName(“t_user”),标识实体类对应的表
2.@TableId
经过以上的测试,MyBatis-Plus在实现CRUD时,会默认将id作为主键列,并在插入数据时,默认基于雪花算法的策略生成id
在实体类中uid属性上通过@TableId将其标识为主键
常用的主键策略
值
描述
IdType.ASSIGN_ID(默 认)
基于雪花算法的策略生成数据id,与数据库id是否设置自增无关
IdType.AUTO
使用数据库的自增策略,注意,该类型请确保数据库设置了id自增, 否则无效
3.@TableField
MyBatis-Plus会自动将下划线命名风格转化为驼峰命名风格
如实体类属性name,表中字段username 此时需要在实体类属性上使用@TableField(“username”)设置属性所对应的字段名
4.@TableLogic 物理删除:从数据库表中真实删除数据
逻辑删除:通过数据库表字段值区分数据状态为已删除和未删除
通过@TableLogic注解实现逻辑删除
表添加字段用于存储删除状态,且设置默认值0
实体类添加该对应字段,并在该字段上添加注解@TableLogic,即实现逻辑删除
1 2 3 4 5 6 7 8 9 10 11 12 13 @Data public class User { private Long id; private String name; private Integer age; private String email; @TableLogic private Integer isDeleted; }
六、条件构造器和常用接口 1.Wrapper介绍
Wrapper : 条件构造抽象类,最顶端父类
AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
QueryWrapper : 查询条件封装
UpdateWrapper : Update 条件封装
AbstractLambdaWrapper : 使用Lambda 语法
LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
LambdaUpdateWrapper : Lambda 更新封装Wrapper
2.QueryWrapper (一)组装查询条件 1 2 3 4 5 6 7 8 9 10 11 12 @Test public void test01 () { is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL) QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.like("username" , "a" ) .between("age" , 20 , 30 ) .isNotNull("email" ); List<User> list = userMapper.selectList(queryWrapper); list.forEach(System.out::println); }
(二)组装删除条件 1 2 3 4 5 6 7 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.isNull("email" ); int result = userMapper.delete(queryWrapper);System.out.println("受影响的行数:" + result);
(三)条件优先级 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Test public void test04 () { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); email IS NULL) queryWrapper .like("username" , "a" ) .gt("age" , 20 ) .or() .isNull("email" ); User user = new User(); user.setAge(18 ); user.setEmail("user@atguigu.com" ); int result = userMapper.update(user, queryWrapper); System.out.println("受影响的行数:" + result); } @Test public void test04 () { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); email IS NULL)) queryWrapper.like("username" , "a" ) .and(i -> i.gt("age" , 20 ).or().isNull("email" )); User user = new User(); user.setAge(18 ); user.setEmail("user@atguigu.com" ); int result = userMapper.update(user, queryWrapper); System.out.println("受影响的行数:" + result); }
(四)组装Select子句 1 2 3 4 5 6 7 8 9 10 11 @Test public void test05 () { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.select("username" , "age" ); 为null List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper); maps.forEach(System.out::println); }
(五)组装排序条件 1 2 3 4 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper .orderByDesc("age" ) .orderByAsc("id" );
(六)实现子查询 1 2 3 4 5 6 7 8 9 10 @Test public void test06 () { (select id from t_user where id <= 3 )) QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.inSql("id" , "select id from t_user where id <= 3" ); List<User> list = userMapper.selectList(queryWrapper); list.forEach(System.out::println); }
(七)常用的条件参数
查询方式
说明
eq
等于 =
ne
不等于<>
gt
大于>
ge
大于等于>=
lt
小于<
le
小于等于
like
模糊查询LIKE ‘%值%’
likeLeft
Like ‘%值’
likeRigth
Like ‘值%’
notLike
模糊查询 NOT LIKE
in
in(v0,v1……)
notin
not in (v0,v1……)
inSql
in (SQL语句)
notInSql
not in (SQL语句)
isNull
NULL值查询
isNotNull
not Null值查询
groupBy
group by
orderBy
排序
orderByAsc
orderByAsc
exists
EXISTS 条件语句
between
between
last
无视优化规则直接拼接到 sql 的最后 !!!只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用
3.UpdateWrapper 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Test public void test07 () { UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(); updateWrapper .set("age" , 18 ) .set("email" , "user@atguigu.com" ) .like("username" , "a" ) .and(i -> i.gt("age" , 20 ).or().isNull("email" )); (age > ? OR email IS NULL)) email IS NULL)) int result = userMapper.update(null , updateWrapper); System.out.println(result); }
4.Condition
先判断用户是否选择了这些条件,若选择则需要组装该条件,若没有选择则一定不能组装,以免影响SQL执行的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void test08UseCondition () { String username = null ; Integer ageBegin = 10 ; Integer ageEnd = 24 ; QueryWrapper<User> queryWrapper = new QueryWrapper<>(); 构成 queryWrapper .like(StringUtils.isNotBlank(username), "username" , "a" ) .ge(ageBegin != null , "age" , ageBegin) .le(ageEnd != null , "age" , ageEnd); ? AND age <= ?) List<User> users = userMapper.selectList(queryWrapper); users.forEach(System.out::println); }
5.LambdaQueryWrapper 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void test09 () { String username = "a" ; Integer ageBegin = 10 ; Integer ageEnd = 24 ; LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper .like(StringUtils.isNotBlank(username), User::getName, username) .ge(ageBegin != null , User::getAge, ageBegin) .le(ageEnd != null , User::getAge, ageEnd); List<User> users = userMapper.selectList(queryWrapper); users.forEach(System.out::println); }
6.LambdaUpdateWrapper 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void test10 () { LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper .set(User::getAge, 18 ) .set(User::getEmail, "user@atguigu.com" ) .like(User::getName, "a" ) .and(i -> i.lt(User::getAge, 24 ).or().isNull(User::getEmail)); 表达式内的逻辑优先运算 User user = new User(); int result = userMapper.update(user, updateWrapper); System.out.println("受影响的行数:" + result); }
七、插件 一、分页插件
Mybatis-Plus自带分页插件,只需简单配置即可实现
添加配置类
1 2 3 4 5 6 7 8 9 10 11 @Configuration @MapperScan("com.atguigu.mybatisplus.mapper") public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void testPage () { Page<User> page = new Page<>(1 , 5 ); userMapper.selectPage(page, null ); List<User> list = page.getRecords(); list.forEach(System.out::println); System.out.println("当前页:" +page.getCurrent()); System.out.println("每页显示的条数:" +page.getSize()); System.out.println("总记录数:" +page.getTotal()); System.out.println("总页数:" +page.getPages()); System.out.println("是否有上一页:" +page.hasPrevious()); System.out.println("是否有下一页:" +page.hasNext()); }
自定义Sql实现分页
UserMapper接口定义接口方法
1 2 3 4 5 6 7 8 9 10 / ** * 根据年龄查询用户列表,分页显示 * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位 * @param age 年龄 * @return */ I Page<User> selectPageVo (@Param("page") Page<User> page, @Param("age") Integer age) ;
UserMapper.xml添加SQL
1 2 3 4 5 6 7 <sql id ="BaseColumns" > id,username,age,email</sql > <select id ="selectPageVo" resultType ="User" > SELECT <include refid ="BaseColumns" > </include > FROM t_user WHERE age > # {age} </select >
二、乐观锁 悲观锁:在进行写操作是,会将数据列锁起来,等当前连接操作完下一个连接才可以继续操作
乐观锁:不会锁表,会在执行时检查数据是否被修改过,如果被修改过则获重新获取数据再进行操作
乐观锁实现流程
数据库中添加version字段
取出记录时,获取当前version
1 SELECT id,`name`,price,`version` FROM product WHERE id= 1
更新时,version + 1,如果where语句中的version版本不对,则更新失败
1 UPDATE product SET price= price+ 50 , `version`= `version` + 1 WHERE id= 1 AND `version`= 1
MyBatis-Plus实现乐观锁
修改实体类,对应版本号字段添加@Version注解
1 2 3 4 5 6 7 8 9 10 11 package com.atguigu.mybatisplus.entity;import com.baomidou.mybatisplus.annotation.Version;import lombok.Data;@Data public class Product { private Long id; private String name; private Integer price; @Version private Integer version; }
添加乐观锁插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Configuration @MapperScan("com.atguigu.mybatisplus.mapper") public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor () { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } }
八、代码生成器 1.导入依赖 1 2 3 4 5 6 7 8 9 10 <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-generator</artifactId > <version > 3.5.1</version > </dependency > <dependency > <groupId > org.freemarker</groupId > <artifactId > freemarker</artifactId > <version > 2.3.31</version > </dependency >
2.快速生成 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class FastAutoGeneratorTest { public static void main (String[] args) { FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/mybatis_plus? characterEncoding=utf-8&userSSL=false" , "root" , "123456" ) .globalConfig(builder -> { builder.author("atguigu" ) .fileOverride() .outputDir("D://mybatis_plus" ); }) .packageConfig(builder -> { builder.parent("com.atguigu" ) .moduleName("mybatisplus" ) .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus" )); }) .strategyConfig(builder -> { builder.addInclude("t_user" ) .addTablePrefix("t_" , "c_" ); }) .templateEngine(new FreemarkerTemplateEngine()) 引擎模板,默认的是Velocity引擎模板 .execute(); } }
九、多数据源
导入依赖
1 2 3 4 5 <dependency > <groupId > com.baomidou</groupId > <artifactId > dynamic-datasource-spring-boot-starter</artifactId > <version > 3.5.0</version > </dependency >
配置application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 spring: datasource: dynamic: primary: master strict: false datasource: master: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://101.132.140.20:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false username: root password: lz1024cx slave_1: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://101.132.140.20:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false username: root password: lz1024cx
使用数据源
1 2 3 4 5 @DS("master") @Service public class UserServiceImpl extends ServiceImpl <UserMapper , User > implements UserService {}
十、MyBatisX插件 用法