MybatisPlus二级映射和关联对象ResultMap

MybatisPlus二级映射和关联对象ResultMap

在我们的教程中,我们设计了一个课程内容的数据结构,包含章节和相关资源。这种结构非常适合在线教育平台或电子学习系统,其中课程内容需要被组织成不同的章节和子章节,每个子章节可能关联特定的学习资源。

这将是一个很好的示例来展示 MyBatis 中如何使用一对多(<collection>)和一对一(<association>)映射。

一、业务背景

1. 数据库表结构

  1. 章节表 (chapter)
    这张表包含所有章节的信息,其中包括大章节和小章节。大章节作为容器,可以包含多个小章节。
    • id (章节ID)
    • parent_id (父章节ID,用于区分大章节和小章节)
    • name (章节名称)
    • courseId (课程ID)
language-java
1
2
3
4
5
6
7
public class Chapter {

private Long id;
private Long parentId;
private String name;
private Long courseId;
}
  1. 资源表 (resource)
    这张表包含与小章节相关联的资源信息。
    • id (资源ID)
    • section_id (章节ID,关联到章节表)
    • name (资源名称)
language-java
1
2
3
4
5
6
public class Resource {

private Long id;
private Long sectionId;
private String name;
}

2. 需求

要求根据courseId查询出指定课程的信息,包括大章节、小章节、资源,并以一定结构返回,比如

language-json
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
34
35
[
{

"id": 1,
"parentId": null,
"name": "Chapter 1",
"courseId": 100,
"subChapters": [
{

"id": 11,
"parentId": 1,
"name": "Section 1.1",
"courseId": 100,
"resource": {

"id": 101,
"sectionId": 11,
"name": "Introduction Video"
}
},
{

"id": 12,
"parentId": 1,
"name": "Section 1.2",
"courseId": 100,
"resource": null
}
],
"resource": null
}
// other...
]

所以我们定义一个Dto如下

language-java
1
2
3
4
5
6
7
public class ChapterDto extends Chapter {

private List<ChapterDto> subChapters;
private Resource resource;

// 构造器、getter和setter
}

二、使用映射直接得到指定结构

ChapterMapper.xml 文件中,我们定义 SQL 查询以及结果映射。

language-xml
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
34
35
36
<mapper namespace="com.example.mapper.ChapterMapper">

<resultMap id="ChapterDtoMap" type="com.example.dto.ChapterDto">
<id column="chapter_id" property="id" />
<result column="parent_id" property="parentId" />
<result column="name" property="name" />
<result column="courseId" property="courseId" />
<collection property="subChapters" ofType="com.example.dto.ChapterDto">
<id column="sub_chapter_id" property="id" />
<result column="sub_parent_id" property="parentId" />
<result column="sub_name" property="name" />
<result column="sub_courseId" property="courseId" />
<association property="resource" javaType="com.example.model.Resource">
<id column="resource_id" property="id" />
<result column="section_id" property="sectionId" />
<result column="resource_name" property="name" />
</association>
</collection>
</resultMap>

<select id="selectChaptersWithResources" resultMap="ChapterDtoMap">
SELECT
c.id AS chapter_id, c.parent_id, c.name, c.courseId,
sc.id AS sub_chapter_id, sc.parent_id AS sub_parent_id, sc.name AS sub_name, sc.courseId AS sub_courseId,
r.id AS resource_id, r.section_id, r.name AS resource_name
FROM
chapter c
LEFT JOIN
chapter sc ON c.id = sc.parent_id
LEFT JOIN
resource r ON sc.id = r.section_id
WHERE
c.courseId = #{courseId} AND c.parent_id IS NULL
</select>

</mapper>

三、其他文件

1. Mapper

language-java
1
2
3
4
public interface ChapterMapper {

List<ChapterDto> selectChaptersWithResources(Long courseId);
}

2. Service

language-java
1
2
3
4
5
6
7
8
9
10
11
@Service
public class ChapterService {

@Autowired
private ChapterMapper chapterMapper;

public List<ChapterDto> getChaptersWithResources(Long courseId) {

return chapterMapper.selectChaptersWithResources(courseId);
}
}

