简介
MyBatis-Plus (简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
官网:https://baomidou.com/
特性
无侵入 :只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小 :启动即会自动注入基本 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
达梦数据库 、 虚谷数据库 、 人大金仓数据库
框架结构
快速开始 创建数据库表 1 2 3 4 5 6 7 8 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) );
1 2 3 4 5 6 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' );
创建SpringBoot项目,添加依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.3.3.RELEASE</version > <relativePath /> </parent > <groupId > cn.yanghuisen</groupId > <artifactId > mp-demo</artifactId > <version > 0.0.1-SNAPSHOT</version > <name > mp-demo</name > <description > Demo project for MyBatis-Plus</description > <properties > <java.version > 1.8</java.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <scope > runtime</scope > </dependency > <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 > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.3.2</version > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > </project >
注:如果引入了MyBatis-Plus就不要在引入MyBtais,两者选一
配置数据库账号密码 修改application.yml文件
1 2 3 4 5 6 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 url: jdbc:mysql://localhost:3306/mpdemo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
编码 创建实体类 使用Lombok
减少代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package cn.yanghuisen.mpdemo.entity;import lombok.Data;@Data public class User { private Long id; private String name; private Integer age; private String email; }
mapper接口 1 2 3 4 5 6 7 8 9 10 11 12 13 package cn.yanghuisen.mpdemo.mapper;import cn.yanghuisen.mpdemo.entity.User;import com.baomidou.mybatisplus.core.mapper.BaseMapper;public interface UserMapper extends BaseMapper <User > {}
UserMapper接口继承BaseMapper,泛型参数为实体类对象
配置扫描注解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package cn.yanghuisen.mpdemo;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication @MapperScan("cn.yanghuisen.mpdemo.mapper") public class MpDemoApplication { public static void main (String[] args) { SpringApplication.run(MpDemoApplication.class, args); } }
使用@MapperScan
指定扫描的mapper
接口路径
开始使用 查询所有的数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package cn.yanghuisen.mpdemo;import cn.yanghuisen.mpdemo.entity.User;import cn.yanghuisen.mpdemo.mapper.UserMapper;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import java.util.List;@SpringBootTest @RunWith(SpringRunner.class) @Slf4j class MpDemoApplicationTests { @Autowired private UserMapper userMapper; @Test void selectListTest () { log.info("*****查询User列表*****" ); List<User> userList = userMapper.selectList(null ); log.info("*****打印userList*****" ); userList.forEach(System.out::println); } }
1 2 3 4 5 User(id=1, name=Jone, age=18, email=test1@baomidou.com) User(id=2, name=Jack, age=20, email=test2@baomidou.com) User(id=3, name=Tom, age=28, email=test3@baomidou.com) User(id=4, name=Sandy, age=21, email=test4@baomidou.com) User(id=5, name=Billie, age=24, email=test5@baomidou.com)
到此就可以体会到MyBatis-Plus的强大魅力,只需要简单的配置就可以实现基本的CRUD操作。
日志配置 开始日志,查看具体SQL命令
application.yml
1 2 3 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
注解 @TableName 表名注解
属性
类型
必须指定
默认值
描述
value
String
否
“”
表名
schema
String
否
“”
schema
keepGlobalPrefix
boolean
否
false
是否保持使用全局的 tablePrefix 的值(如果设置了全局 tablePrefix 且自行设置了 value 的值)
resultMap
String
否
“”
xml 中 resultMap 的 id
autoResultMap
boolean
否
false
是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建并注入)
@TableId 主键注解
属性
类型
必须指定
默认值
描述
value
String
否
“”
主键字段名
type
Enum
否
IdType.NONE
主键类型
IdType
主键生成方式
值
描述
AUTO
数据库ID自增
NONE
无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
INPUT
insert前自行set主键值
ASSIGN_ID
分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator
的方法nextId
(默认实现类为DefaultIdentifierGenerator
雪花算法)
ASSIGN_UUID
分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator
的方法nextUUID
(默认default方法)
ID_WORKER
分布式全局唯一ID 长整型类型(please use ASSIGN_ID
)
UUID
32位UUID字符串(please use ASSIGN_UUID
)
ID_WORKER_STR
分布式全局唯一ID 字符串类型(please use ASSIGN_ID
)
@TableField 字段注解(非主键)
属性
类型
必须指定
默认值
描述
value
String
否
“”
数据库字段名
el
String
否
“”
映射为原生 #{ ... }
逻辑,相当于写在 xml 里的 #{ ... }
部分
exist
boolean
否
true
是否为数据库表字段
condition
String
否
“”
字段 where
实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s}
,参考
update
String
否
“”
字段 update set
部分注入, 例如:update=”%s+1”:表示更新时会set version=version+1(该属性优先级高于 el
属性)
insertStrategy
Enum
N
DEFAULT
举例:NOT_NULL: insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>)
updateStrategy
Enum
N
DEFAULT
举例:IGNORED: update table_a set column=#{columnProperty}
whereStrategy
Enum
N
DEFAULT
举例:NOT_EMPTY: where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if>
fill
Enum
否
FieldFill.DEFAULT
字段自动填充策略
select
boolean
否
true
是否进行 select 查询
keepGlobalFormat
boolean
否
false
是否保持使用全局的 format 进行处理
jdbcType
JdbcType
否
JdbcType.UNDEFINED
JDBC类型 (该默认值不代表会按照该值生效)
typeHandler
Class<? extends TypeHandler>
否
UnknownTypeHandler.class
类型处理器 (该默认值不代表会按照该值生效)
numericScale
String
否
“”
指定小数点后保留的位数
FieldStrategy
值
描述
IGNORED
忽略判断
NOT_NULL
非NULL判断
NOT_EMPTY
非空判断(只对字符串类型字段,其他类型字段依然为非NULL判断)
DEFAULT
追随全局配置
FieldFill
字段填充策略
值
描述
DEFAULT
默认不处理
INSERT
插入时填充字段
UPDATE
更新时填充字段
INSERT_UPDATE
插入和更新时填充字段
@TableLogic 表字段逻辑处理注解(逻辑删除)
属性
类型
必须指定
默认值
描述
value
String
否
“”
逻辑未删除值
delval
String
否
“”
逻辑删除值
CRUD接口 MapperCRUD接口
这里只是一部分查询接口,完整的查询接口请查看官方文档。
insert-插入数据 1 2 3 4 5 6 7 8 9 10 @Test void insertTest () { User user = new User(); user.setName("Ahh" ); user.setAge(20 ); user.setEmail("admin@yanghuisen.cn" ); userMapper.insert(user); log.info(user.toString()); selectListTest(); }
调用insert方法进行数据插入,传入一个user对象。插入成功后会把生成的ID,回填到user对象中。
注:默认使用的是雪花算法 生成对应的ID,如果要修改ID生成算法可以在实体类上添加注解@TableId
1 2 3 4 5 6 7 8 9 10 11 public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; private String email; }
注:如果要使用auto自增主键的话,需要开启数据库的主键ID自增。
update-更新数据 1 2 3 4 5 6 7 8 9 10 11 @Test void updateTest () { User user = new User(); user.setId(6L ); user.setName("啊哈哈" ); user.setAge(20 ); user.setEmail("admin@yanghuisen.cn" ); int i = userMapper.updateById(user); log.info(i+"" ); selectListTest(); }
注:updateById
传入的是user对象,不是具体的ID。
select-查询数据 批量查询 1 2 3 4 5 @Test void selectUserByBatchId () { List<User> userList = userMapper.selectBatchIds(Arrays.asList(1 ,2 ,3 ,4 ,5 )); userList.forEach(System.out::println); }
Map条件查询 1 2 3 4 5 6 7 8 @Test void selectUserByMap () { Map<String,Object> map = new HashMap<>(); map.put("id" ,1 ); map.put("name" ,"Jone" ); List<User> userList = userMapper.selectByMap(map); userList.forEach(System.out::println); }
delete-删除数据 1 2 3 4 5 @Test void deleteUserById () { userMapper.deleteById(1302239794451763202L ); selectListTest(); }
删除同样有批量删除和map删除,使用方式和查询的一样
条件构造器 细心的就会发现,上面的CRUD都是一些简单的操作,如果是复杂的就没办法了,所以这时就要用到了条件构造器。
条件构造器有很多,具体的还请查看官方网文档,这里只会列举几个使用方法。
isNotNull-不为空 查询name不为空的用户,并且邮箱部位空的用户
1 2 3 4 5 6 7 8 9 10 @Test public void isNotNullTest () {QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.isNotNull("name" ); wrapper.isNotNull("email" ); userMapper.selectList(wrapper).forEach(System.out::println); }
ge-大于等于 查询age大于等于22的用户
1 2 3 4 5 6 7 @Test public void geTest () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.ge("age" ,22 ); userMapper.selectList(wrapper).forEach(System.out::println); }
eq-等于 查询name等于Jone的用户
1 2 3 4 5 6 7 @Test public void eqTest () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("name" ,"Jone" ); System.out.println(userMapper.selectOne(wrapper)); }
selectOne:只查询一条数据
between-X与N之间的值 查询年龄在20到25之间的用户的数量
1 2 3 4 5 6 7 8 @Test public void betweenTest () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.between("age" ,20 ,25 ); Integer count = userMapper.selectCount(wrapper); System.out.println(count); }
selectCount:查询数量
like-模糊查询 1 2 3 4 5 6 7 8 @Test public void likeTest () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.like("name" ,"a" ); List<Map<String,Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); }
selectMaps:返回List<Map>
形式的数据
notLike-模糊查询 查询name中不包含a的用户
1 2 3 4 5 6 7 8 @Test public void notLikeTest () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.notLike("name" ,"a" ); List<Map<String,Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); }
likeLeft-左模糊查询 查询name中以e结尾的用户
1 2 3 4 5 6 7 8 @Test public void likeLeftTest () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.likeLeft("name" ,"e" ); List<Map<String,Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); }
likeRight-左模糊查询 查询name中以j开头的用户
1 2 3 4 5 6 7 8 @Test public void likeRightTest () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.likeRight("name" ,"j" ); List<Map<String,Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); }
inSql id 在子查询中查询出来
1 2 3 4 5 6 7 8 @Test public void inSqlTest () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.inSql("id" ,"select id from user where id < 5" ); List<Object> objects = userMapper.selectObjs(wrapper); objects.forEach(System.out::println); }
selectObjs:返回List<Object>
形式的数据
orderByDesc-降序排序 根据id进行降序排序
1 2 3 4 5 6 7 8 @Test public void orderByDescTest () { QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.orderByDesc("id" ); List<User> userList = userMapper.selectList(wrapper); userList.forEach(System.out::println); }
分页插件 MyBatisPlus也提供了分页插件,但是需要稍微的配置一下
在配置类中添加分页插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package cn.yanghuisen.mpdemo.config;import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration public class MyBatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor () { return new PaginationInterceptor(); } }
测试分页
1 2 3 4 5 6 7 8 @Test void pageTest () { Page<User> page = new Page<>(1 ,5 ); userMapper.selectPage(page,null ); page.getRecords().forEach(System.out::println); log.info("总条数:" +page.getTotal()); }
代码生成器、
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
添加依赖 MyBatisPlus
在 3.0.3
版本之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖
1 2 3 4 5 <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-generator</artifactId > <version > 3.4.0</version > </dependency >
编码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 package cn.yanghuisen.mpdemo;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 CodeGeneration { public static void main (String[] args) { AutoGenerator autoGenerator = new AutoGenerator(); GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir" ); gc.setOutputDir(projectPath+"src/main/java" ); gc.setAuthor("啊哈哈" ); gc.setOpen(false ); gc.setFileOverride(false ); gc.setServiceName("%sService" ); gc.setIdType(IdType.ASSIGN_ID); gc.setDateType(DateType.ONLY_DATE); DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/mpdemo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8" ); dsc.setDriverName("com.mysql.cj.jdbc.Driver" ); dsc.setUsername("root" ); dsc.setPassword("123456" ); dsc.setDbType(DbType.MYSQL); PackageConfig pc = new PackageConfig(); pc.setModuleName("demo" ); pc.setParent("cn.yanghuisen.mpdemo" ); pc.setEntity("entity" ); pc.setMapper("mapper" ); pc.setService("service" ); pc.setController("controller" ); StrategyConfig strategy = new StrategyConfig(); strategy.setInclude("t_user" ); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setEntityLombokModel(true ); strategy.setLogicDeleteFieldName("flag" ); TableFill createTime = new TableFill("create_time" , FieldFill.INSERT); TableFill updateTime = new TableFill("update_time" , FieldFill.INSERT_UPDATE); ArrayList<TableFill> fills = new ArrayList<>(); fills.add(createTime); fills.add(updateTime); strategy.setTableFillList(fills); strategy.setVersionFieldName("version" ); strategy.setRestControllerStyle(true ); strategy.setControllerMappingHyphenStyle(true ); autoGenerator.setGlobalConfig(gc); autoGenerator.setDataSource(dsc); autoGenerator.setPackageInfo(pc); autoGenerator.setStrategy(strategy); autoGenerator.execute(); } }
多数据源(2020-10-18补充)
多数据源,顾名思义,就是配置多个数据来源。一般项目中都是使用一个数据源就足够了,但是也有例外的时候,有时候就需要根据不同的业务需求连接不同的数据库进行查询。
MyBatis-Plus
中已经集成了多数据源的功能,只需要简单的配置就可以使用。
多数据源的使用 添加依赖 Mybatis-Plus
虽然支持多数据源配置,但是没有集成到MyBatis-Plus
的的依赖中,需要单独添加依赖
1 2 3 4 5 6 <dependency > <groupId > com.baomidou</groupId > <artifactId > dynamic-datasource-spring-boot-starter</artifactId > <version > 3.2.1</version > </dependency >
上面依赖是截止到我写多数据源这个文档的时候的最新的版本
配置数据源 修改application.yaml
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 spring: datasource: dynamic: primary: master strict: false datasource: master: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 url: jdbc:mysql://localhost:3306/mpdemo?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8 slave_1: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 url: jdbc:mysql://localhost:3306/slave_1?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
@DS
注解
结果
没有注解
使用默认数据源
@DS("dsName")
dsName为数据源名称
@DS
注解就是切换数据源的关键,该注解可以声明在类和方法上。如果同时在类和方法上都声明了该注解,则方法上的优先于类上的 。(建议只在service
上设数据源)
切换数据源非常简单,只需要在service
方法上添加@DS
注解即可,并且传递数据源名称即可
例子1:
1 2 3 4 5 6 7 @Override @DS("master") public List<User> master () { List<User> userList = userMapper.selectList(null ); userList.forEach(x->log.info(x.toString())); return userList; }
例子2:
1 2 3 4 5 6 7 @Override @DS("slave_1") public List<User> slave_1 () { List<User> userList = userMapper.selectList(null ); userList.forEach(x->log.info(x.toString())); return userList; }
这样就完成了数据源的切换
方法内切换数据源 上面的数据源切换有一个问题,就是在一个方法内还是只能使用一个数据源,如果要在方法内切换到别的数据源是无法做到的。比如以下代码
1 2 3 4 5 6 7 8 9 10 @Override @DS("slave_1") public List<User> slave_1 () { List<User> userList = userMapper.selectList(null ); userList.forEach(x->log.info(x.toString())); return userList; }
这种问题我在公司就遇到了,单使用@DS
注解也是不行的。
这时就要用到DynamicDataSourceContextHolder
类了,通过DynamicDataSourceContextHolder
,在方法内切数据源
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Override @DS("slave_1") public List<User> test02 () { DynamicDataSourceContextHolder.push("master" ); List<User> userList = this .dsMaster(); DynamicDataSourceContextHolder.poll(); userList.forEach(x->log.info(x.toString())); log.info("----切换数据源----" ); userList = this .dsMaster(); userList.forEach(x->log.info(x.toString())); return userList; }
使用DynamicDataSourceContextHolder.push("master");
切换到指定数据源,并且使用之后要使用DynamicDataSourceContextHolder.poll();
恢复到@DS
指定的数据源。
拓展 自动填充 在阿里巴巴开发手册中明确规定,所有的数据库表中都应该含有create_time
和update_time
两个字段(用于表示该条记录的创建和修改时间)。通过自动填充可以自动化的设置这两个字段的值,避免每次都要手动设置。自动填充有两种方式。
数据库级别 数据库级别的就很简单了,给表中添加create_time
和update_time
字段后,设置下就好了。不同的数据库工具设置方法不一样,这里以Navicat
为例。
不建议使用这种方式,一般工作中不允许随意改变数据库
代码级别 注:代码级别设置自动填充的话,需要关闭数据库级别的自动填充(取消Navicat中的根据当前时间戳更新)
通过@TableField
注解设置填充策略。
修改实体类,添加@TableField
注解,设置填充策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Data public class User { @TableId(type = IdType.ASSIGN_ID) 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; }
编写自动填充处理器类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package cn.yanghuisen.mpdemo.handler;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;import org.apache.ibatis.reflection.MetaObject;import org.springframework.stereotype.Component;import java.util.Date;@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); } }
这样在进行插入和更新操作时,就可以自动填充时间到数据库了。
乐观锁 什么是乐观锁,什么是悲观锁
乐观锁
,顾名思义,很乐观。就是它总会知道自己不会出现问题,无论干什么都不会上锁。如果出现问题,再次更新测试。
悲观锁
,顾名思义,很悲观。就是它做什么都会上锁,再去操作。
乐观锁在数据库中一般使用version
字段表示。
乐观锁的实现机制:
取出记录时,获取当前version
更新时,带上这个version
执行更新时, set version = newVersion where version = oldVersion
如果version不对,就更新失败
乐观锁的实现 给数据库表添加version
字段,默认值为1
实体类添加version
属性,并给其添加@Version
注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package cn.yanghuisen.mpdemo.entity;import com.baomidou.mybatisplus.annotation.*;import lombok.Data;import java.util.Date;@TableName("user") @Data public class User { @TableId(type = IdType.ASSIGN_ID) 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; @Version private Integer version; }
创建配置类,注册乐观锁插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package cn.yanghuisen.mpdemo.config;import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configuration public class MyBatisPlusConfig { @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor () { return new OptimisticLockerInterceptor(); } }
逻辑删除
物理删除:直接把数据中的数据删除
逻辑删除:在数据中没有被删除,而是通过一个变量(字段)来表示其失效(删除)状态
添加flag
字段(用于表示是否是删除状态),类型为int
,默认值为0
实体类中添加flag
属性,并且设置注解@TableLogic
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package cn.yanghuisen.mpdemo.entity;import com.baomidou.mybatisplus.annotation.*;import lombok.Data;import java.util.Date;@TableName("user") @Data public class User { @TableId(type = IdType.ASSIGN_ID) 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; @Version private Integer version; @TableLogic private Integer flag; }
在application.yml
进行配置逻辑删除
1 2 3 4 5 mybatis-plus: global-config: db-config: logic-delete-value: 1 logic-not-delete-value: 0
测试逻辑删除
1 2 3 4 5 @Test void deleteUserById () { userMapper.deleteById(7L ); selectListTest(); }
注:在添加逻辑删除后,在查询时会自动过滤掉逻辑删除的记录。