基础简介
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
特性:
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
快速入门
基础CRUD的操作
准备:
首先让我们的数据库持久层的mapper继承一个baseMapper
package cn.supperbro.mybatisplustest.mapper;
import cn.supperbro.mybatisplustest.domain.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface UserMapper extends BaseMapper<User> {
}
将其放入spring的IOC容器当中,每次使用,直接自动装配
@Autowired
private UserMapper userMapper;
- insert into …
@Test
public void testInsert(){
User user = new User();
user.setName("Marry");
user.setAge(20);
user.setEmail("1817566926@qq.com");
System.out.println(userMapper.insert(user));
}
在执行这个插入语句的时候,调用的是userMapper.insert()方法,不用再去手动书写sql语句。
Parameters: 1433565591939190785(Long), Marry(String), 20(Integer), 1817566926@qq.com(String), 0(Integer), 2021-09-03 07:00:46.138(Timestamp), null
从插入的数据来看,我们没有给id赋值,然而却生成了一个Long类型的数据,是因为MyBatis有自动生成的id的策略,我们只需要添加注解就ok
@TableId(type = IdType.ASSIGN_ID)
private Long id;
对于 IdType这是一个枚举类型
package com.baomidou.mybatisplus.annotation;
public enum IdType {
AUTO(0),
NONE(1),
INPUT(2),
ASSIGN_ID(3),
ASSIGN_UUID(4),
/** @deprecated */
@Deprecated
ID_WORKER(3),
/** @deprecated */
@Deprecated
ID_WORKER_STR(3),
/** @deprecated */
@Deprecated
UUID(4);
private final int key;
private IdType(int key) {
this.key = key;
}
public int getKey() {
return this.key;
}
}
自3.3.0开始,mybatis默认使用雪花算法+UUID(不含中划线)
- update…
@Test
public void testUpdate(){
User user = new User();
user.setId(1433565591939190785L);
user.setName("Marry-Plus");
System.out.println(userMapper.updateById(user));
}
使用更新时,我们同样时调用方法就好了
但是从我们的更新语句中发现
UPDATE user SET name=?, version=?, update_time=? WHERE id=? AND version=?
自动添加了 version=?, update_time=?。
首先关于update_time
1.阿里巴巴开发手册中提到,每个数据库的表应该包括创建时间和更新时间两个字段,用于追踪到数据的修改,新增加的基本信息。
2.在mybatis中也有这样的机制:
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
3 根据FieldFill字段填充进行自动填充,配置生成策略
package cn.supperbro.mybatisplustest.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
//注入到spring并且去实现 MetaObjectHandler接口,实现方法
@Component
public class DateHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime",new Date(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
关于version字段呢
首先需要了解的是乐观锁:简单理解就是非常乐观,认为所有的操作不会出现问题。乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
1 取出记录时,获取当前version
2 更新时,带上这个version
3 执行更新时, set version = newVersion where version = oldVersion
4 如果version不对,就更新失败
配置springboot,定义一个配置类,注入
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
在实体类中加入注解字段:
@Version
private int version;
-
delete…
对于删除操作,实际上我们是不希望对数据进行真正意义上的删除,也就是物理删除,我们希望进行逻辑删除。同样mybatis也存在这样的机制。说明:
只对自动注入的sql起效:
插入: 不作限制
查找: 追加where条件过滤掉已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
更新: 追加where条件防止更新到已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
删除: 转变为 更新
例如:删除: update user set deleted=1 where id = 1 and deleted=0
查找: select id,name,deleted from user where deleted=0
字段类型支持说明:支持所有数据类型(推荐使用 Integer,Boolean,LocalDateTime)
如果数据库字段使用datetime,逻辑未删除值和已删除值支持配置为字符串null,另一个值支持配置为函数来获取值如now()附录:
逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。
配置文件
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
注解
@TableLogic
private int flag;
测试
@Test
public void testDelete(){
System.out.println(userMapper.deleteById(1433565591939190785L));
}
我们调用的虽然是delete方法,但是实际上它运行的却是:
UPDATE user SET flag=1 WHERE id=? AND flag=0
- select
@Test
public void testSelect() {
System.out.println(("----- selectAll method test ------"));
List<User> userList = userMapper.selectList(null);
for (User user : userList) {
System.out.println(user);
}
}
但我们在设置了逻辑删除之后,查询也会遵循对应的原则,及不会去查找 flag = 1的元组
关于Wrapper
在进行条件查询时的时候,我们发现总是需要传递一个wrapper,那么wrapper如何定义呢。
测试案例:
@Test
public void testQueryWrapper01(){
QueryWrapper<User> user = new QueryWrapper<>();
//模糊查询,查询名字中含有J,并且邮箱以test开头的user
user.like("name",'j').likeRight("email","test");
userMapper.selectList(user).forEach(System.out::println);
}
@Test
public void testQueryWrapper02(){
QueryWrapper<User> user = new QueryWrapper<>();
//查询id在2--8之间的元素
user.between("id",2,8);
userMapper.selectList(user).forEach(System.out::println);
}
@Test
public void testQueryWrapper03(){
QueryWrapper<User> user = new QueryWrapper<>();
//查询id为1433565591939190785的元素
user.eq("id",1433565591939190785L);
userMapper.selectList(user).forEach(System.out::println);
}
@Test
public void testQueryWrapper04(){
QueryWrapper<User> user = new QueryWrapper<>();
//查询id为1,3,6,10其中的元素
user.in("id", Arrays.asList(1,3,6,100));
userMapper.selectList(user).forEach(System.out::println);
}
@Test
public void testQueryWrapper05(){
QueryWrapper<User> user = new QueryWrapper<>();
//查询createTime降序排列元素
user.orderByDesc("create_time");
userMapper.selectList(user).forEach(System.out::println);
}
关于分页插件
配置,设置springboot配置类
//Spring boot方式
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {
// 旧版
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
// paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
// paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
// 最新版
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
return interceptor;
}
}
测试案例
@Test
public void testLimitPage(){
QueryWrapper<User> user = new QueryWrapper<>();
//查询createTime降序排列元素
user.orderByDesc("create_time");
Page<User> page = new Page<>(1,5);
userMapper.selectPage(page,user);
page.getRecords().forEach(System.out::println);
}
关于代码自动生成器
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
package cn.supperbro.mybatisplustest;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.Arrays;
public class DivCodeGenerator {
public static void main(String[] args) {
//构建一个代码自动生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
//项目路劲
String projectPath = System.getProperty("user.dir");
//放置的目录
gc.setOutputDir(projectPath + "/src/main/java");
//作者名
gc.setAuthor("supperbro");
//是否打开文件生成的win窗口
gc.setOpen(false);
//是否覆盖文件
gc.setFileOverride(false);
//去掉service的I前缀
gc.setServiceName("%sService");
//配置ID生成策略
gc.setIdType(IdType.ASSIGN_ID);
//配置日期格式
gc.setDateType(DateType.ONLY_DATE);
// 实体属性 Swagger2 注解
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
//设置数据源
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/jdbctest?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=UTF-8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
//数据库的类型
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
//模块名
pc.setModuleName("demo");
//包路径
pc.setParent("cn.supperbro.mybatisplustest");
//生成实体类
pc.setEntity("domain");
//生成mapper
pc.setMapper("mapper");
pc.setService("service");
pc.setController("controller");
mpg.setPackageInfo(pc);
// 策略配置
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("flag");//逻辑删除策略
//自动填充配置
TableFill create = new TableFill("create_time", FieldFill.INSERT);
TableFill modify = new TableFill("create_time", FieldFill.INSERT_UPDATE);
strategy.setTableFillList(Arrays.asList(create,modify));
//乐观锁
strategy.setVersionFieldName("version");
//rest风格
strategy.setRestControllerStyle(true);
strategy.setControllerMappingHyphenStyle(true);
mpg.setStrategy(strategy);
mpg.execute();
}
}