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 操作智能分析阻断,也可自定义拦截规则,预防误操作
使用SpringBoot初始化
导入依赖
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<!-- MyBatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
:: 使用mybatis-plus 可以节省大量代码,
连接数据库
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://localhost:3306/xxx?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: xxx
password: xxx
mybatis-plus:
# 搜索指定包别名
typeAliasesPackage: com.ruoyi.**.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 加载全局的配置文件
# configLocation: classpath:mybatis/mybatis-config.xml
configuration:
map-underscore-to-camel-case: false
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: isDelete # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
创建数据表
CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
`userAccount` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '账号',
`userPassword` varchar(512) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密码',
`unionId` varchar(256) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '微信开放平台id',
`mpOpenId` varchar(256) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '公众号openId',
`userName` varchar(256) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '用户昵称',
`userAvatar` varchar(1024) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '用户头像',
`userProfile` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '用户简介',
`userRole` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'user' COMMENT '用户角色:user/admin/ban',
`createTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updateTime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`isDelete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除',
PRIMARY KEY (`id`),
KEY `idx_unionId` (`unionId`)
) ENGINE=InnoDB AUTO_INCREMENT=1669963950083579906 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户'
创建pojo
package com.kkkkkba.search.model.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 用户
*
*/
@TableName(value = "user")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private Long id;
/**
* 用户账号
*/
private String userAccount;
/**
* 用户密码
*/
private String userPassword;
/**
* 开放平台id
*/
private String unionId;
/**
* 公众号openId
*/
private String mpOpenId;
/**
* 用户昵称
*/
private String userName;
/**
* 用户头像
*/
private String userAvatar;
/**
* 用户简介
*/
private String userProfile;
/**
* 用户角色:user/admin/ban
*/
private String userRole;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 是否删除
*/
@TableLogic
private Integer isDelete;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
创建Mapper
public interface UserMapper extends BaseMapper<User> {
}
使用Mybatis-Plus
在主启动类上配置Mapper接口
@MapperScan("com.kkkkkba.search.mapper")
CRUD
查询
@Test
void MyTest (){
List<User> posts = userMapper.selectList(null);
posts.forEach(System.out::println);
}
插入
@Test
void Insert (){
User user = new User();
user.setUserName("kba");
int insert = userMapper.insert(user);
System.out.println(insert);
}
主键生成策略
默认 ASSIGN_ID
雪花算法
雪花算法(snowflake)最早是twitter内部使用分布式环境下的唯一ID生成算法。
雪花算法使用64位long类型的数据存储id
自增 AUTO
实体类配置@TableId(type = IdType.AUTO)
其他
public enum IdType {
AUTO(0), // 自增id
NONE(1), // 未设置主键
INPUT(2), // 手动输入
ASSIGN_ID(3), //默认全局唯一id
ASSIGN_UUID(4); // 全局唯一id uuid
}
更新
@Test
void update(){
User user = new User();
user.setId(1669963950083579905);
user.setUserAccount("kbaaaaa");
userMapper.updateById(user);
}
自动填充
创建时间,修改时间,应该自动化完成
:::info
方式一 数据库级别
:::
- 表中新增字段 create_time update_time
- 修改数据库字段
:::info
方式二 代码级别
:::
- 删除数据库的默认值
- 实体类字段属性添加字段
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date createTime;
/**
* 更新时间
*/
@TableField(fill = FieldFill.UPDATE)
private Date updateTime;
- 编写处理器实现注释
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.strictInsertFill(createTime, new Date(), metaObject);
this.strictUpdateFill(updateTime, new Date, metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(updateTime, new Date, metaObject);
}
}
乐观锁
乐观锁,任何时候都不会上锁,出现问题在更新值测试
悲观锁: 任何时候都会上锁,再去操作
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
- 取出记录时,获取当前 version
- 更新时,带上这个 version
- 执行更新时, set version = newVersion where version = oldVersion
- 如果 version 不对,就更新失败
测试
- 给数据库增加version字段 默认值为1
- 实体类增加对应的字段并增加@Version注解
- 注册组件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
- 测试
// 测试乐观锁成功案例
@Test
void OptimisticLocker (){
User user1 = new User();
user1.setId(1669963950083579905L);
user1.setUserName("乐观锁");
userMapper.updateById(user1);
}
// 测试乐观锁失败
@Test
void OptimisticLockerError (){
User user1 = new User();
user1.setId(1669963950083579905L);
user1.setUserName("乐观锁,线程1");
User user2 = new User();
user2.setId(1669963950083579905L);
user2.setUserName("乐观锁,线程2");
// 模拟插队任务
userMapper.updateById(user2);
userMapper.updateById(user1); // 如果没有乐观锁就会覆盖插队线程的值
}
查询-Map
@Test
void Select1(){
User user = userMapper.selectById(1669963950083579905L);
System.out.println(user);
}
@Test
void Select2(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(1669963950083579905L, 1664885844209651713L));
users.forEach(System.out::println);
}
// Map 查询
@Test
void Select3(){
HashMap<String, Object> map = new HashMap<>();
// 自定义查询内容
map.put("name","kkkkkba");
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
分页插件
- 配置分页插件
@Configuration
@MapperScan("scan.your.mapper.package")
public class MybatisPlusConfig {
/**
* 添加分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多个插件,切记分页最后添加
//interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType
return interceptor;
}
}
- 测试分页查询
@Test
void Select4(){
// 参数一 当前页
// 参数二 页面大小
Page<User> page = new Page<>(1,2);
Page<User> pages = userMapper.selectPage(page, null);
pages.getRecords().forEach(System.out::println);
System.out.println(pages.getTotal());
}
删除
@Test
void Del(){
// id删除
userMapper.deleteById(1669963950083579905L);
// 批批量删除
userMapper.selectBatchIds(Arrays.asList(1669963950083579905L, 1664885844209651713L))
// Map删除
。。。。。。。
}
逻辑删除
物理删除: 从数据库中直接删除
逻辑删除: 在数据库中没有删除,定义一个变量让他失效
应用场景:管理员可以查看被删除的记录
- 在数据库中定义deleted字段
- 实体类中添加对应字段,添加@TableLogic注释
- 配置yml
mybatis-plus:
global-config:
db-config:
logic-delete-field: isDelete # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
条件查询器
Wrapper 复杂SQL查询时使用
@Test
void Select1(){
// 查询 unionId 不为空的user
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("unionId")
.eq("userRole","user");
userMapper.selectList(wrapper).forEach(System.out::println );
}
@Test
void Select2(){
// 查询 userName == 赵今麦
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("userName","赵今麦");
userMapper.selectList(wrapper).forEach(System.out::println );
}
@Test
void Select3(){
// 查询 范围
QueryWrapper<User> wrapper = new QueryWrapper<>();
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, 2023);
calendar.set(Calendar.MONTH, 5); // 月份从0开始,所以1月是0
calendar.set(Calendar.DAY_OF_MONTH, 4);
Date customDate = calendar.getTime();
wrapper.between("createTime",customDate,new Date());
userMapper.selectList(wrapper).forEach(System.out::println );
}
@Test
void Select4(){
// 查询 用户名不包含kba 且 以l开头i结尾
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.notLike("userAccount","kba")
.likeLeft("userAccount","l")
.likeRight("userAccount","i");
userMapper.selectList(wrapper).forEach(System.out::println );
}
@Test
void Select5(){
// 查询 用户名不包含kba 且 以l开头i结尾
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.inSql("id","select id from user where userAccount like '%kba%' ");
userMapper.selectList(wrapper).forEach(System.out::println );
}
@Test
void Select6(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("id");
userMapper.selectList(wrapper).forEach(System.out::println );
}
QueryWrapper
allEq 全部匹配 .allEq(Map)
eq .eq(“name”,“value”) -> name = value
ne .ne(“name”,“value”) -> name <> value
gt .gt(“age”,10) -> age >10
ge .ge(“age”,10) -> age >=10
lt .lt(“age”,10) -> age < 10
le .le(“age”,10) -> age <= 10
between 属于ab之间 .between(“age”,a,b)
notBetween 不属于ab之间 ,notBetween(“age”,a,b)
like .like(“name”,“kba”) -> name like “%kba%”
notLike .notlike(“name”,“kba”) -> name not like “%kba%”
likeLeft .likeLeft(“name”,“kba”) -> name like “%kba”
likeRight .likeRight(“name”,“kba”) -> name like “kba%”
notLikeLeft .notlikeLeft(“name”,“kba”) -> name like “%kba”
notLikeRight .notlikeRight(“name”,“kba”) -> name like “kba%”
isNull .isNull(“name”) -> name is null
isNotNull isNotNull(“name”) -> name is not null
in in(“age”,{11,12,13}) -> age in (11,12,13)
notIn notIn(“age”,{11,12,13}) -> age not in (11,12,13)
inSql sql
notInSql
groupBy .group(“gender”,“id”) --> group by name,id
orderByAsc .orderByAsc(“id”) --> oder by id Asc
orderByDesc .orderByDesc(“id”) --> oder by id Desc
orderBy .orderBy(“id”) --> oder by id
or .eq(“age”,1).or().eq(“name”,“kba”) --> age = 1 or name = “kba”
and .eq(“age”,1).and().eq(“name”,“kba”) --> age = 1 and name = “kba”
nested
apply
last
exists
notExists