3. Controller

language-java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
@RequestMapping("/chapters")
public class ChapterController {

@Autowired
private ChapterService chapterService;

@GetMapping("/{courseId}")
public ResponseEntity<List<ChapterDto>> getChapters(@PathVariable Long courseId) {

List<ChapterDto> chapters = chapterService.getChaptersWithResources(courseId);
return ResponseEntity.ok(chapters);
}
}

四、概念理解

一级映射

在提供的 resultMap 中,一级映射是针对 ChapterDto类的直接属性的映射。这意味着数据库中的列(如 chapter_id, parent_id等)直接映射到 ChapterDto类的相应属性(如 id, parent_id等),这部分映射是非常直接的。

二级映射

二级映射用于处理复杂的对象关系,比如当一个对象包含其他对象或对象的集合时。这通常在处理一对多关系时出现,例如,一个章节结构(ChapterDto)可能包含多个子章节。

聚合

这种聚合是根据您在 <collection> 标签中定义的规则进行的。MyBatis 会识别哪些行应该被映射为独立的实例,哪些行应该作为子元素聚合到其他实例中。

五、标签使用

1. 标签

用途:用于映射一对多关系。在这个例子中,ChapterDto类包含一个 Chapter 类型的列表,这代表了大章节和小章节之间的一对多关系。

常用属性

  • property:指定要映射到的目标属性名称。
  • ofType:指定集合中元素的类型。

2. 标签

用途:用于映射一对一关系。在您的例子中,ChapterDto包含一个 Resource 类型的属性,这代表了小章节和资源之间的一对一关系。
常用属性

  • property:指定要映射到的目标属性名称。
  • javaType:指定关联对象的类型。
SpringBoot3整合MyBatisPlus

SpringBoot3整合MyBatisPlus

一、起因

随着SpringBoot3的发布,mybatisplus也在不断更新以适配spirngboot3 。目前仍然处于维护升级阶段,最初2023.08时,官方宣布对SpringBoot3的原生支持,详情看这里

但是对于较新版本的SpringBoot3,仍然有很多bug,甚至无法启动,摸爬滚打又游历社区后,实践后得到一套成功的版本搭配,具体如下

Version
Java 17
Spring Boot 3.2.1
Spring Cloud 2023.0.0
Mybatis Plus 3.5.5

二、引入依赖

language-xml
1
2
3
4
5
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>

同时官方也提供了Demo项目,详情看这里,里面使用的版本搭配如下

Version
Java 17
Spring Boot 3.2.0
Mybatis Plus 3.5.5-SNAPSHOT
JavaWeb MyBatisPlus添加一个能够批量插入的Mapper通用方法

JavaWeb MyBatisPlus添加一个能够批量插入的Mapper通用方法

众所周知,mybatisplus提供的BaseMapper里只有单条插入的方法,没有批量插入的方法,
而在Service层的批量插入并不是真的批量插入,实际上是遍历insert,但也不是一次insert就一次IO,而是到一定数量才会去IO一次,性能不是很差,但也不够好。

怎么才能实现真正的批量插入呢?

这里是mybatisplus官方的演示仓库,可以先去了解一下。

一、注册自定义通用方法流程

  1. 把自定义方法写到BaseMapper,因为没法改BaseMapper,所以继承一下它
language-java
1
2
3
4
5
public interface MyBaseMapper<T> extends BaseMapper<T> {


int batchInsert(@Param("list") List<T> entityList);
}

MyBaseMapper扩展了原有的BaseMapper,所以你之后的Mapper层都继承自MyBaseMapper而不是BaseMapper即可。

  1. 把通用方法注册到mybatisplus
language-java
1
2
3
4
5
6
7
8
9
10
11
@Component
public class MySqlInjector extends DefaultSqlInjector {

@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {

List<AbstractMethod> defaultMethodList = super.getMethodList(mapperClass, tableInfo);
defaultMethodList.add(new BatchInsert("batchInsert"));
return defaultMethodList;
}
}

