Mybatis-Plus(简称MP)是一个Mybatis的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发、提升效率而生。就像魂斗罗的1P、2P,基友搭配,效率翻倍。
官网:https://mp.baomidou.com/
一、特性
-
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
-
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
-
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
-
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
-
支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer2005、SQLServer 等多种数据库
-
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
-
支持 XML 热加载:Mapper 对应的 XML 支持热加载,对于简单的 CRUD 操作,甚至可以无 XML 启动
-
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
-
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
-
支持关键词自动转义:支持数据库关键词(order、key......)自动转义,还可自定义关键词
-
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
-
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
-
内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
-
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
-
内置 Sql 注入剥离器:支持 Sql 注入剥离,有效预防 Sql 注入攻击
二、快速开始
-
先准备一张数据表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));
2. 对应的Data脚本
DELETE FROM userINSERT 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');
-
初始化工程
使用Spring Initializer快速初始化一个Spring Boot工程
pom.xml
<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> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!--lombok用来简化实体类--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> <!--mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> </dependencies>
配置文件application.properties,配置数据库相关配置
#mysql数据库连接(mysql5.x版本)spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghaispring.datasource.username=rootspring.datasource.password=1234#mybatisplus日志mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
-
编写代码
在SpringBoot启动类上添加@MapperScan主键,扫码mapper文件夹(也可以放在MybatisPlus配置文件)
@SpringBootApplication@MapperScan("com.guli.mybatisplusdemo.mapper")public class MybatisPlusDemoApplication { public static void main(String[] args) { SpringApplication.run(MybatisPlusDemoApplication.class,args); }}
创建实体User.java
package com.guli.mybatisplusdemo.entity;import lombok.Data;/** * @author Guli * @date 2020/4/2 13:52 */@Datapublic class User { private Long id; private String name; private Integer age; private String email;}
因为是demo,省掉controller和service,直接写mapper:UserMapper.java
public interface UserMapper extends BaseMapper<User> {} //BaseMapper接口封装了很多方法可以使用
-
编写测试类, 简单的crud操作
1)查询操作
@SpringBootTestpublic class MpDemoTest { @Autowired private UserMapper userMapper;//此处如果报红可以不管,强迫症可以在UserMapper接口上加@Repository注解 //查询user表所有的数据 @Test public void findAll(){ List<User> users = userMapper.selectList(null); users.forEach(System.out::println); }}
运行查看结果...
2)插入操作
@Testpublic void testInsert(){ User user = new User(); user.setName("张三"); user.setAge(11); user.setEmail("zhangsan@qq.com"); int insert = userMapper.insert(user); System.out.println(insert); //返回受影响的行数}
运行结果...
mybatis-plus默认的主键策略是:ID_WORKER (全局唯一ID)
3) 更新操作
@Testpublic void testUpdate() User user = new User(); user.setId(1L); user.setAge(100); int i = userMapper.updateById(user); System.out.println("i = " + i);}
更新操作前:
更新操作之后:
三、Mybatis-Plus的自动填充
一些数据每次都使用相同的方式填充,我们例如创建时间、更新时间等,都可以使用Mybatis-Plus的自动填充功能实现。
-
在User表里添加字段create_time、update_time类型为datetime
-
在实体类中加入两个字段,并添加注解
@Datapublic class User { @TableId(type= IdType.ID_WORKER) //mp默认的id策略,生成19位,Long类型id使用这个// @TableId(type = IdType.ID_WORKER_STR) //mp自带的策略,生成19位值,如果id是string类型使用这个 private Long id; private String name; private Integer age; private String email; @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;}
3. 实现对元对象处理器接口java
@Componentpublic 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); }}
然后执行更新之前的添加和更新操作就可以了..
四、乐观锁的使用
乐观锁的适用场景:当更新操作某一条数据的时候,这条数据不被其他线程更新,实现线程安全的数据更新。
实现方式:
-
取出记录时,获取当前version
-
更新时带上这个version
-
执行更新时,set version = newVersion where version = oldVersion
-
如果version不匹配,更新操作不能执行成功
(1)在User表中添加version字段,数据类型int 长度11(设置默认值1)
(2)在实体类中添加version字段并添加@Version注解
@Version@TableField(fill = FieldFill.INSERT)private Integer version;
(3)在MybatisPlusConfig中注册Bean
注意这个地方可以把@MapperScan注解从启动类移到这里,这里有启动类上就可以删除
@EnableTransactionManagement@Configuration@MapperScan("com.guli.mybatisplusdemo.mapper")public class MybatisPlusConfig { /** * 乐观锁插件 */ @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor(){ return new OptimisticLockerInterceptor(); }}
(4)测试乐观锁修改
@Testpublic void testOptimisticLock(){ //第一步:查询出数据 User user = userMapper.selectById(1L); //第二步:修改数据 user.setName("李四"); user.setEmail("lisi@qq.com"); //第三步执行更新操作 userMapper.updateById(user);}
可以看的version已经由1变成了2
五、逻辑删除
-
物理删除:真实删除,将数据从数据库中删除,之后就查不到这条数据
-
逻辑删除:不是真实删除,通过将修改删除字段状态,数据库里还有这条数据
(1)在数据库中添加deleted字段,类型为tinyint,长度为1
(2)在User实体类中添加deleted字段
@TableLogic@TableField(fill = FieldFill.INSERT)private Integer deleted;
(3)元对象处理器接口中添加deleted的insert默认值(也可以在数据库设计表的时候默认值设为0)
@Overridepublic void insertFill(MetaObject metaObject) { this.setFieldValByName("createTime",new Date(),metaObject); this.setFieldValByName("updateTime",new Date(),metaObject); this.setFieldValByName("deleted",0,metaObject);}
(4)mybatis-plus默认0是没有删除,1是删除,也可以在配置文件中修改
mybatis-plus.global-config.db-config.logic-delete-value=1mybatis-plus.global-config.db-config.logic-not-delete-value=0
(5)在MybatisPlusConfig中注册Bean
@Beanpublic ISqlInjector sqlInjector(){ return new LogicSqlInjector();}
(6)测试逻辑删除逻辑
@Testpublic void testLogicDeleteSelect(){ int i = userMapper.deleteById(1L); System.out.println("i = " + i);}
执行成功可以看到数据量id为1那条数据deleted字段值变为1;
然后调用查询所有的方法查看结果:
可以看到mybatis-plus会自动的在sql语句中添加WHERE deleted=0,查询出的结果里也没有刚刚删除的数据。
六、Wrapper介绍
Wrapper :条件构造抽象类,最顶端父类
AbstractWrapper :用于查询条件封装,生成 sql 的 where 条件
QueryWrapper :Entity 对象封装操作类,不是用lambda语法
UpdateWrapper :Update 条件封装,用于Entity对象更新操作
AbstractLambdaWrapper :Lambda 语法使用 Wrapper统一处理解析 lambda 获取 column。
LambdaQueryWrapper :看名称也能明白就是用于Lambda语法使用的查询Wrapper
LambdaUpdateWrapper :Lambda 更新封装Wrapper
(1)gt、ge、lt、le
-
gt:大于
-
ge:大于等于
-
lt:小于
-
le:小于等于
//查询年龄大于等于20的user@Testpublic void testGe(){ List<User> users = userMapper.selectList(new QueryWrapper<User>().lambda().ge(User::getAge, 20)); users.forEach(System.out::println); }
(2)eq、ne
-
eq:等于
-
ne:不等于
//查询name是张三@Testpublic void testEq(){ User user = userMapper.selectOne(new QueryWrapper<User>().lambda().eq(User::getName, "张三")); System.out.println("user = " + user);}
(3)between区间
//查询年龄在20-30之间的@Testpublic void testBetween(){ List<User> users = userMapper.selectList(new QueryWrapper<User>().lambda().between(User::getAge, 20, 30)); users.forEach(System.out::println);}
(4)like 模糊查询
//对名字进行模糊查询@Testpublic void testLike(){ List<User> user = userMapper.selectList(new QueryWrapper<User>().lambda().like(User::getName, "张")); user.forEach(System.out::println);}
还有很多很方便使用的功能可以上官网学习...
往期回顾: