MyBatis-Plus
MyBatis-Plus
简介
为简化开发而生!是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变。
润物无声:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑。
效率至上:只需简单配置,即可快速进行 CRUD 操作,从而节省大量时间。
丰富功能:热加载、代码生成、分页、性能分析等功能一应俱全。
愿景:是成为 MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。

特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作, BaseMapper
- 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求, 以后简单的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数据库,创建user表,插入测试数据:
DROP TABLE IF EXISTS user;
CREATE TABLE user (
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
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');
- 创建mybatis_plus的SpringBoot工程,在pom文件中导入mybatis-plus的依赖包。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
导入其他数据源的包,web启动包,lombok。
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
- 修改application.yml,配置数据源。
spring:
datasource:
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
type: com.alibaba.druid.pool.DruidDataSource
- 编写entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
- 编写mapper接口
//继承了BaseMapper,所有简单的CRUD操作都已经编写完成了
public interface UserMapper extends BaseMapper<User> {
}
- 编写一个MyBatisPlusConfig:
@MapperScan("com.yinrz.mapper") // 扫描我们的 mapper 文件夹
@Configuration // 配置类
public class MyBatisPlusConfig {
}
- 写一个测试类进行测试:
@SpringBootTest
public class MybatisPlusApplicationTests{
@Resource
UserMapper userMapper;
@Test
public void test01(){
List<User> users = userMapper.selectList(null);
for (User user:users){
System.out.println(user);
}
}
}
运行结果:

insert插入操作
@Test
public void testInsert() {
User user = new User();
user.setName("yinrz");
user.setAge(18);
user.setEmail("1760266861@qq.com");
int result = userMapper.insert(user);
System.out.println(result); // 受影响的行数
System.out.println(user); // 发现id会自动回填
}

**我们发现id会自动回填 **
数据库插入的id的默认值为: ID_WORKER 全局的唯一id(雪花算法)
分布式系统唯一ID生成方法总汇:https://www.cnblogs.com/haoxinyue/p/5208136.html
我们可以修改插入的id的生成策略
- 在实体类的id字段上添加注解@TableId,指定type=IdType.xxx,例如
@TableId(type = IdType.AUTO),数据库要开启主键自增!!!
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
}
- id生成策略:
public enum IdType {
AUTO(0), // 数据库id自增(数据库要开启主键自增!!!)
NONE(1), // 未设置主键
INPUT(2), // 手动输入
ID_WORKER(3), // 全局唯一id(默认)
UUID(4), // 全局唯一id,uuid
ID_WORKER_STR(5); //ID_WORKER的字符串表示法
}
- 重启测试,发现插入的id比上一条id多了1:

update更新操作
@Test
public void testUpdate(){
User user = new User();
user.setId(1255894026599604227L);
user.setName("yinrz2");
user.setAge(21);
int i = userMapper.updateById(user);
System.out.println(i);
}

**注意:updateById传入的参数是一个对象!通过条件自动拼接动态sql 。要在主键上加入@TableId注解,或者主键名就叫id!!! **
delete删除操作
- 通过单个id删除
@Test
public void testDeleteById() {
userMapper.deleteById(1255894026599604227L);
}

- 通过多个id批量删除
@Test
public void testDeleteBatchId() {
userMapper.deleteBatchIds(Arrays.asList(1L, 2L));
}

- 通过map指定满足的条件,然后删除
@Test
public void testDeleteMap() {
HashMap<String, Object> map = new HashMap<>();
map.put("name", "Tom");
userMapper.deleteByMap(map);
}

逻辑删除
物理删除 :从数据库中直接移除
逻辑删除 :再数据库中没有被移除,而是通过一个变量来让他失效! deleted = 0 变成 deleted = 1
管理员可以查看被删除的记录!防止数据的丢失,类似于回收站!
- 在数据库表中添加一个字段:deleted,int类型,默认值为0(0:未删除,1:删除)。
- 在实体类中增加对应字段,并添加@TableLogic注解
@TableLogic
private Integer deleted;
- 在MyBatisPlusConfig文件里添加Bean:
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
- 测试删除,发现走的是update方法而不是delete方法了:
@Test
public void testDeleteById() {
userMapper.deleteById(4L);
}

select查询操作
- 通过单个id查询
@Test
public void testSelectById() {
User user = userMapper.selectById(4L);
System.out.println(user);
}

- 通过多个id查询
@Test
public void testSelectByBatchId() {
List<User> users = userMapper.selectBatchIds(Arrays.asList(4,5));
for (User user : users) {
System.out.println(user);
}
}

- 通过map指定满足的条件,然后查询
@Test
public void testSelectByBatchIds() {
HashMap<String, Object> map = new HashMap<>(); // 自定义要查询
map.put("name", "Sandy");
map.put("age", 21);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}

分页查询
- 在MyBatisPlusConfig中添加Bean:
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
- 测试,使用Page对象,mapper使用selectPage方法,getRecords方法返回查询结果:
@Test
public void testPage() {
// 参数一:当前页 // 参数二:页面大小
Page<User> page = new Page<>(1, 3);
userMapper.selectPage(page, null);
List<User> users = page.getRecords();
for (User user:users){
System.out.println(user);
}
System.out.println(page.getTotal());
}