关键的一句在于defaultMethodList.add(new BatchInsert("batchInsert"));,意为注册一个新的方法叫batchInsert,具体实现在BatchInsert类。

  1. 实现BatchInsert类
language-java
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class BatchInsert extends AbstractMethod {

public BatchInsert(String name) {

super(name);
}

@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {

KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
String columnScript = getAllInsertSqlColumn(tableInfo.getFieldList());
String valuesScript = SqlScriptUtils.convertForeach(LEFT_BRACKET + getAllInsertSqlProperty("item.", tableInfo.getFieldList()) + RIGHT_BRACKET,
LIST, null, "item", COMMA);
String keyProperty = null;
String keyColumn = null;
// 表包含主键处理逻辑,如果不包含主键当普通字段处理
if (StringUtils.isNotBlank(tableInfo.getKeyProperty())) {

if (tableInfo.getIdType() == IdType.AUTO) {

/* 自增主键 */
keyGenerator = Jdbc3KeyGenerator.INSTANCE;
keyProperty = tableInfo.getKeyProperty();
// 去除转义符
keyColumn = SqlInjectionUtils.removeEscapeCharacter(tableInfo.getKeyColumn());
} else if (null != tableInfo.getKeySequence()) {

keyGenerator = TableInfoHelper.genKeyGenerator(methodName, tableInfo, builderAssistant);
keyProperty = tableInfo.getKeyProperty();
keyColumn = tableInfo.getKeyColumn();
}
}
String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);
SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, methodName, sqlSource, keyGenerator, keyProperty, keyColumn);
}

/**
* 获取 insert 时所有列名组成的sql片段
* @param fieldList 表字段信息列表
* @return sql 脚本片段
*/
private String getAllInsertSqlColumn(List<TableFieldInfo> fieldList) {

return LEFT_BRACKET + fieldList.stream()
.map(TableFieldInfo::getColumn).filter(Objects::nonNull).collect(joining(COMMA + NEWLINE)) + RIGHT_BRACKET;
}

/**
* 获取 insert 时所有属性值组成的sql片段
* @param prefix 前缀
* @param fieldList 表字段信息列表
* @return sql 脚本片段
*/
private String getAllInsertSqlProperty(final String prefix, List<TableFieldInfo> fieldList) {

final String newPrefix = prefix == null ? EMPTY : prefix;
return fieldList.stream()
.map(i -> i.getInsertSqlProperty(newPrefix).replace(",", ""))
.filter(Objects::nonNull)
.collect(joining(COMMA + NEWLINE));
}
}

二、BatchInsert具体实现逻辑解析

以如下简单的表user举例,

列名 描述 类型
id 主键,自增 bigint
user_name 用户名 varchar(64)
user_age 用户年龄 int

对于的entity大抵如下

language-java
1
2
3
4
5
6
7
8
9
10
11
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("user")
public class User{

@TableId(value = "id", type = IdType.AUTO)
private Long id;
private String userName;
private int userAge;
}

那么对于batchInsert,我们希望传入List<User>并希望得到类似如下的mybtaisplus xml sql语句

language-xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<insert id="batchInsert" parameterType="java.util.List">
insert into user(
id,
user_name,
user_age
)values
<foreach collection="list" item="item" separator=",">
(
#{item.id},
#{item.userName},
#{item.userAge}
)
</foreach>
</insert>

但是我们并不自己写这个xml,不然这需要对每一个数据表都要写一个,就像不那么硬的硬代码一样,我们希望有段逻辑,只需要传入entity,就能自己解析其中列名和对应的属性名,生成这段xml实现批量插入的功能。

假设你的UserMapper已经继承自MyBaseMapper,如果调用UserMapper.bacthInsert(List<User> entityList),那么会进入这个函数

language-java
1
2
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo)

其中mapperClass是映射器类,modelClass是模型类,我们并不需要了解,最主要的是tableInfo,这是表信息,它包含了关于数据库表的各种信息,如表名、列名、主键等。这个参数提供了详细的表信息,这对于生成针对特定表的SQL语句是必要的。

