Mybatis-Plus
文章目录
1、MybatisPlus
简介
一个更加简化的jdbc访问数据库的一个框架,是mybatis的升级版,mybatis虽然简化了JDBC访问数据库的操作,但是还是要写SQL语句,而mybatisplus就需要我们写SQL语句了,只要我们继承一个类即可BaseMapper<实体类名称>
,一旦继承了该类所有的CRUD操作都已经自动帮我们编写完了。
- **官网:**https://mp.baomidou.com/
特性:
- 无入侵。损耗小
- 强大的CRUD操作
- 支持lombok形式调用
- 支持主键自动生成
- 支持ActiveRecord模式
- 支持自定义全局通用操作
- 内置代码生成器:采用代码或者Maven插件可快速生成Mapper、model、service、controller层代码,支持模板引擎,更有超多自定义配置
- 内置分页插件且支持多种数据库
- 内置性能分析插件:可输出sql语句以及其执行时间
- 内置全局拦截插件
2、快速入门
数据库环境
- 创建数据库
Mybatis_plus
- 创建表
使用MybatisPlus的步骤
- 导入依赖
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
**说明:**尽量不要同时导入Mybatis和MybatisPlus 版本差异
- 配置数据源(application.yaml)
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/mybatis-plus?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
- pojo
package com.example.mybatisplus.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class plus {
private Integer id;
private String name;
private String password;
}
- mapper(dao)
package com.example.mybatisplus.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mybatisplus.pojo.plus;
import org.springframework.stereotype.Repository;
@Repository
//将我们的mapper继承mybatis-plus的BaseMapper类即可
public interface PlusMapper extends BaseMapper<plus> {
//继承了该类基本的CRUD操作都已经自动帮我们生成了,只需调用即可
//注意我们需要在主启动类上加上扫描该包的注解 @MapperScan()
}
只需将我们的mapper继承mybatis-plus的BaseMapper类即可,继承了该类基本的CRUD操作都已经自动帮我们生成了,只需调用即可。 注意:需在主启动类上加上扫描该包的注解 @MapperScan()
- test
package com.example.mybatisplus;
import com.example.mybatisplus.dao.PlusMapper;
import com.example.mybatisplus.pojo.plus;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class MybatisPlusApplicationTests {
@Autowired
PlusMapper plusMapper;
@Test
void contextLoads() {
List<plus> pluses = plusMapper.selectList(null);
pluses.forEach(System.out::println);
}
@Test
//条件查询
void contextLoads() {
//批量查询
List<plus> pluses1 = plusMapper.selectBatchIds(Arrays.asList(1, 2));
//map条件查询
HashMap<String, Object> map = new HashMap<>();
map.put("name","qiumin");
map.put("name","changsha");
List<plus> pluses2 = plusMapper.selectByMap(map);
//查询所有
List<plus> pluses = plusMapper.selectList(null);
pluses.forEach(System.out::println);
}
}
- SQL语句mybatis-plus已经帮我们写好了,方法也写好了,我们只需要调用方法
3、配置日志
由于我们现在使用了mybatis-plus,SQL语句在底层就帮我们生成好了,我们可以通过日志来看到底是怎样执行的!
- 配置日志(控制台打印)
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
4、CRUD
//注入
@Autowired
PlusMapper plusMapper;
插入
@Test
void contextLoads() {
List<plus> pluses = plusMapper.selectList(null);
plus plus=new plus(3,"haha","888888");
//插入
plusMapper.insert(plus);
pluses.forEach(System.out::println);
}
//插入时没有设置id主键时默认主键填充为 ID_WORKER(3)
更新
@Test
void contextLoads() {
plus plus=new plus();
plus.setId(3);
plus.setName("hahaha");
//更新
plusMapper.updateById(plus);
List<plus> pluses = plusMapper.selectList(null);
pluses.forEach(System.out::println);
}
//更新时会通过条件自动拼接Sql,即动态SQL
删除
@Test
void contextLoads() {
plus plus=new plus();
plus.setId(3);
//删除三号用户
plusMapper.deleteById(plus);
List<plus> pluses = plusMapper.selectList(null);
pluses.forEach(System.out::println);
}
5、主键生成策略
**分布式系统唯一id生成参考文档:**http://www.cnblogs.com/haoxinyue/p/5208136.html
- 雪花算法
主键自增
在类字段上配置属性自增:
- 实体字段上
@TableId(type = IdType.AUTO)
。 - 数据库字段上一定是自增的。默认为:ID_WORKER(3)主键填充策略
- 源码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.baomidou.mybatisplus.annotation;
public enum IdType {
AUTO(0), //自增策略
NONE(1), //没有
INPUT(2), //自己手动输入
ASSIGN_ID(3), //MySQL默认的主键填充策略,全局唯一
ASSIGN_UUID(4); //UUID填充策略,全局唯一
private final int key;
private IdType(int key) {
this.key = key;
}
public int getKey() {
return this.key;
}
}
6、自动填充时间
表的创建时间、修改时间,这些我们可以通过一些必要的设置实现自动填充
方式一:数据库级别的操作
- 在表中创建两个字段,分别用于创建时间和更新时间
create_time
update_time
注意:这里由于数据库的版本不同,该数据类型也不相同
- 在5.7版本以上的MySQL版本才有更新的选项,且数据类型为
datetime
,在勾选更新即可 - 而5.7之前的的版本数据类型为
timestamp
,且没有更新选项
方式二:代码上的操作
- 清除数据库级别的操作。
- 添加填充的字段,且在上面加入对应的注解。
//自动添加填充时间
@TableField(fill= FieldFill.INSERT)
private Date create_time;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date update_time;
- 编写一个对该注解处理的处理器类
package com.example.mybatisplus.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill...");
//处理创建和插入时的注解填充
this.setFieldValByName("create_time",new Date(),metaObject);
this.setFieldValByName("update_time",new Date(),metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill...");
//处理更新时的注解填充
this.setFieldValByName("update_time",new Date(),metaObject);
}
}
7、乐观锁和悲观锁
乐观锁:对一些共享的资源但不会上锁,因为加了锁就会影响部分的性能,所以乐观锁不会加锁,它是使用CAS操作实现资源的安全。
悲观锁:不管什么情况都会加锁,加了锁就会影响性能,会设计锁的升级,升级成重量级时,即一个
monitor
实现mysql数据库的乐观锁:
- 取出记录时,获取当前的version版本
- 更新时版本号加一
set version =newVersion where version=oldVersion
- 如果版本号不对就更新失败
mysql数据库的乐观锁的实现
- 在数据库中加一个version的字段用来表示乐观锁。
ALTER TABLE plus ADD `version` INT(4) NOT NULL COMMENT '乐观锁'
- 在实体类中加上这个字段并且在该字段上加上乐观锁的注解 @Version。
//乐观锁
@Version
private Integer version;
- 注册组件,即注册乐观锁插件。
package com.example.mybatisplus.handler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
//扫描mapper包
@MapperScan("com.example.mybatisplus.dao")
//开启
@EnableTransactionManagement
@Configuration
public class MyVersionConfig {
@Bean
public MybatisPlusInterceptor myOptimisticLockerInnerInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
- 测试乐观锁
@Test
void contextLoads() {
//先查询出乐观锁版本
plus plus1=plusMapper.selectById(2);
plus1.setName("hunan");
//另一个线程插队执行
plus plus2=plusMapper.selectById(2);
plus2.setName("changsha");
plusMapper.updateById(plus2);
plusMapper.updateById(plus1);
List<plus> pluses = plusMapper.selectList(null);
pluses.forEach(System.out::println);
}
- 先查询出来我们想要更新的记录用于获取乐观锁版本,然后就会根据乐观锁的版本来决定是否更新成功,当一个线程已经更新完成,另一个线程来的时候由于前一个线程将乐观锁的版本加1了,所以后来的的线程更新失败。
底层生成的SQL语句为:
update plus set name="changsha",version=version+1 where id=2 and version=1
注意:这里可能会遇到 **Mybatis_plus 出现 MP_OPTLOCK_VERSION_ORIGINAL ** 问题
解决办法参考网址:https://blog.csdn.net/heiiochange/article/details/114491848
8、分页查询
注册分页插件
package com.example.mybatisplus.handler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
//扫描mapper包
@MapperScan("com.example.mybatisplus.dao")
//开启
@EnableTransactionManagement
@Configuration
public class MyVersionConfig {
@Bean
//注册分页插件
public PaginationInnerInterceptor getPaginationInnerInterceptor(){
return new PaginationInnerInterceptor();
}
}
测试
@Test
public void testPageLimit(){
Page<plus> page = new Page<>(1,1);
plusMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println);
}
9、逻辑删除
删除分为两种:
- 物理删除:这是真正的删除,删除后在机器中就找不到了,永久删除了,就相当于将回收站中的东西清空。
- 逻辑删除:这是逻辑上的删除,实际上还存在,就相当于将文件放到回收站中。
实现数据库的逻辑删除
- 数据库中加上一个表示逻辑删除的字段 (版本不同可能delete不能作为字段名,根据版本而定)
- 实体类中加上字段,并加上注解 @TableLogic
//逻辑删除
@TableLogic
private Integer delete;
- 配置config
@Bean
public ISqlInjector getISqlInjector(){
return new LogicSqlInjector();
}
注意:在mybatis-plus 3.1.1版本之后的不需要配置了直接自动识别,只需 注解+.yaml
- 配置Application.yaml
mybatis-plus:
global-config:
db-config:
logic-delete-value: 1 #删除了为1
logic-not-delete-value: 0 #没删除为0
- 测试
@Test
public void testLogic(){
plusMapper.deleteById(2);
List<plus> pluses = plusMapper.selectList(null);
pluses.forEach(System.out::println);
}
- 现在的删除方法走的其实是更新操作,就是将表示逻辑删除的字段修改一下值,后续的查询操作就查不出来该记录,因为已经逻辑删除了,但是在数据库中还是存在的,只不过有逻辑删除标记
10、条件构造器
mybatisplus中一些简单的SQL语句会自动帮我们生成但是一些复杂的SQL语句还是需要我们去约束一下,那么条件构造器就显得很重要了 Wrapper
- Wrapper使用测试:
@Test
public void testWrapper1(){
QueryWrapper<plus> wrapper = new QueryWrapper<>();
//查出来的数据根据id排序
wrapper.orderByDesc("id");
List<plus> pluses = plusMapper.selectList(wrapper);
pluses.forEach(System.out::println);
}
@Test
public void testWrapper2(){
QueryWrapper<plus> wrapper = new QueryWrapper<>();
//查询名字不为空,密码不为空且收入大于10000的用户
wrapper.isNotNull("name").isNotNull("password").ge("income",21);
List<plus> pluses = plusMapper.selectList(wrapper);
pluses.forEach(System.out::println);
}
@Test
public void testWrapper3(){
QueryWrapper<plus> wrapper = new QueryWrapper<>();
//查询name值为qiumin的数据
wrapper.eq("name","qiumin");
List<plus> pluses = plusMapper.selectList(wrapper);
pluses.forEach(System.out::println);
}
@Test
public void testWrapper4(){
QueryWrapper<plus> wrapper = new QueryWrapper<>();
//查询说如在999到10000之间的银狐数据
wrapper.between("income",999,10000);
List<plus> pluses = plusMapper.selectList(wrapper);
pluses.forEach(System.out::println);
}
@Test
public void testWrapper5(){
QueryWrapper<plus> wrapper = new QueryWrapper<>();
//模糊查询 查询名字中不带 杰 的并且邮箱是以好结尾的用户数据
wrapper.notLike("name","杰").likeLeft("mail","h");
List<plus> pluses = plusMapper.selectList(wrapper);
pluses.forEach(System.out::println);
}
@Test
public void testWrapper6(){
QueryWrapper<plus> wrapper = new QueryWrapper<>();
//子查询
wrapper.inSql("id","select id from user where id<6");
List<plus> pluses = plusMapper.selectList(wrapper);
pluses.forEach(System.out::println);
}
参考mybatis-plus官网:
11、代码自动生成器
mybatis-plus可以自动的帮我们生成一张表的pojo、mapper、service、controller代码不需要我们手动的写,我们只需把表创建好,编写自动生成代码运行,程序就会自动的帮我们生成文件。包括swagger2
- 导入包
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!--以下核心包-->
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!--代码生成需要用到的引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--以上核心包-->
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- 配置相关的环境、逻辑删除等等,代码生成在dev环境下才能生效
spring:
profiles:
active: dev
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/mybatis-plus?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
global-config:
db-config:
logic-delete-value: 1
logic-not-delete-value: 0
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 代码自动生成程序
在test包下编写代码自动生成程序
package com.qiumin.mybatisplust;
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.ArrayList;
public class qiuminCode {
public static void main(String[] args) {
//需要构建一个代码自动生成器对象
AutoGenerator mpg = new AutoGenerator();
//1、全局配置
GlobalConfig gc = new GlobalConfig(); //注意导包正确
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("qiumin"); //设置作者
gc.setOpen(false);
gc.setFileOverride(false);
gc.setServiceName("%sService"); //去Service的I前缀
gc.setIdType(IdType.ID_WORKER); //主键自增策略
gc.setDateType(DateType.ONLY_DATE); //时间类型
gc.setSwagger2(true); //接口文档
mpg.setGlobalConfig(gc); //丢入自动生成器对象
//2、设置数据源 [自己的数据源]
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatis-plus?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc); //丢入自动生成器对象
//3、包的配置
PackageConfig pc = new PackageConfig();
pc.setModuleName("user");
pc.setParent("com.qiumin"); //路径
pc.setEntity("entity");
pc.setMapper("mapper");
pc.setXml("mapper.xml");
pc.setController("controller");
pc.setService("service");
pc.setServiceImpl("service.impl");
mpg.setPackageInfo(pc); //丢入自动生成器对象
//4、策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("user"); //设置要映射的表名
strategyConfig.setNaming(NamingStrategy.underline_to_camel);
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
strategyConfig.setEntityLombokModel(true); //自动使用Lombok
strategyConfig.setLogicDeleteFieldName("flag"); //逻辑删除
//5、自动填充配置
TableFill create_time = new TableFill("create_time", FieldFill.INSERT); //自动填充创建时间
TableFill update_time = new TableFill("update_time", FieldFill.INSERT_UPDATE); //自动填充更新时间
ArrayList<TableFill> tableList = new ArrayList<>();
tableList.add(create_time);
tableList.add(update_time);
strategyConfig.setTableFillList(tableList);
strategyConfig.setVersionFieldName("version"); //乐观锁
strategyConfig.setRestControllerStyle(true);
mpg.setStrategy(strategyConfig);//丢入自动生成器对象
mpg.execute(); //执行
}
}
qiumin