官方网站 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
二、入门案例
运行环境 java17 springboot 3.x mybatis-plus 3.5.7
1. 数据库的搭建
CREATE DATABASE `mybatis_plus` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use `mybatis_plus`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
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');
2. 创建springboot项目引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</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.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
mapper层继承BaseMapper,指名操作的实体类
// 启动类
@SpringBootApplication
@MapperScan("com/study/mapper")
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
// 测试类
@SpringBootTest
class MybatisPlusApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
void contextLoads() {
List<User> users = userMapper.selectList();
for (User user: users) {
System.out.println(user);
}
}
遇见 nvalid value type for attribute 'factoryBeanObjectType': java.lang.String,手动升级mybatis-spring
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
</dependency>
添加日志
mybatis-plus: configuration: # 标准输出 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
三、mybatis-plus的CRUD
1. 增加功能 insert
public void testInsert() {
// 新增功能
User user = new User();
user.setName("zhangsan");
user.setAge(23);
user.setEmail("liubo_china@163.com");
// insert into user values(?,?,?,?)
userMapper.insert(user);
System.out.println(user.getId());
}
2. 删除功能 delete
@Test
public void testDelete() {
// 通过id删除 DELETE FROM user WHERE id=?
// int i = userMapper.deleteById(1838410087146074114L);
// System.out.println("result :" + i);
// 多个id批量删除 DELETE FROM user WHERE id IN ( ? , ? )
List<Long> ids = Arrays.asList(1L, 2l);
int i = userMapper.deleteByIds(ids);
System.out.println(i);
}
3. 修改功能 update
public void testUpdate() {
// 根据ID修改
User user = new User();
user.setId(1838409255742861313L);
user.setName("长子");
int i = userMapper.updateById(user);
}
4. 查询功能 select
public void testSelect() {
// 查询全部
List<User> users = userMapper.selectList(null);
// 通过id 查询单条记录
User user = userMapper.selectById();
// 根据多个id 查询多条数据
userMapper.selectBatchIds();
}
5. 自定义功能接口
配置XML文件,自定义sql
5. service接口
IService 是 MyBatis-Plus 提供的一个通用 Service 层接口,它封装了常见的 CRUD 操作,包括插入、删除、查询和分页等。通过继承 IService 接口,可以快速实现对数据库的基本操作,同时保持代码的简洁性和可维护性。
IService 接口中的方法命名遵循了一定的规范,如 get 用于查询单行,remove 用于删除,list 用于查询集合,page 用于分页查询,这样可以避免与 Mapper 层的方法混淆。
提示
- 泛型
T
为任意实体对象 - 建议如果存在自定义通用 Service 方法的可能,请创建自己的
IBaseService
继承Mybatis-Plus
提供的IService
基类 - 对象
Wrapper
为 条件构造器
具体方法实现 见官网 IService
7. 常用注解
@TableName | 表示实体类与数据表名之间的映射 |
@TableId(value = ) | 表示实体类的属性为主键,value属性代表与其关联的表中字段名 |
@Tablefield @Table | 关联字段 |
四. 条件构造器
1. 条件查询
public void testWrapper() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
// select * from user where name like %o% and age between(20,30) and email not null
wrapper.like("name","%o%")
.between("age",20,30)
.isNotNull("email");
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
2. 指定字段
// 查询指定字段
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 查询name age两列
wrapper.select("name", "age");
List<User> users = userMapper.selectList(wrapper);
for (User user : users) {
System.out.println(user);
}
3. 子查询
public void testInSql() {
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
//select id,name,email,age from user where (id in (select id from user where id <4 ))
userQueryWrapper.inSql("id","select id from user where id <4 ");
List<User> users = userMapper.selectList(userQueryWrapper);
users.forEach(System.out::println);
}
4. UpdateWrapper修改
@Test
public void testUpdateQueryWrapper() {
// 使用updateWrapper完成修改操作 查询名字含 n 年龄20 30 邮箱不为空的 修改
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.like("name","n").and(i -> i.gt("age",20)) // name > 20
.or().isNull("email");
// 设置要修改的指
updateWrapper.set("name","小黑");
updateWrapper.set("age",99);
// UPDATE user SET name=?,age=? WHERE (name LIKE ? AND (age > ?) OR email IS NULL)
int result = userMapper.update(updateWrapper);
System.out.println("result :" + result);
}
5. 使用Condition拼接条件
// condition 条件,可以写一个条件判断
userQueryWrapper.like(StringUtils.isNotBlank(name),"name","n")
.ge(age != null,"age",20);
6. lambdaQueryWrapper
// userQueryWrapper.like(StringUtils.isNotBlank(name),"name","n").ge(age != null,"age",20);
lambdaQueryWrapper.like(StringUtils.isNotBlank(name),User::getName,"n").ge(age != null,User::getAge,20);
五、分页功能
1. 配置拦截器 mybatisPlusInterceptor
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
2. 使用Page对象实现分页
public void testPage() {
// 当前页 一页几条内容
Page<User> userPage = new Page<>(1,2);
Page<User> selectPage = userMapper.selectPage(userPage, null);
selectPage.getRecords().forEach(System.out::println); // 获得当前页的记录
System.out.println(selectPage.getCurrent()); // 当前页
System.out.println(selectPage.getSize()); // 当前页的条数
System.out.println(selectPage.getTotal()); // 总记录数
}
如果没有效果,是因为SpringBoot启动类未扫描到配置类。
@SpringBootApplication(scanBasePackages = {"com.study.config"})
@MapperScan("com/study/mapper")
public class MybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisPlusApplication.class, args);
}
}
六、乐观锁和悲观锁
乐观锁
- 定义:乐观锁假设在大多数情况下不会发生冲突,因此在进行操作时不加锁,而是在提交时检查数据是否被其他事务修改过。
- 实现方式:通常使用版本号或时间戳来实现。当更新数据时,先检查版本号,如果版本号未改变,则可以成功更新;如果已改变,则需要重试或放弃操作。
- 优点:减少了锁的竞争,提高了系统的并发性能。
- 缺点:在高冲突的场景下,可能频繁失败,导致重试次数增加。
乐观锁的实现通常包括以下步骤:
- 读取记录时,获取当前的版本号(version)。
- 在更新记录时,将这个版本号一同传递。
- 执行更新操作时,设置
version = newVersion
的条件为version = oldVersion
。 - 如果版本号不匹配,则更新失败。
配置类
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 乐观锁插件
return interceptor;
}
实体类表示版本的属性添加@version注解
@Data
public class User {
private Long id;
private String name;
private String email;
private Integer age;
@Version
private Integer version;
}
悲观锁
- 定义:悲观锁假设在大多数情况下会发生冲突,因此在进行任何操作之前都会对所需的数据加锁,以避免其他事务的干扰。
- 实现方式:通过数据库的行锁或表锁等机制实现。在操作开始时加锁,直到操作完成后再释放锁。
- 优点:保证了数据的一致性,适合高冲突的场景。
- 缺点:可能导致性能下降,因为锁会增加等待时间,并且容易出现死锁。
七、通用枚举
将java代码中的枚举类型,指定属性添加到数据库中。如在数据表user中Sex属性为int类型,1代表男,2代表女。枚举属性使用 @EnumValue
注解,指定枚举值在数据库中存储的实际值。支持枚举类中的任意字段,如序号或编码。
public enum SexEnum {
MALE(1,"男"),
FEMALE(2,"女");
@EnumValue
private Integer sex;
private String sexName;
SexEnum(Integer sex, String sexName) {
this.sex = sex;
this.sexName = sexName;
}
}
八、代码生成器 代码生成器
九、mybatisX插件的使用
通过模板 快速生成