然后执行如下

language-java
1
2
3
4
5
6
7
//如果你的表名没有主键,那么你需要指定keyGenerator 为NoKeyGenerator,
//因为重写injectMappedStatement最后需要返回return this.addInsertMappedStatement
//其中就需要KeyGenerator
KeyGenerator keyGenerator = NoKeyGenerator.INSTANCE;
//SqlMethod.INSERT_ONE就是"INSERT INTO %s %s VALUES %s"
//我们依据表的信息生成列名sql片段和属性名sql片段后填入%s就可以得到近似最后的xml sql
SqlMethod sqlMethod = SqlMethod.INSERT_ONE;

然后执行如下

language-java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//tableInfo.getFieldList()会得到一个包含数据表列信息(不包含主键)的TableFieldInfo类组成的List
String columnScript = getAllInsertSqlColumn(tableInfo.getFieldList());
//这行代码就是在调用这个函数
private String getAllInsertSqlColumn(List<TableFieldInfo> fieldList) {

return LEFT_BRACKET + fieldList.stream()
//从TableFieldInfo中只拿取列名
.map(TableFieldInfo::getColumn)
//过滤null
.filter(Objects::nonNull)
//在元素间以逗号和分行分割
.collect(joining(COMMA + NEWLINE)) + RIGHT_BRACKET;
}
//对于User表,这个函数返回以下String
/*
(user_name,
user_age)
*/

然后执行如下

language-java
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
34
35
36
37
38
39
40
41
42
43
//首先调用了getAllInsertSqlProperty
String valuesScript = SqlScriptUtils.convertForeach(
// 这也是个内置函数,可以直接去看看
LEFT_BRACKET + getAllInsertSqlProperty("item.", tableInfo.getFieldList()) + RIGHT_BRACKET,
LIST,
null,
"item",
COMMA
);
//LEFT_BRACKET + getAllInsertSqlProperty("item.", tableInfo.getFieldList()) + RIGHT_BRACKET
//得到
/*
(#{userName},
#{userAge})
*/
//经过convertForeach函数后,得到如下字符串
/*
<foreach collection="list" item="item" separator=",">
(#{userName},
#{userAge})
</foreach>
*/

//getAllInsertSqlProperty函数如下
private String getAllInsertSqlProperty(final String prefix, List<TableFieldInfo> fieldList) {

//这里newPrefix 就是"item."
final String newPrefix = prefix == null ? EMPTY : prefix;
return fieldList.stream()
//i.getInsertSqlProperty("item.")是内置函数,假设i现在遍历到了user_name列
//那么得到的就是"#{userName},"
//然后,被删了
//所以本来每个元素从TableFieldInfo变成了形如"#{userName}"的字符串
.map(i -> i.getInsertSqlProperty(newPrefix).replace(",", ""))
.filter(Objects::nonNull)
//在元素间插入逗号和分行
.collect(joining(COMMA + NEWLINE));
}
//对于User表,这个函数返回以下String
/*
#{userName},
#{userAge}
*/

然后执行如下

language-java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
		//定义主键属性名
String keyProperty = null;
//定义主键列名
String keyColumn = null;
// 表包含主键处理逻辑,如果不包含主键当普通字段处理
if (StringUtils.isNotBlank(tableInfo.getKeyProperty())) {

if (tableInfo.getIdType() == IdType.AUTO) {

/* 自增主键 */
keyGenerator = Jdbc3KeyGenerator.INSTANCE;
keyProperty = tableInfo.getKeyProperty();
// 去除转义符
keyColumn = SqlInjectionUtils.removeEscapeCharacter(tableInfo.getKeyColumn());
} else if (null != tableInfo.getKeySequence()) {

keyGenerator = TableInfoHelper.genKeyGenerator(methodName, tableInfo, builderAssistant);
keyProperty = tableInfo.getKeyProperty();
keyColumn = tableInfo.getKeyColumn();
}
}
//这段代码没什么好说的,就是根据不同情况,得到三个变量
//keyGenerator keyProperty keyColumn

