Mybatis-plus
Mybatis-plus为简化开发而生
特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
快速开始
-
创建数据库
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) );
-
导入数据
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项目并且导入相关的依赖
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.15</version> </dependency> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- mybatis-plus--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency>
-
连接数据库
spring: datasource: username: root password: 123456 url: jdbc:mysql://localhost:3306/mydata?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8 driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource # druid数据源
-
创建pojo类
@Data public class User { private Long id; private String name; private Integer age; private String email; }
-
创建mapper类
@Mapper //继承最基本的BaseMapper类 public interface UserMapper extends BaseMapper<User> { //所有的CRUD操作已经编写完成了,不需要再配置其他文件了 }
-
在启动类上添加包扫描MapperScan,扫描到mapper文件夹
@SpringBootApplication @MapperScan("com.hzp.easybuy.mapper") public class EasyBuyApplication { public static void main(String[] args) { SpringApplication.run(EasyBuyApplication.class, args); } }
-
测试类测试
@Autowired private UserMapper userMapper; @Test public void testSelect() { System.out.println(("----- selectAll method test ------")); //参数是一个wrapper,是一个条件构造器,这里用不到可以直接写null List<User> userList = userMapper.selectList(null); userList.forEach(System.out::println); }
控制台输出:
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)
配置日志
配置application.yaml文件
#日志配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
再次运行刚刚的测试文件,输出日志
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@688a2c09] was not registered for synchronization because synchronization is not active
JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@12b5736c] will not be managed by Spring
==> Preparing: SELECT id,name,age,email FROM user
==> Parameters:
<== Columns: id, name, age, email
<== Row: 1, Jone, 18, test1@baomidou.com
<== Row: 2, Jack, 20, test2@baomidou.com
<== Row: 3, Tom, 28, test3@baomidou.com
<== Row: 4, Sandy, 21, test4@baomidou.com
<== Row: 5, Billie, 24, test5@baomidou.com
<== Total: 5
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@688a2c09]
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)
2022-11-29 15:54:56.575 INFO 10848 --- [ionShutdownHook] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closing ...
2022-11-29 15:54:56.578 INFO 10848 --- [ionShutdownHook] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closed
CRUD扩展
插入操作
@Test
public void testInsert(){
User user = new User();
user.setAge(123);
user.setEmail("123@qq.com");
user.setName("Stephen Curry");
int result = userMapper.insert(user);//帮我们自动生成了id 1597500746948890626(Long)
System.out.println(user);
}
输出:
User(id=1597500746948890626, name=Stephen Curry, age=123, email=123@qq.com)
插入只需要调用insert方法即可,但是这里有一个细节,我们这里并没有设置id,但是插入之后,我们可以看到是有id的。这跟主键生成策略有关。
主键生成策略
默认生成策略为全局唯一id:ID_WORKER
@Data
public class User {
@TableId(type = IdType.ID_WORKER)
private Long id;
private String name;
private Integer age;
private String email;
}
我们可以查看其他的主键生成策略
public enum IdType {
/*
*/
AUTO(0),//主键自增策略,配置 @TableId(type = IdType.AUTO)策略,数据库的字段也必须是自增的
NONE(1),//未设置主键
INPUT(2),//手动输入,配置手动输入就一定要自己输入字段值,不然为null
ID_WORKER(3),//默认的全局唯一id
UUID(4),//全局唯一id,UUID
ID_WORKER_STR(5);//ID_WORKER 的字符串表示
private int key;
private IdType(int key) {
this.key = key;
}
public int getKey() {
return this.key;
}
}
雪花算法
SnowFlake是Twitter公司采用的一种算法,目的是在分布式系统中产生全局唯一且趋势递增的ID。
更新操作
@Test
public void testUpdate(){
User user = new User();
user.setId(6L);
user.setEmail("123456@qq.com");
int result = userMapper.updateById(user);
System.out.println(user);
}
自动填充
数据的创建时间,修改时间,这些操作都应该是自动化完成的,而不是手动更新。
方式一:数据库级别自动填充(工作中不要直接修改数据库)
在数据库新建两个字段,create_time,update_time 并且设置数据类型为datetime,默认值为CURRENT_TIMESTAMP,update_time设置更新
在实体类添加我们新建的字段
@Data
public class User {
@TableId(type = IdType.INPUT)
private Long id;
private String name;
private Integer age;
private String email;
private Date createTime;
private Date updateTime;
}
再次运行更新方法
@Test
public void testUpdate(){
User user = new User();
user.setId(6L);
user.setEmail("123456789@qq.com");
int result = userMapper.updateById(user);
System.out.println(user);
}
idea查看刚刚的那张表
可以看到这张表的更新时间自动的变成了我们更新数据的时间。
方式二:代码级别(推荐使用)
删除刚刚设置的默认值
@TableField注解配置实体类的字段
进入@TableField查看源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface TableField {
String value() default "";
String el() default "";
boolean exist() default true;
String condition() default "";
String update() default "";
FieldStrategy strategy() default FieldStrategy.DEFAULT;
FieldFill fill() default FieldFill.DEFAULT;
boolean select() default true;
}
可以看到在TableField中有FieldFill fill() default FieldFill.DEFAULT;默认填充,我们查看它有什么填充方式。
public enum FieldFill {
DEFAULT,
INSERT,
UPDATE,
INSERT_UPDATE;
private FieldFill() {
}
}
我们可以看到在FieldFill中有四种默认值,此时根据我们的需求,让字段中的创建时间自动生成,更新时间在修改的时候自动更新,所以修改实体类User
@Data
public class User {
@TableId(type = IdType.INPUT)
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;
}
新建handler文件夹并新建处理器处理注解
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入时的填充策略
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill...");
//在创建的时候插入时间
this.setFieldValByName("createTime",new Date(),metaObject);
this.setFieldValByName("updateTime",new Date(),metaObject);
}
//更新时的填充策略
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill...");
this.setFieldValByName("updateTime",new Date(),metaObject);
}
}
测试查看时间的变化
乐观锁
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
- 取出记录时,获取当前 version
- 更新时,带上这个 version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果 version 不对,就更新失败
给数据库增加一个version字段
实体类增加对应的version字段并添加注解
@Version//乐观锁注解
private Integer version;
新建config文件夹创建配置类并注册乐观锁插件
//扫描mapper文件夹
@MapperScan("com.hzp.easybuy.mapper")
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
//旧版方式注册乐观锁插件
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
测试
@Test
//测试乐观锁
public void testOptimisticLocker(){
//线程1
//查询信息
User user = userMapper.selectById(1L);
//修改用户信息
user.setName("hahahsd");
user.setEmail("211232@qq.com");
//更新操作
//模拟另一个线程插队
User user2 = userMapper.selectById(1L);
//修改用户信息
user2.setName("hahah333sd");
user2.setEmail("211234234234232@qq.com");
userMapper.updateById(user2);
userMapper.updateById(user);//如果没有乐观锁就会覆盖插队线程的值
}
查看数据库值,发现最后更新的user并没有覆盖user2的值
查询操作
@Test
public void testSelectById(){//单个查询
User user = userMapper.selectById(1L);
System.out.println(user);
}
@Test
public void testSelectByBatchId(){//批量查询
List<User> users = userMapper.selectBatchIds(Arrays.asList(1,2,3));
users.forEach(System.out::println);
}
条件查询方式之一map
//条件查询map
@Test
public void testSelectByBatchIds(){
HashMap<String , Object> hashMap = new HashMap<>();
//自定义查询的条件
hashMap.put("name","Stephen Curry");
hashMap.put("age",123);
List<User> users = userMapper.selectByMap(hashMap);
users.forEach(System.out::println);
}
分页查询
Mybaits plus内置了分页插件,我们要使用直接在配置类中注册分页插件
//配置分页插件
@Bean
public PaginationInterceptor paginationInterceptor(){
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
return paginationInterceptor;
}
然后直接使用page对象
//分页查询
@Test
public void testPage(){
//参数1: 当前页 参数2:页面大小
Page<User> page = new Page<>(1,3);
userMapper.selectPage(page,null);
page.getRecords().forEach(System.out::println);
}
page还有很多其他的方法,需要使用可以查看官方文档。
删除操作
@Test
public void testDelete(){
userMapper.deleteById(1597500746948890626L);
}
除此之外还可以批量删除,跟上面的查询类似。
逻辑删除
物理删除:数据库中删除数据
逻辑删除:没从数据库删除,但是通过其他变量让它失效,让用户查询不到。
可以防止数据的丢失,类似于回收站
表中新加一个deleted字段
在实体类新增字段
@TableLogic
private Integer deleted;
配置类注册插件
//逻辑删除
@Bean
public ISqlInjector sqlInjector(){
return new LogicSqlInjector();
}
配置yaml
mybatis-plus:
#配置逻辑删除
global-config:
db-config:
logic-delete-value: 1
logic-not-delete-value: 0
执行删除操作,发现数据库并没有走delete,而是update了deleted字段
再次进行查询,发现查询不到删除的数据,但是数据仍然在数据库
User(id=1, name=hahah333sd, age=18, email=211234234234232@qq.com, deleted=0, version=2, createTime=Tue Nov 29 16:34:19 CST 2022, updateTime=Tue Nov 29 17:41:14 CST 2022)
User(id=2, name=Jack, age=20, email=test2@baomidou.com, deleted=0, version=1, createTime=Tue Nov 29 16:34:19 CST 2022, updateTime=Tue Nov 29 16:34:19 CST 2022)
User(id=3, name=Tom, age=28, email=test3@baomidou.com, deleted=0, version=1, createTime=Tue Nov 29 16:34:19 CST 2022, updateTime=Tue Nov 29 16:34:19 CST 2022)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com, deleted=0, version=1, createTime=Tue Nov 29 16:34:19 CST 2022, updateTime=Tue Nov 29 16:34:19 CST 2022)
User(id=5, name=Billie, age=24, email=test5@baomidou.com, deleted=0, version=1, createTime=Tue Nov 29 16:34:19 CST 2022, updateTime=Tue Nov 29 16:34:19 CST 2022)
User(id=7, name=Stephen Curry, age=123, email=123456789@qq.com, deleted=0, version=1, createTime=Tue Nov 29 08:57:59 CST 2022, updateTime=Tue Nov 29 17:01:39 CST 2022)
性能分析插件
首先在yaml配置一下开发环境
spring:
profiles:
active: dev
然后在config文件夹内进行配置
//SQL执行效率插件
@Bean
@Profile({"dev","test"})//设置dev,test环境开启,保证我们的效率
public PerformanceInterceptor performanceInterceptor(){
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(1);//单位毫秒,设置sql执行最大时间,超时则不执行
performanceInterceptor.setFormat(true);//开启sql格式化
return new PerformanceInterceptor();
}
这是旧版的mybatis-plus进行性能分析,新版本的mybatis-plus通过执行SQL分析打印来打印SQL以及执行时长