MyBatisPlus概述和快速入门
概述
基础框架
MyBatis
Spring
SpringMVC
为什么需要学习?
MyBatisPlus
可以节省我们大量的工作时间,所有的CRUD
都可以自动化完成。还有其他框架JPA
、tk-mapper
。
简介
是什么?
Mybatis
本来就是简化JDBC
操作的!MyBatis-Plus
是一个Mybatis
的增强工具,在 MyBatis
的基础上只做增强不做改变,为简化开发、提高效率而生。
愿景
成为 MyBatis
最好的搭档,就像 魂斗罗中的 1P
、2P
,基友搭配,效率翻倍。
特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本
CURD
,性能基本无损耗,直接面向对象操作,BaseMapper
- 强大的
CRUD
操作:内置通用Mapper
、通用Service
,仅仅通过少量配置即可实现单表大部分CRUD
操作,更有强大的条件构造器,满足各类使用需求,以后简单的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
操作智能分析阻断,也可自定义拦截规则,预防误操作
快速入门
使用第三方库,学习流程
- 导入对应的依赖
- 研究依赖如何配置
- 代码如何编写
- 提高扩展技术能力
步骤
- 创建数据库
test
- 创建
user
表
sql复制代码DROP TABLE IF EXISTS user;
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');
-- 真实开发中,version(乐观锁)、deleted(逻辑删除)、gmt_create、gmt_modified 这些字段也是需要的
- 编写项目,初始化项目,使用
Spirngboot
初始化项目。 - 导入依赖
xml复制代码<!--数据库驱动-->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--Mybatis-plus-->
<!--Mybatis-plus 是自己开发的,并非官方的!-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.4</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
说明:我们使用Mybatis-plus
可以节省我们大量的代码,尽量不要同时导入MyBatis
和Mybatis-plus
,有版本的差异。 5. 连接数据库,这一步和Mybatis
相同!
properties复制代码# 根据自己的数据库配置
# mysql 5
spring.datasource.username=root
spring.datasource.password=123456
# useSSL 安全连接;useUnicode 是否使用Unicode字符集;characterEncoding 字符集;
# mysql 8 驱动不同,需要增加时区 serverTimezone=GMT%2B8spring.datasource.url=jdbc:mysql://localhost/test?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
# 使用 8 的驱动,高版本兼容低版本
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
- 传统方式:
pojo
-dao
(连接mybatis
,配置mapper.xml
文件)-service
-controller
; 使用mybatis-plus
之后:
pojo
:
java复制代码@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
mapper
接口
java复制代码/**
* 在对应的 mapper 上面实现继承基本的类 BaseMapper
*/
@Repository
public interface UserMapper extends BaseMapper<User> {
/*
* 所有的CRUD操作都已经编写完成了
* 不需要配置一大推文件了
*/
}
注意点:需要在主启动类上去扫描我们的mapper
包下的所有接口。@MapperScan("com.zbc.mybatis_plus_demo.mapper")
- 测试类中测试
java复制代码@SpringBootTest
class MybatisPlusDemoApplicationTests {
/**
* 继承了 BaseMapper,所有的方法都来自己的父类
* 我们也可以编写自己的扩展方法
*/
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
//参数是一个 wrapper,条件构造器
//查询所有用户
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
}
思考问题
SQL
谁帮我们写的?Mybatis-Plus
- 方法从哪里来的?
Mybatis-Plus
配置日志
我们所有的sql
现在是不可见的,我们需要知道是怎么执行的,还有在开发过程中调试以及查找定位问题的时候需要查看。在开发和测试环境可以配置,生产环境不需要配置(因为产生大量不必要的日志,并且对性能有一点影响)。 在application.properties
中假如以下配置
properties复制代码# 配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
重新执行刚才的测试代码
log复制代码Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@5d717f19] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1180105925 wrapping com.mysql.cj.jdbc.ConnectionImpl@2c16677c] 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@5d717f19]
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)
Insert
插入
java复制代码//测试插入
@Test
public void testInsert() {
User user = new User();
user.setName("zbc");
user.setAge(20);
user.setEmail("test@163.com");
//帮我们自动生成id
int result = userMapper.insert(user);
//受影响的行数
System.out.println(result);
//发现,id会自动回填
System.out.println(user);
}
log复制代码Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@18715bb] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1180105925 wrapping com.mysql.cj.jdbc.ConnectionImpl@2c16677c] will not be managed by Spring
==> Preparing: INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
==> Parameters: 1456795944390905857(Long), zbc(String), 20(Integer), test@163.com(String)
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@18715bb]
1
User(id=1456795944390905857, name=zbc, age=20, email=test@163.com)
数据库插入的id
是全局唯一id
,1456795944390905857
是基于雪花算法生成。
主键生成策略
在MybatisPlus
中关于主键生成策略有一个专门的注解(@TableId
)
java复制代码@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface TableId {
String value() default "";
IdType type() default IdType.NONE;
}
IdType
是策略的类型
java复制代码public enum IdType {
AUTO(0),
NONE(1),
INPUT(2),
ASSIGN_ID(3),
ASSIGN_UUID(4);
private final int key;
}
-
AUTO
:数据库
ID
自增
- 需要在实体类字段上添加
@TableId(type = IdType.AUTO)
- 数据库字段也必须是自增。
- 再次测试,我们发现
id
自增1
- 需要在实体类字段上添加
-
NONE
: 默认方案,未设置主键 -
INPUT
:插入前手动设置主键,一旦设置之后就需要配置id
了 -
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
,主键类型Long
,后续建议使用ASSIGN_ID
-
:UUID
32
为UUID
字符串,主键类型String
,后续建议使用ASSIGN_UUID
-
:分布式全局唯一ID_WORKER_STR
ID
,主键类型String
,后续建议使用ASSIGN_ID
雪花算法:snowflake
是Twitter
开源的分布式ID生成算法,结果是一个long
型的ID
。其核心思想是:使用41bit
作为毫秒数,10bit
作为机器的ID
(5个bit
是数据中心,5个bit
的机器ID),12bit
作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096
个 ID
),最后还有一个符号位,永远是0
。可以保证几乎全球唯一。
UUID
:国际标准化组织ISO
提出的一个概念,是一个128bit
的数字,也可以表现为32
个16
进制的字符,中间用-
分割。
- 时间戳+
UUID
版本号,分三段占16
个字符(60bit
+4bit
), Clock Sequence
号与保留字段,占4
个字符(13bit
+3bit
),- 节点标识占
12
个字符(48bit
), - 示例:
3F2504E0-4F89-11D3-9A0C-0305E82C3301
update
更新
java复制代码//测试更新
@Test
public void testUpdate() {
User user = new User();
//通过条件自动拼接动态sql
user.setId(1L);
user.setName("zbc");
user.setAge(14);
//注意:updateById 实际传参是一个 对象,并不是id
int result = userMapper.updateById(user);
System.out.println(result);
System.out.println(user);
}
所有的sql
都是自动帮你配置的
自动填充
创建时间、修改时间,这些字段都是自动化完成的,我们不希望手动更新。(阿里巴巴开发手册:所有的数据库表:gmt_create
、gmt_modified
这两个字段几乎所有的表都需要配置上,而且需要自动化)
数据库级别(不建议使用)
- 在表中新增字段
create_time
、update_time
sql复制代码-- 添加 create_time 设置默认时间 CURRENT_TIMESTAMP
ALTER TABLE `table_name`
ADD COLUMN `create_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间';
-- 修改 create_time 设置默认时间 CURRENT_TIMESTAMP
ALTER TABLE `table_name`
MODIFY COLUMN `create_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间';
-- 添加 update_time 设置默认时间 CURRENT_TIMESTAMP 设置更新时间为 ON UPDATE CURRENT_TIMESTAMP
ALTER TABLE `table_name`
ADD COLUMN `update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间';
-- 修改 update_time 设置默认时间 CURRENT_TIMESTAMP 设置更新时间为 ON UPDATE CURRENT_TIMESTAMP
ALTER TABLE `table_name`
MODIFY COLUMN `update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间';
- 测试插入方法,需要同步实体类字段
java复制代码private Date createTime;
private Date updateTime;
id | name | age | create_time | update_time | |
---|---|---|---|---|---|
1456795944390905861 | zbc | 20 | test@163.com | 2021-11-07 17:29:33 | 2021-11-07 17:29:33 |
- 测试更新方法
id | name | age | create_time | update_time | |
---|---|---|---|---|---|
1456795944390905861 | zbc | 14 | test@163.com | 2021-11-07 17:29:33 | 2021-11-07 17:34:39 |
代码级别
- 删除数据库级别
sql复制代码-- 去掉 create_time 默认值
ALTER TABLE `table_name`
MODIFY COLUMN `create_time` TIMESTAMP NULL COMMENT '创建时间';
-- 去掉 update_time 默认值和默认更新
ALTER TABLE `table_name`
MODIFY COLUMN `update_time` TIMESTAMP NULL COMMENT '修改时间';
- 实体类字段数据增加注解
java复制代码@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
java复制代码//填充类型说明:
public enum FieldFill {
/**
* 默认不处理
*/
DEFAULT,
/**
* 插入时填充字段
*/
INSERT,
/**
* 更新时填充字段
*/
UPDATE,
/**
* 插入和更新时填充字段
*/
INSERT_UPDATE
}
- 编写处理器来处理这个注解
java复制代码package com.zbc.mybatis_plus_demo.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;
/**
* 不要忘记把处理器加到IOC容器中
*
* @author zbc * @date 2021/11/7
*/
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
/**
* 插入时的填充策略
*
* @param metaObject 元数据
*/
@Override
public void insertFill(MetaObject metaObject) {
//setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject)
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
/**
* 更新时的填充策略
*
* @param metaObject 元数据
*/
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
乐观锁
- 悲观锁:认为数据是不安全的,随时可能都有变动,无论什么时候都会加锁。再操作。
- 乐观锁:相对悲观锁而言,乐观锁任务数据是安全的,一般情况下不会出现问题,无论干什么都不去上锁。如果出现冲突,则返回错误信息,交给用户决定。
乐观锁实现方式
- 取出记录时,获取当前
version
- 更新时,带上这个
version
- 执行更新时,
set version = newVersion where version = oldVersion
- 如果
version
不对,就更新失败
使用乐观锁
- 数据库增加
version
字段:
sql复制代码ALTER TABLE `table_name`
ADD COLUMN `version` INT DEFAULT 1 COMMENT '乐观锁';
- 给实体类增加字段
java复制代码@Version
private Integer version;
- 注册乐观锁组件
java复制代码@Configuration
@EnableTransactionManagement
public class MyBatisPlusConfig {
/**
* 用来配置mybatisPlus 插件
*
* @return 拦截器
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
- 测试成功
java复制代码//测试乐观锁成功
@Test
public void testOptimisticLocker() {
//1.先获取用户信息
User user = userMapper.selectById(1L);
//2.修改用户信息
user.setName("test");
user.setAge(25);
//3.执行更新操作
userMapper.updateById(user);
}
log复制代码Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74431b9c] was not registered for synchronization because synchronization is not active
2021-11-07 18:41:52.417 INFO 21884 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2021-11-07 18:41:52.792 INFO 21884 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
JDBC Connection [HikariProxyConnection@1729992636 wrapping com.mysql.cj.jdbc.ConnectionImpl@774f2992] will not be managed by Spring
==> Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id=?
==> Parameters: 1(Long)
<== Columns: id, name, age, email, create_time, update_time, version
<== Row: 1, zbc, 14, test1@baomidou.com, null, 2021-11-06 22:28:53, 1
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74431b9c]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74ce7fdf] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@33779587 wrapping com.mysql.cj.jdbc.ConnectionImpl@774f2992] will not be managed by Spring
==> Preparing: UPDATE user SET name=?, age=?, email=?, update_time=?, version=? WHERE id=? AND version=?
==> Parameters: test(String), 25(Integer), test1@baomidou.com(String), 2021-11-07 18:41:52.93(Timestamp), 2(Integer), 1(Long), 1(Integer)
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@74ce7fdf]
- 测试失败
java复制代码//测试乐观锁失败
@Test
public void testOptimisticLocker2() {
//线程1
User user = userMapper.selectById(1L);
user.setName("test1");
//模拟线程2,先一步执行修改操作
User user2 = userMapper.selectById(1L);
user2.setName("test2");
userMapper.updateById(user2);
//如果没有乐观锁,就会覆盖线程2的值
userMapper.updateById(user);
}
log复制代码Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@f2fb225] was not registered for synchronization because synchronization is not active
2021-11-07 18:46:35.982 INFO 13340 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2021-11-07 18:46:36.457 INFO 13340 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
JDBC Connection [HikariProxyConnection@2001676690 wrapping com.mysql.cj.jdbc.ConnectionImpl@602298b] will not be managed by Spring
==> Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id=?
==> Parameters: 1(Long)
<== Columns: id, name, age, email, create_time, update_time, version
<== Row: 1, test, 25, test1@baomidou.com, null, 2021-11-07 18:41:53, 2
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@f2fb225]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@13908f9c] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@849031967 wrapping com.mysql.cj.jdbc.ConnectionImpl@602298b] will not be managed by Spring
==> Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id=?
==> Parameters: 1(Long)
<== Columns: id, name, age, email, create_time, update_time, version
<== Row: 1, test, 25, test1@baomidou.com, null, 2021-11-07 18:41:53, 2
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@13908f9c]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@642c72cf] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@966457052 wrapping com.mysql.cj.jdbc.ConnectionImpl@602298b] will not be managed by Spring
==> Preparing: UPDATE user SET name=?, age=?, email=?, update_time=?, version=? WHERE id=? AND version=?
==> Parameters: test2(String), 25(Integer), test1@baomidou.com(String), 2021-11-07 18:46:36.63(Timestamp), 3(Integer), 1(Long), 2(Integer)
<== Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@642c72cf]
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6aa6c17] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@1196877260 wrapping com.mysql.cj.jdbc.ConnectionImpl@602298b] will not be managed by Spring
==> Preparing: UPDATE user SET name=?, age=?, email=?, update_time=?, version=? WHERE id=? AND version=?
==> Parameters: test1(String), 25(Integer), test1@baomidou.com(String), 2021-11-07 18:46:36.68(Timestamp), 3(Integer), 1(Long), 2(Integer)
<== Updates: 0
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6aa6c17]
Select
查询
java复制代码//测试查询
@Test
public void testSelect() {
//根据id查询
User user = userMapper.selectById(1L);
System.out.println(user);
//批量查询
List<User> users = userMapper.selectBatchIds(Arrays.asList(2L, 3L));
users.forEach(System.out::println);
//条件查询 map
Map<String, Object> map = new HashMap<>();
map.put("name", "zbc");
List<User> users1 = userMapper.selectByMap(map);
users1.forEach(System.out::println);
}
log复制代码//根据id查询
==> Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id=?
==> Parameters: 1(Long)
//批量查询
==> Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE id IN ( ? , ? )
==> Parameters: 2(Long), 3(Long)
//条件查询 map
==> Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user WHERE name = ?
==> Parameters: zbc(String)
分页查询
实现方式
- 原始分页
limit
- 第三方插件
pageHelper
MyBatisPlus
自带分页功能
使用分页查询
- 增加分页插件,分页插件需要在乐观锁插件前面添加
java复制代码@Configuration
@EnableTransactionManagement
public class MyBatisPlusConfig {
/**
* 用来配置mybatisPlus 插件
*
* @return 拦截器
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//分页插件,注意数据库的类型
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
//乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
- 直接使用
Page
对象即可
java复制代码//测试分页查询
@Test
public void testPage() {
//参数1:页码,参数2:每页记录数
Page<User> page = new Page<>(1, 5);
userMapper.selectPage(page, null);
page.getRecords().forEach(System.out::println);
}
log复制代码//先求总记录数
==> Preparing: SELECT COUNT(*) AS total FROM user
==> Parameters:
<== Columns: total
<== Row: 12
<== Total: 1
//然后分页查询
==> Preparing: SELECT id,name,age,email,create_time,update_time,version FROM user LIMIT ?
==> Parameters: 5(Long)
//输出结果
User(id=1, name=test2, age=25, email=test1@baomidou.com, createTime=null, updateTime=Sun Nov 07 18:46:37 CST 2021, version=3)
User(id=2, name=Jack, age=20, email=test2@baomidou.com, createTime=null, updateTime=Sat Nov 06 22:28:53 CST 2021, version=1)
User(id=3, name=Tom, age=28, email=test3@baomidou.com, createTime=null, updateTime=Sat Nov 06 22:28:53 CST 2021, version=1)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com, createTime=null, updateTime=Sat Nov 06 22:28:53 CST 2021, version=1)
User(id=5, name=Billie, age=24, email=test5@baomidou.com, createTime=null, updateTime=Sat Nov 06 22:28:53 CST 2021, version=1)
12
delete
删除
java复制代码//测试删除
@Test
public void testDelete() {
//单个id删除
userMapper.deleteById(1456795944390905863L);
//批量id删除
userMapper.deleteBatchIds(Arrays.asList(1456795944390905861L, 1456795944390905862L));
//条件删除 map
Map<String, Object> map = new HashMap<>();
map.put("name", "123");
userMapper.deleteByMap(map);
}
log复制代码//单个id删除
==> Preparing: DELETE FROM user WHERE id=?
==> Parameters: 1456795944390905863(Long)
<== Updates: 1
//批量id删除
==> Preparing: DELETE FROM user WHERE id IN ( ? , ? )
==> Parameters: 1456795944390905861(Long), 1456795944390905862(Long)
<== Updates: 2
//条件删除 map、
==> Preparing: DELETE FROM user WHERE name = ?
==> Parameters: 123(String)
<== Updates: 1
逻辑删除
- 物理删除:从数据库中直接删除
- 逻辑删除:从数据库中没有删除,而是通过一个变量来让它失效
应用场景:管理员可以查看被删除的记录,防止数据丢失,类似回收站。
使用
- 增加
deleted
字段
sql复制代码ALTER TABLE `table_name`
ADD COLUMN `deleted` INT(1) DEFAULT 1 COMMENT '逻辑删除';
- 实体类增加属性
java复制代码@TableLogic
private Integer deleted;
- 增加逻辑删除配置
properties复制代码# 逻辑删除
# 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置@TableLogic)
mybatis-plus.global-config.db-config.logic-delete-field=flag
# 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-delete-value=1
# 逻辑未删除值(默认为 0)
mybatis-plus.global-config.db-config.logic-not-delete-value=0
- 测试
java复制代码//测试逻辑删除
@Test
public void testLogicDelete(){
userMapper.deleteById(1456795944390905864L);
User user = userMapper.selectById(1456795944390905864L);
System.out.println(user);
}
log复制代码==> Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0
==> Parameters: 1456795944390905864(Long)
<== Updates: 1
==> Preparing: SELECT id,name,age,email,create_time,update_time,version,deleted FROM user WHERE id=? AND deleted=0
==> Parameters: 1456795944390905864(Long)
<== Total: 0
null
本质上是更新操作并不是删除,而是修改操作。 查询的时候我们发现已经不能查找到了
条件构造器(重点)
条件构造器主要用来写一些复杂的sql
注意事项
不支持以及不赞成在 RPC
调用中把 Wrapper
进行传输
wrapper
很重- 传输
wrapper
可以类比为你的controller
用map
接收值(开发一时爽,维护火葬场) - 正确的
RPC
调用姿势是写一个DTO
进行传输,被调用方再根据DTO
执行相应的操作
拼接方法
查询方法 | 说明 | 示例 |
---|---|---|
eq | 等于 = | eq("username", "admin") —> username = 'admin' |
ne | 等于 = | ne("username", "admin") —> username <> 'admin' |
gt | 大于 > | gt("age", "18") —> age > 18 |
ge | 大于等于 >= | ge("age", "18") —> age >= 18 |
lt | 小于 < | lt("age", 18) —> age < 18 |
le | 小于等于 <= | le("age", 18) —> age <= 18 |
between | 在值1和值2之间 | between("age", 18, 24) —> age BETWEEN 18 AND 24 |
notBetween | 不在值1和值2之间 | notBetween("age", 18, 24) —> age NOT BETWEEN 18 AND 24 |
like | 模糊查询 | like("name", "张") —> name LIKE '%张%' |
notLike | 不模糊查询内 | notLike("name", "张") —> name NOT LIKE '%张%' |
likeLeft | 左模糊查询 | likeLeft("name", "三") —> name LIKE '%三' |
likeRight | 右模糊查询内 | likeRight("name", "张") —> name LIKE '张%' |
isNull | 字段为空 | isNull("name") —> name IS NULL |
isNotNull | 字段不为空 | isNotNull("name") —> name IS NOT NULL |
in | 在集合内 | in("age", 16,17,18) —> age IN (16,17,18) |
notIn | 在集合内 | notIn("age", 16,17,18) —> age NOT IN (16,17,18) |
inSql | 子查询 | in("id", "SELECT id FROM table WHERE id < 3") —> id IN (SELECT id FROM table WHERE id < 3) |
notInSql | 子查询 | notInSql("id", "SELECT id FROM table WHERE id < 3") —> id ONT IN (SELECT id FROM table WHERE id < 3) |
groupBy | 分组 | groupBy("age", "id") —> GROUP BY id, name |
orderByAsc | 字段正序 | orderByAsc("age") —> ORDER BY age ASC |
orderByDesc | 字段倒序 | orderByDesc("age") —> ORDER BY age DESC |
having | 分组筛选 | having("sum(age) > 10") —>HAVING SUM(age) > 10 |
or | 或 | eq("id", 1).or().eq("name", "张三") —>id = 1 OR name = '张三' |
and | 且 | eq("id", 1).and().eq("name", "张三") —>id = 1 AND name = '张三' |
exists | 存在 | exists("SELECT id FROM table WHERE age = 1") —>EXISTS (SELECT id FROM table WHERE age = 1) |
notExists | 存在 | notExists("SELECT id FROM table WHERE age = 1") —>NOT EXISTS (SELECT id FROM table WHERE age = 1) |
isNotnull
和ge
演示
java复制代码@Test
void test1() {
//查询name不为空并且邮箱不为空,年龄大于15
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.
isNotNull("name").
isNotNull("email").
ge("age", 15);
userMapper.selectList(wrapper).forEach(System.out::println);
}
eq
和selectOne
演示
java复制代码@Test
void test2() {
//查询name=test2
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "test2");
//selectOne 只能查询一个,假如出现多个结果会报错
User user = userMapper.selectOne(wrapper);
System.out.println(user);
}
between
和selectCount
演示
java复制代码@Test
void test3() {
//查询年龄在[20,30]之间的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age", 20, 30);
long count = userMapper.selectCount(wrapper);
System.out.println(count);
}
notLike
和likeRight
和selectMaps
演示
java复制代码@Test
void test4() {
//name不包含bc,并且email以t开头(t%)
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.
notLike("name", "bc").
likeRight("email", "t");
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
inSql
和selectObjs
和子查询演示
java复制代码@Test
void test5() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.inSql("id", "SELECT id FROM user WHERE id < 10");
List<Object> objects = userMapper.selectObjs(wrapper);
objects.forEach(System.out::println);
}
orderByDesc
演示
java复制代码@Test
void test6() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("id");
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}