然后执行如下

language-java
1
2
3
4
5
6
7
8
9
String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript);
//就是把表名user,列名片段,属性名片段,填入%s中,得到如下
/*
INSERT INTO user (user_name,
user_age) VALUES <foreach collection="list" item="item" separator=",">
(#{userName},
#{userAge})
</foreach>
*/

然后执行如下

language-java
1
2
3
4
//这两句没有什么可说的,是重写injectMappedStatement函数的必要的操作
//自定义的内容就在于sql和主键
SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, methodName, sqlSource, keyGenerator, keyProperty, keyColumn);
JavaWeb 自动编号器Utils,用于各种自增长的编号,单号等

JavaWeb 自动编号器Utils,用于各种自增长的编号,单号等

使用以下技术栈

  • springboot
  • mybatisplus
  • mysql

一、首先设计一张表

表名假设为auto_numbering

名称 数据类型 备注 描述
id bigint 主键
name varchar(64) 编号名称
prefix varchar(8) 前缀
inti_value bigint 初始值
current_value bigint 当前值
length int 编号长度(不包含前缀)

假设有两条数据

id name prefix inti_value current_value length
1 stock_input SI 0 2 8
2 product_code PC 0 5 8

第一条表示入库单编号,编号的规则是,前缀为SI,当前编号数为2,长度为8,那么下一次使用入库单号时,对应的current_value加1变为3,得到的单号应该是字符串"SI00000003" 。第二条表示产品编号,同理,对应的current_value加1变为6,应该得到字符串"PC00000006"

那么如何设计这样一个自动编号工具类呢?这个工具类的方法最好是static,这样可以直接得到自动编号器的返回值,同时又要去让数据表对应的current_value自增。

二、创建对应的Domain类、Mapper接口、编号名称枚举类

domain

language-java
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
34
35
36
37
38
39
40
41
42
43
44
45
46
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("auto_numbering")
public class AutoNumbering{


private static final long serialVersionUID = 1L;

/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;

/**
* 编号名称
*/
private String name;

/**
* 前缀
*/
private String prefix;

/**
* 初始值
*/
private Long intiValue;

/**
* 当前值
*/
private Long currentValue;

/**
* 编号长度(不包含前缀)
*/
private Integer length;

}

mapper

language-java
1
2
3
4
5
6
7
8
9
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface AutoNumberingMapper extends BaseMapper<AutoNumbering> {


}

enum

language-java
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
34
35
public enum AutoNumberingEnum {


STOCK_INPUT("stock_input", "入库编号"),
PRODUCT_CODE("product_code", "产品编号");

private final String name;
private final String remark;

AutoNumberingEnum(String name, String remark) {

this.name = name;
this.remark = remark;
}

/**
* 获取枚举名称
*
* @return name
*/
public String getName() {

return this.name;
}

/**
* 获取枚举描述
*
* @return remark
*/
public String getRemark() {

return this.remark;
}
}

三、写一个Service接口和实现类

service接口类

language-java
1
2
3
4
5
6
7
8
9
10
public interface AutoNumberingService {


/**
* 获取下一个编号
* @param param 自动编号枚举值
* @return 自动编号字符串
*/
String getNextNo(AutoNumberingEnum param);
}

serviceimpl

language-java
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
public class AutoNumberingServiceImpl implements AutoNumberingService {


@Autowired
private AutoNumberingMapper autoNumberingMapper ;

/**
* 自动编号方法
*
* @param param 自动编号枚举值
* @return 编号
*/
@Transactional
public String getNextNo(AutoNumberingEnum param) {


// 查找当前的采番配置信息
LambdaQueryWrapper<AutoNumbering> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(AutoNumbering::getName, param.getName());
List<AutoNumbering> AutoNumberings = AutoNumberingMapper.selectList(wrapper);
// 未获取到配置信息
if (AutoNumberings.isEmpty()) {

return null;
} else {

// 规则获取
AutoNumbering autoNumbering = AutoNumberings.get(0);
// 前缀
String prefix = autoNumbering.getPrefix();
// 长度
Integer length = autoNumbering.getLength();
// 顺番
Long currentNo = autoNumbering.getCurrentValue();
// 更新原数据
currentNo++;
}
autoNumbering.setCurrentValue(currentNo);
AutoNumberingMapper.updateById(autoNumbering);

// 生成编号
String formatNo = StringUtils.leftPad(String.valueOf(currentNo), length, "0");
return String.format("%s%s", prefix, formatNo);
}
}

