MyBatis-Plus使用

MyBatis-plus 学习笔记

1.创建工程

创建一个SpringBoot的工程,此前的笔记作为参考

2.基本配置

导入依赖

1
2
3
4
5
6
<!--MyBatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.2</version>
</dependency>

application.yaml配置

log-impl: 的配置可以控制是否输出sql及结果到日志中

1
2
3
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3.BaseMapper

继承BaseMapper<T>

当我们使用userMapper接口时,可以让其继承BaseMapper<User>.

BaseMapper提供了许多方法供我们操作数据库,大多数操作无需再在UserMapper.xml中写SQL语句

注入对象

在测试类中,注入UserMapper对象,然后就可以调用BaseMapper中的方法进行操作

测试添加

注意这里User的构造器没有传入id,MyBatis-plus会通过雪花算法自动生成id

如果需要自动生成id , id的数据类型数据库对应的类型至少是bigint , 实体类中主键ID必须是Long类型,long、int、Integer无法实现id自动生成。

测试删除

主要是4个方法:delete()、deleteById()、deleteByMap()、deleteBatchIds()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    @Test
public void testDelete(){
//删除所有
userMapper.delete(null);

//根据id删除,在id后加上L表示long类型,不然会报错
// userMapper.deleteById(1700427938523058178L);

//根据其他值删除
// Map<String,Object> map = new HashMap<String,Object>();
// map.put("name","jack");
// map.put("age","18");
// userMapper.deleteByMap(map);

//根据多个id删除
// List<Long> list = Arrays.asList(1L, 2L, 3L);
// userMapper.deleteBatchIds(list);
}

测试修改

测试查询

主要是4个方法:selectList()、selectById()、selectBatchIds()、selectByMap()

当业务需要自定义查询逻辑时,可以像mybatis中一样,通过UserMapper.xml中的sql语句来完成操作.

MyBatisPlus中映射文件的路径默认如以下,也就是如果直接在resources\mapper\..下创建映射文件,可以不在application.yaml中配置mapper-locations

4.通用Service接口

创建接口和实现类

创建UserService接口,并创建UserServiceImpl实现类

两者关系如下图

UserService继承了IService ,并指定泛型为<User>实体 (如下)

UserServiceImpl实现UserService接口,继承ServiceImpl并指定泛型为mapper接口和实体<UserMapper,User> (如下)

测试

测试以查询总条数和批量插入的方法为例

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
package com.example.mybatis_plus;

import com.example.mybatis_plus.pojo.User;
import com.example.mybatis_plus.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.ArrayList;
import java.util.List;

@SpringBootTest
public class MyBatisPlusServiceTest {
@Autowired
UserService userService;

@Test
public void testCount(){
long count = userService.count();
System.out.println("总记录数"+count);
}

@Test
public void testInsertMore(){
List<User> list = new ArrayList<User>();
for (int i = 0; i < 5; i++) {
User user = new User("insertMore"+i,19,"testInsertMore@test.com");
list.add(user);
}
userService.saveBatch(list);
}

}

5.常用注解

@TableName

一般情况下MybatisPlus默认会将表名设置为实体的小写,如实体User对应表名user.

当数据库中的表名和实体类的名字对不上时,比如数据库中的表叫t_user而实体类为User时,就需要使用注解或者全局配置来指定表名

注解

在User类上加@TableName(“t_user”)即可

全局配置

在application.yaml中按下图配置,可以对所有表名加上指定前缀(这里是 t_ )

@TableId

当主键名为 uid 而不是 id 时,若数据库没有给主键设置自增长,MybatisPlus无法将uid识别为主键并进行生成,所以此时需要使用**@TableId注解,将uid**指定为主键

value属性

当数据库中的字段名和实体类的属性名不一样时,可以使用value进行指定

type属性

type属性可以设置主键id的生成方式,默认使用雪花算法生成,如果需要使用自增长,则需要**在数据库中将主键设置为自增长并同时将type设置为 IdType.AUTO **

@TableField

value属性

假如数据库中字段名为user_name,而实体类属性名为userName,这种情况可以正常运行,因为MybatisPlus会将下划线改为驼峰命名。

但如果数据库中字段名为user_name,而实体类属性名为name,则需要使用**@TableField(“user_name”)**来指定属性所对应的字段名

fill属性(自动填充公共字段)

像下面这样添加fill属性,在插入和更新时,会自动填充该字段

1
2
3
4
5
6
7
8
9
//创建时间
@JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime createTime;

//更新时间
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;

关于填充的内容和指定的字段名需要在处理器中配置

按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
@Component
public class MyMetaObjecthandler implements MetaObjectHandler {

@Override
public void insertFill(MetaObject metaObject) {
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime", LocalDateTime.now());
}

@Override
public void updateFill(MetaObject metaObject) {
metaObject.setValue("updateTime",LocalDateTime.now());
}
}

@TableLogic

@TableLogic可以实现数据的逻辑删除而不是物理删除

使用方法:

在数据库中增加一个is_deleted字段,默认为0。在实体类中添加一个isDeleted属性,并带上**@TableLogic**注解

这样就可以在执行删除操作时,自动将is_deleted的值从0修改为1,而不是直接的物理删除

6.条件构造器

QueryWrapper

基本用法

声明一个QueryWrapper对象,wapper.xxx(),看方法名就能清楚所代表的条件

考虑优先级

当需要某些操作优先执行的时候可以用**.and(i->i.xxx())**的形式

仅查询部分字段

嵌套查询

将嵌套的子查询写在inSql中

UpdateWrapper

UpdateWrapper的使用方法和QueryWrapper很类似,仅仅是updateWrapper在完成查询后使用.set()对值进行修改

Condition组装条件

日常开发中可能会遇到传入参数为空的情况

这个时候就需要使用condition来决定是否使用该条件

如下图,当name不为空的时候,才会进行对name的模糊查询

beginAge和endAge也如此类推

LambdaQueryWrapper

LambdaQueryWrapperQueryWrapper的区别在于字段名的写法

LambdaQueryWrapper中字段名用 User::getName 的方式与实体类直接挂钩

QueryWrapper中字段名直接用字符串

使用LambdaQueryWrapper可以防止字段名输入错误,也能在修改字段名时减少需要修改的参数

LambdaUpdateWrapper

LambdaQueryWrapper类似,所有原先使用字符串作为字段名参数的地方全都改成Lambda的方式

7.分页查询

配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Configuration
public class MyBatisPlusConfig {

/*
* 配置分页拦截器
* 指定数据库方言为MySQL
* */
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

return interceptor;
}

}

使用

首先声明一个Page<实体类>对象,构造器参数分别为当前页码每页条数

使用selectPage(),传入Page对象和条件构造器即可

8.乐观锁

配置类

使用

在数据库中添加一个version的字段,默认为0

然后在对应的实体类中加入version属性,并附上@Version注解

结果

像上面这样配置完成后,像下面这种情况就不会再出现

加上了乐观锁之后,每一次改动,version都会+1,如果改动提交后,发现在提交前已有其他用户提交了其他改动,则version会对应不上,那么此次提交就会失败。

比如上面这种情况,小李小王同时进行修改,但小李先提交了,所以小王的这一次修改会失败。

9.通用枚举

在数据库中设置一个测试枚举的属性sex

创建枚举类,将需要存入数据库的属性用**@EnumValue**标识

设置好后,我们新增user时,使用枚举的属性时,就会自动向数据库插入我们之前所标识的属性

10.快速生成

创建一个新项目

连接数据库

选择一个表,右键点击MybatisX-Generator. (此前需要先安装MybatisX插件)

填入相关信息

完成后将会自动生成所需文件