乐观锁
-
乐观锁假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。
-
当我们要对一个数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行加锁以防止并发。这种借助数据库锁机制,在修改数据之前先锁定,再修改的方式被称之为悲观锁。
-
乐观锁在修改时会带上version等于当前version值的条件,修改成功后会给version的值加1。加了乐观锁后,在并发环境下,多个人同时修改同一条数据时,只会有1个人修改成功,其他人修改失败。
如何实现:
- 在数据库表中添加version字段,int类型,默认值为1。
- entity类添加对应字段,并在该字段上添加注解:
@Version
private Integer version;
- 在MyBatisPlusConfig中,注册bean:
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
- 测试,发现线程2先修改然后将version的值+1,使得线程1修改不成功因为找不到对应version的值:
@Test
public void testOptimisticLocker() {
// 线程1
User user = userMapper.selectById(8L);
user.setName("yinrz3");
// 线程2
User user2 = userMapper.selectById(8L);
user2.setName("yinrz4");
//线程2先更新
userMapper.updateById(user2);
//线程1再更新
userMapper.updateById(user); // 如果没有乐观锁就会成功更新
}


自动填充
创建时间(gmt_create)、修改时间(gmt_modified)几乎所有的表都要配置上,而且需要自动填充,我们不希望手动更新!
- 在数据库表中添加字段gmt_create与gmt_modified,datetime类型,无默认值。
- 在实体类中添加对应字段,并添加注解:
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
- 编写一个handler来处理如何填充:
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
// 插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
// 更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
- 插入一条数据测试,发现日期自动回填了:

性能分析插件
我们在平时的开发中,会遇到一些慢sql,性能分析插件会帮助我们输出每条 SQL 语句及其执行时间 。
- 在MyBatisPlusConfig中配置Bean:
@Bean
@Profile({"dev","test"})// 设置 dev test 环境开启,保证我们的效率
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(500); //ms 设置sql执行的最大时间,如果超过了则不执行
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
- 测试:

如果超时:

条件查询器Wrapper
allEq,eq,ne(不等于),gt(大于),ge(大于等于),lt,le,between,notBetween,like,notLike,likeLeft(百分号在左边),likeRight(百分号在右边),isNull,isNotNull,in,notIn,inSql(子查询),notInSql,groupBy,orderBy(默认,升序,小到大),orderByAsc(升序),orderByDesc(降序),having,or,and,nested,apply,last,exists,notExists。
@Test
public void testWrapper() {
QueryWrapper<User> wrapper=new QueryWrapper<>();
wrapper.eq("name","yinrz") //name = yinrz
.between("age",18,22) //age BETWEEN 18 AND 22
.like("name","a") // email LIKE %a%
.notLike("email","b") //email NOT LIKE %b%
.likeRight("email","c") //email LIKE c%
.likeLeft("email","d") //email LIKE %d
.inSql("id","select id from user where id < 10") //id IN (select id from user where id < 10)
.orderByDesc("id"); //ORDER BY id DESC
List<User> users = userMapper.selectList(wrapper);
for (User user :users){
System.out.println(user);
}
}

代码自动生成器
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、 Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
public static void main(String[] args) {
//代码自动生成器
AutoGenerator autoGenerator = new AutoGenerator();
//1.全局配置
GlobalConfig gc=new GlobalConfig();
gc.setOutputDir(System.getProperty("user.dir")+"/src/main/java"); //文件输出路径
gc.setAuthor("yinrz"); //作者名
gc.setOpen(false); //是否打开文件
gc.setDateType(DateType.ONLY_DATE); //设置创建的日期
gc.setFileOverride(false); //是否覆盖
gc.setServiceName("%sService"); //去Service的I前缀
gc.setIdType(IdType.ID_WORKER); //主键填充策略
gc.setSwagger2(true); //是否加入Swagger注解
autoGenerator.setGlobalConfig(gc);
//2.配置数据源
DataSourceConfig dsc=new DataSourceConfig();
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8");
dsc.setUsername("root");
dsc.setPassword("root");
dsc.setDbType(DbType.MYSQL);
autoGenerator.setDataSource(dsc);
//3.配置包
PackageConfig pc = new PackageConfig();
//pc.setModuleName("xxx"); //模块名
pc.setParent("com.yinrz"); //父包名
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setService("service");
pc.setController("controller");
autoGenerator.setPackageInfo(pc);
//4.配置策略
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("user"); //数据库的表名
strategy.setNaming(NamingStrategy.underline_to_camel); //数据库表名驼峰命名
strategy.setColumnNaming(NamingStrategy.underline_to_camel); //数据库字段驼峰命名
strategy.setEntityLombokModel(true); //加lombok注解
strategy.setLogicDeleteFieldName("deleted"); //逻辑删除的字段名
//配置自动填充
TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
ArrayList<TableFill> tableFills = new ArrayList<>();
tableFills.add(gmtCreate);
tableFills.add(gmtModified);
strategy.setTableFillList(tableFills);
strategy.setVersionFieldName("version"); //乐观锁的字段名
strategy.setRestControllerStyle(true); //RestController注解
strategy.setControllerMappingHyphenStyle(true); // 类似于localhost:8080/hello_id_2
autoGenerator.setStrategy(strategy);
//执行代码生成
autoGenerator.execute();
}


浙公网安备 33010602011771号