四、写一个Utils类静态获取编号

language-java
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
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class NoGeneratorUtil {


private static AutoNumberingService autoNumberingService;

@Autowired
public void setAutoNumberingService(AutoNumberingService autoNumberingService) {

NoGeneratorUtil.autoNumberingService = autoNumberingService;
}

/**
* 获取最新的入库单编号
* @return 最新的入库单编号
*/
public static String getNextStockInputNo() {

return autoNumberingService.getNextNo(AutoNumberingEnum.STOCK_INPUT);
}

/**
* 获取最新的产品编号
* @return 最新的c'p 编号
*/
public static String getNextProductNo() {

return autoNumberingService.getNextNo(AutoNumberingEnum.PRODUCT_CODE);
}

这段代码是SpringBoot框架中的一种常见用法,用于将SpringBoot管理的bean注入到静态变量中。那么,autoNumberingService是如何被注入的呢?实际上,当SpringBoot启动时,它会扫描所有的类,并为带有@Component(或者@Service等)注解的类创建实例。在创建NoGeneratorUtil实例的过程中,SpringBoot会调用所有带有@Autowired注解的setter方法,包括setAutoNumberingService方法,从而将AutoNumberingService的实现类注入到autoNumberingService静态变量中。

五、使用

之后如下使用即可一行代码获取最新编号

language-java
1
2
String string1 = NoGeneratorUtil.getNextStockInputNo();
String string2 = NoGeneratorUtil.getNextProductNo();
JavaWeb 若依RuoYi-Vue3框架将Mybatis切换成MybatisPlus

JavaWeb 若依RuoYi-Vue3框架将Mybatis切换成MybatisPlus

这里是官方的做法

mybatisSqlSessionFactoryBean,而mybatis-plusMybatisSqlSessionFactoryBean,所以一般最好是项目中使用一个最好,当然想要共存也可以,mybatis-plus的版本最好要高。这里只讲如何切换成MyBatisPlus。

一、修改yml

application.yml中将mybatis配置注释并写上新的mybatisplus,如下所示

language-yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
## MyBatis配置
#mybatis:
# # 搜索指定包别名
# typeAliasesPackage: com.ruoyi.**.domain
# # 配置mapper的扫描,找到所有的mapper.xml映射文件
# mapperLocations: classpath*:mapper/**/*Mapper.xml
# # 加载全局的配置文件
# configLocation: classpath:mybatis/mybatis-config.xml
mybatis-plus:
# 配置要扫描的xml文件目录,classpath* 代表所有模块的resources目录 classpath 不加星号代表当前模块下的resources目录
mapper-locations: classpath*:mapper/**/*Mapper.xml
# 实体扫描,*通配符
typeAliasesPackage: com.ruoyi.**.domain

二、maven导入mybatisplus包,如下

language-xml
1
2
3
4
5
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>最新版本</version>
</dependency>

三、注释SqlSessionFactoryBean

找到com.ruoyi.framework.config.MyBatisConfig,注释public SqlSessionFactory sqlSessionFactory(DataSource dataSource)如下

language-java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//    @Bean
// public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception
// {

// String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
// String mapperLocations = env.getProperty("mybatis.mapperLocations");
// String configLocation = env.getProperty("mybatis.configLocation");
// typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
// VFS.addImplClass(SpringBootVFS.class);
//
// final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
// sessionFactory.setDataSource(dataSource);
// sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
// sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ",")));
// sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
// return sessionFactory.getObject();
// }

到这里就完成了切换,快去试试 吧。

资料参考
若依框架集成mybatis换成mybatis-plus记录