介绍
简介
[MyBatis-Plus](https://github.com/baomidou/mybatis-plus)(简称 MP)是一个 [MyBatis](http://www.mybatis.org/mybatis-3/) 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
官网是这样说的:
他们愿景成为mybatis的搭档,就像魂斗罗的1p和2p,**好基友搭配,干活不累**。
特征
# 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
# 损耗小:启动即会自动注入基本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操作智能分析阻断,也可自定义拦截规则,预防误操作
支持数据库
mysql 、mariadb 、oracle 、db2 、h2 、hsql 、sqlite 、postgresql 、sqlserver 、presto 、Gauss 、Firebird
Phoenix 、clickhouse 、Sybase ASE 、 OceanBase 、达梦数据库 、虚谷数据库 、人大金仓数据库 、南大通用数据库 、
使用
引入依赖
<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>
<!--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>
</dependency>
<!--lombok用来简化实体类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
注意:引入mybatis-plus后不要在引入mybatis,避免版本差异问题
编写实体类
给类名加@TableName,详解:https://baomidou.com/guide/annotation.html#tablename
给id字段写@TableId,详解:https://baomidou.com/guide/annotation.html#tableid
给普通字段写@TableField,详解:https://baomidou.com/guide/annotation.html#tablefield
注意名称与数据库对应。
创建Mapper接口
public interface UserMapper extends BaseMapper<User> {
启动类加注解
@MapperScan("xxx.xxx.xxx.mapper")
加入配置
# 配置SQL日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
# 配置自动驼峰式编码
mybatis-plus.configuration.map-underscore-to-camel-case: false
启动调用mapper的方法
注意mapper,然后调用mapper的方法即可。
我们会发现我们mapper中没有代码,但是仍然可以使用。
常用CRUD方法
插入Insert
操作
int resultLen = mapper.insert(T);
注意:我们这里没有设置主键生成策略,默认是ID_WORKER(雪花算法)策略,
主键生成策略
# myabtisPlus的id生成策略默认是ID_WORKER全局唯一ID
自增策略其实有很多,比如数据库自增,UUID,自己设计、redis原子操作等。各有各的好处坏处,比如数据库自增不利于分库分表,UUID不利于排序和存储、自己设计容易出bug,难度大(大佬请忽略)、redis生成有限制必须使用redis。
参考资料:分布式系统唯一ID生成方案汇总:https://www.cnblogs.com/haoxinyue/p/5208136.html
# 配置id的自增策略
1、给某张表某个ID字段加:@TableId(type=IdType.xxxxx),具体看文档https://baomidou.com/guide/annotation.html#tableid
2、全局设置:mybatis-plus.global-config.db-config.id-type=auto,具体也是看官网文档
更新Update
操作
int result = mapper.update(T);
注意:
其SQL生成是动态的,比如说我们更新一个User对象,其中有三个字段id,name,age,然后我们创建一个User对象,然后只setAge(20),setId(1),不设置name,那么mybatisplus生成的SQL就是:update user set age = 20 where id = 1。
自动填充
这个是咋回事呢,比如说我们数据中有creatTime和updateTime,然后我们想在insert的时候让mybatisplus自动帮我们填充creatTime和updateTime,update的时候让mybatisplus帮我们自动更新updateTime,这个就是自动填充。
操作如下:
1、给字段加注解
@TableField(fill = FieldFill.INSERT) // 代表着插入的时候会填充值
private Date createTime;
//@TableField(fill = FieldFill.UPDATE) // 代表着更新的时候会填充值
@TableField(fill = FieldFill.INSERT_UPDATE) // 代表着插入和更新会填充值
private Date updateTime;
2、写类实现MetaObjectHandler接口
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/*当insert的时候调用*/
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
/*当update的时候调用*/
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
乐观锁
使用场景:我们更新一条数据之前,希望这条数据没有被别人更新过。
举例:有一名员工的工资salary是3000,这时人事经理想来将这salary改为2000,然后总经理想将salary改为5000,然后他们都填好了想修改的salary,然后人事经理先提交,然后总经理后提交,完了后我们人事经理发现咋钱越改越多呢,这个时候就出现了问题,这个就是丢失更新,解决办法可以这样,我们给加一个version字段,然后每次更改后都将version+1,只有当前version和待修改的version一致,那么才能修改,这个version就可以看成一个乐观锁。我们加了version以后,前面的例子最初version为1,人事经理改完后version为2,这时候总经理那里拿到的还是version为1的数据,它来修改的话,手里的version比数据库中的version低,故而不允许修改。
那么myabtisplus里面就可以实现这个version。
官方解释:
# 意图:
当要更新一条记录的时候,希望这条记录没有被别人更新
# 乐观锁实现方式:
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion
如果version不对,就更新失败
那么如何使用:
1、插件配置:
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
2、数据库必须有标志version字段,然后实体类中加注解
@Version
private Integer version;
注意:
支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
整数类型下 newVersion = oldVersion + 1
newVersion 会回写到 entity 中
仅支持 updateById(id) 与 update(entity, wrapper) 方法
在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
查询Select
id查询:
T t = mapper.selectById(id);
多个id批量查询
List<t> ts = mapper.selectBatchIds(Arrays.asList(1, 2, 3));
条件查询
HashMap<String, Object> map = new HashMap<>();
map.put("name", "Helen");
map.put("age", 18);
List<T> users = mapper.selectByMap(map);
Wrapper条件后面再说
.......
分页
配置bean
/**分页插件*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
代码:
Page<T> page = new Page<>(1,5);
mkapper.selectPage(page, null);
page.getRecords().forEach(System.out::println);
System.out.println(page.getCurrent());
System.out.println(page.getPages());
System.out.println(page.getSize());
System.out.println(page.getTotal());
System.out.println(page.hasNext());
System.out.println(page.hasPrevious());
删除Delete
操作
id删除
mapper.deleteById(id);
批量删除
int result=mapper.deleteBatchIds(Arrays.asList(8,9));
条件查询
HashMap<String, Object> map = new HashMap<>();
map.put("name", "Helen");
map.put("age", 18);
int result = mapper.deleteByMap(map);
Wrapper条件后面会说
...
逻辑删除
说起删除,应该分为两种:
逻辑删除:给定某一个标志代表该条数据已经被删除。
物理删除:真的删除了
1、给实体类字段上加注解
@TableLogic
private Integer deleted;
2、配置文件
# 此为默认配置,如果你的删除和未删除的标志不同则可以在此配置
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
3、配置bean
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
4、官网说明
# 说明:
只对自动注入的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()
# 附录:
逻辑删除是为了方便数据恢复和保护数据本身价值等等的一种方案,但实际就是删除。
如果你需要频繁查出来看就不应使用逻辑删除,而是以一个状态去表示。
# 备注:
字段值insert的时候,可以数据库默认值,可以自己set,使用自动填充功能
性能分析日志
配置插件
参数说明:
参数:maxTime: SQL 执行最大时长,超过自动停止运行,有助于发现问题。
参数:format: SQL是否格式化,默认false。
配置bean:
/**
* SQL 执行性能分析插件
* 开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长
*/
@Bean
@Profile({"dev","test"})// 设置 dev test 环境开启,prod不建议开启
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(100);//ms,超过此处设置的ms则sql不执行
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
配置spring的dev环境
#环境设置:dev、test、prod
spring.profiles.active=dev
我们可以自定义对应开发、测试、生产环境不同的配置文件,然后不同环境下我们去使用不同的配置文件,可以在启动的时候添加参数,就可以不改变配置的情况下动态的选择环境,启动代码的时候-d,具体百度。
Wrapper介绍
介绍
Wrapper:条件构造抽象类,最顶端父类
AbstractWrapper:用于查询条件封装,生成sql的where 条件
QueryWrapper:Entity对象封装操作类,不是用lambda语法
UpdateWrapper:Update条件封装,用于Entity对象更新操作
AbstractLambdaWrapper:Lambda语法使用Wrapper统一处理解析lambda获取column。
LambdaQueryWrapper:看名称也能明白就是用于Lambda语法使用的查询Wrapper
LambdaUpdateWrapper:Lambda更新封装Wrapper
一般我们使用QueryWrapper。
使用
ge、gt、le、lt、isNull、isNotNull:
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.isNull("name")
.ge("age", 12)
.isNotNull("email");
int result = userMapper.delete(queryWrapper);
System.out.println("delete return count = " + result);
/**
ge:大于等于
gt:大于
le:小于等于
lt:小于
isNull:为空
isNotNull:不为空
*/
eq、ne:
/**
eq:相等
ne:不相等
*/
between、notBetween:
/**
between:在某一个边界
notBetween:不在某一个边界
*/
allEq:
/**
allEq:整个map的内容全都要全等
*/
like、notLike、likeLeft、likeRight:
/**
like:两边like,%a%
notLike:不like
likeLeft:左边like,%a
likeRiter:右边like,a%
*/
太多了,就不一一列举了。。。。