mirror of
https://gitee.com/easii/mapstruct-plus.git
synced 2025-12-06 17:18:43 +08:00
release 1.4.0-R1
This commit is contained in:
parent
182bc024da
commit
155575096c
@ -23,6 +23,7 @@ export const series = {
|
||||
{ text: 'Map 转对象', link: '/guide/map-to-class' },
|
||||
{ text: '枚举转换', link: '/guide/enum-convert' },
|
||||
{ text: '一个类与多个类之间转换', link: '/guide/multiple-class-convert' },
|
||||
{ text: '类循环嵌套', link: '/guide/cycle-avoiding' },
|
||||
{ text: '类转换API', link: '/guide/converter-api' },
|
||||
{ text: '配置项', link: '/guide/configuration' },
|
||||
{ text: '常见问题', link: '/guide/faq' },
|
||||
|
||||
@ -62,18 +62,26 @@ footer:
|
||||
<dependency>
|
||||
<groupId>io.github.linpeilie</groupId>
|
||||
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
|
||||
<version>1.3.6</version>
|
||||
<version>1.4.0-R1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
- gradle
|
||||
|
||||
```groovy
|
||||
implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.3.6'
|
||||
implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.4.0-R1'
|
||||
```
|
||||
|
||||
## 更新日志
|
||||
|
||||
### 1.4.0
|
||||
|
||||
- **优化复杂对象转换逻辑,占用元空间更小!性能更快!**
|
||||
- 去除 hutool 等依赖,目前项目中只依赖了 MapStruct
|
||||
- 适配对象循环嵌套场景
|
||||
- [feature#63](https://github.com/linpeilie/mapstruct-plus/pull/63)`AutoMapping`、`ReverseAutoMapping` 支持 `qualifiedByName`、`conditionQualifiedByName` 和 `dependsOn` 属性
|
||||
- [issue#I93Z2Z](https://gitee.com/easii/mapstruct-plus/issues/I93Z2Z)`AutoMappings` 支持配置在方法上面
|
||||
|
||||
### 1.3.6
|
||||
|
||||
- 兼容内部类转换
|
||||
|
||||
@ -58,18 +58,26 @@ fotter:
|
||||
<dependency>
|
||||
<groupId>io.github.linpeilie</groupId>
|
||||
<artifactId>mapstruct-plus-spring-boot-starter</artifactId>
|
||||
<version>1.3.6</version>
|
||||
<version>1.4.0-R1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
- gradle
|
||||
|
||||
```groovy
|
||||
implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.3.6'
|
||||
implementation group: 'io.github.linpeilie', name: 'mapstruct-plus-spring-boot-starter', version: '1.4.0-R1'
|
||||
```
|
||||
|
||||
## Change Log
|
||||
|
||||
### 1.4.0
|
||||
|
||||
- **Optimize complex object conversion logic, take up less meta-space! and faster!**
|
||||
- Get rid of dependencies such as hutool, which currently only rely on MapStruct in the project.
|
||||
- The adaptation object loop nesting scenario
|
||||
- [feature#63](https://github.com/linpeilie/mapstruct-plus/pull/63) `AutoMapping`、`ReverseAutoMapping` supports `qualifiedByName`,`conditionQualifiedByName`,and `dependsOn` properties.
|
||||
- [issue#I93Z2Z](https://gitee.com/easii/mapstruct-plus/issues/I93Z2Z) `AutoMappings` supports configuration on methods.
|
||||
|
||||
### 1.3.6
|
||||
|
||||
- Compatible with internal class conversion.
|
||||
|
||||
@ -6,6 +6,14 @@ category:
|
||||
description: MapStructPlus release log
|
||||
---
|
||||
|
||||
### 1.4.0
|
||||
|
||||
- **Optimize complex object conversion logic, take up less meta-space! and faster!**
|
||||
- Get rid of dependencies such as hutool, which currently only rely on MapStruct in the project.
|
||||
- The adaptation object loop nesting scenario
|
||||
- [feature#63](https://github.com/linpeilie/mapstruct-plus/pull/63) `AutoMapping`、`ReverseAutoMapping` supports `qualifiedByName`,`conditionQualifiedByName`,and `dependsOn` properties.
|
||||
- [issue#I93Z2Z](https://gitee.com/easii/mapstruct-plus/issues/I93Z2Z) `AutoMappings` supports configuration on methods.
|
||||
|
||||
### 1.3.6
|
||||
|
||||
- Compatible with internal class conversion.
|
||||
|
||||
82
docs/guide/cycle-avoiding.md
Normal file
82
docs/guide/cycle-avoiding.md
Normal file
@ -0,0 +1,82 @@
|
||||
---
|
||||
title: 类循环嵌套
|
||||
order: 7
|
||||
category:
|
||||
- 指南
|
||||
description: MapStructPlus MapStructPlus类循环嵌套 CycleAvoiding
|
||||
---
|
||||
|
||||
## 背景
|
||||
|
||||
类循环嵌套是指两个类互相引用,例如,源对象和目标对象结构都包含父对象和子对象之间的双向关联。
|
||||
当存在这种情况时,直接进行转换时,会导致栈溢出的问题(stack overflow error)。
|
||||
|
||||
示例:
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class TreeNode {
|
||||
private TreeNode parent;
|
||||
private List<TreeNode> children;
|
||||
}
|
||||
|
||||
@Data
|
||||
public class TreeNodeDto {
|
||||
private TreeNodeDto parent;
|
||||
private List<TreeNodeDto> children;
|
||||
}
|
||||
```
|
||||
|
||||
`parent` 属性可以是其他类型的,可能跨越一个更长的属性链形成的嵌套循环。
|
||||
|
||||
为了适配这种情况,MapStructPlus 的 **`AutoMapper`** 注解中增加了 **`cycleAvoiding`** 属性,该属性用于标识,是否需要避免循环嵌套的问题。
|
||||
默认为 `false`,如果需要避免循环嵌套,需要将该属性设置为 `true`。
|
||||
|
||||
当配置为 `true` 时,在整个对象的转换过程链路中,会传递一个 `CycleAvoidingMappingContext` 对象,临时保存转换生成的对象,
|
||||
在转换链路中,如果发现需要生成的对象已经存在,会直接返回该类型,从而避免栈溢出问题。
|
||||
所以,配置该属性为 `true` 时,会有一点的性能消耗,如果没有循环嵌套的情况,使用默认配置即可,避免不必要的性能消耗。
|
||||
|
||||
## 使用示例
|
||||
|
||||
以上面的示例为例,在 `AutoMapper` 注解中,配置 `cycleAvoiding` 属性为 `true`,如下所示:
|
||||
|
||||
```java
|
||||
@Data
|
||||
@AutoMapper(target = TreeNodeDto.class, cycleAvoiding = true)
|
||||
public class TreeNode {
|
||||
private TreeNode parent;
|
||||
private List<TreeNode> children;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AutoMapper(target = TreeNode.class, cycleAvoiding = true)
|
||||
public class TreeNodeDto {
|
||||
private TreeNodeDto parent;
|
||||
private List<TreeNodeDto> children;
|
||||
}
|
||||
```
|
||||
|
||||
编译生成的转换逻辑如下:
|
||||
|
||||
```java
|
||||
public TreeNodeDto convert(TreeNode arg0, CycleAvoidingMappingContext arg1) {
|
||||
TreeNodeDto target = arg1.getMappedInstance(arg0, TreeNodeDto.class);
|
||||
if (target != null) {
|
||||
return target;
|
||||
}
|
||||
|
||||
if (arg0 == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TreeNodeDto treeNodeDto = new TreeNodeDto();
|
||||
|
||||
arg1.storeMappedInstance(arg0, treeNodeDto);
|
||||
|
||||
treeNodeDto.setParent(demoConvertMapperAdapterForCycleAvoiding.iglm_TreeNodeToTreeNodeDto(arg0.getParent(), arg1));
|
||||
treeNodeDto.setChildren(
|
||||
demoConvertMapperAdapterForCycleAvoiding.iglm_TreeNodeToTreeNodeDto(arg0.getChildren(), arg1));
|
||||
|
||||
return treeNodeDto;
|
||||
}
|
||||
```
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
title: 常见问题
|
||||
order: 7
|
||||
order: 8
|
||||
category:
|
||||
- 指南
|
||||
description: MapStructPlus MapStructPlus常见问题 faq
|
||||
|
||||
@ -8,8 +8,26 @@ description: MapStructPlus Map转为对象 map convert to class
|
||||
|
||||
MapStructPlus 提供了更加强大的 `Map<String, Object>` 转对象的功能。
|
||||
|
||||
::: warning
|
||||
MapStructPlus 1.4.0 及以后版本,不再内置 [Hutool](https://hutool.cn) 框架,如果需要用到该功能时,需要额外引入 `hutool-core` 依赖。
|
||||
:::
|
||||
|
||||
## 使用
|
||||
|
||||
### 添加依赖
|
||||
|
||||
> 1.4.0 及以后的版本需要添加该依赖,1.4.0之前的版本内置 hutool,不需要额外添加。
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<version>${hutool.version}</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
### 添加注解
|
||||
|
||||
**当想要自动生成 `Map<String, Object>` 转为目标类的接口及实现类时,只需要在目标类上添加 `@AutoMapMapper` 注解**。
|
||||
|
||||
## 支持的 value 类型
|
||||
|
||||
@ -6,6 +6,14 @@ category:
|
||||
description: MapStructPlus release log
|
||||
---
|
||||
|
||||
### 1.4.0
|
||||
|
||||
- **优化复杂对象转换逻辑,占用元空间更小!性能更快!**
|
||||
- 去除 hutool 等依赖,目前项目中只依赖了 MapStruct
|
||||
- 适配对象循环嵌套场景
|
||||
- [feature#63](https://github.com/linpeilie/mapstruct-plus/pull/63)`AutoMapping`、`ReverseAutoMapping` 支持 `qualifiedByName`、`conditionQualifiedByName` 和 `dependsOn` 属性
|
||||
- [issue#I93Z2Z](https://gitee.com/easii/mapstruct-plus/issues/I93Z2Z)`AutoMappings` 支持配置在方法上面
|
||||
|
||||
### 1.3.6
|
||||
|
||||
- 兼容内部类转换
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<mapstruct.version>1.5.1.Final</mapstruct.version>
|
||||
<mapstruct-plus.version>1.4.0</mapstruct-plus.version>
|
||||
<mapstruct-plus.version>1.4.0-R1</mapstruct-plus.version>
|
||||
<lombok.version>1.18.22</lombok.version>
|
||||
<hutool.version>5.8.26</hutool.version>
|
||||
</properties>
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
package io.github.linpeilie.mapper;
|
||||
|
||||
import org.mapstruct.Named;
|
||||
import org.springframework.boot.context.properties.bind.Name;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Named("TitleTranslator")
|
||||
public class Titles {
|
||||
|
||||
@Named("EnglishToFrench")
|
||||
public String translateTitleEF(String title) {
|
||||
if ("One Hundred Years of Solitude".equals(title)) {
|
||||
return "Cent ans de solitude";
|
||||
}
|
||||
return "Inconnu et inconnu";
|
||||
}
|
||||
|
||||
@Named("FrenchToEnglish")
|
||||
public String translateTitleFE(String title) {
|
||||
if ("Cent ans de solitude".equals(title)) {
|
||||
return "One Hundred Years of Solitude";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package io.github.linpeilie.model;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import io.github.linpeilie.annotations.AutoMapping;
|
||||
import io.github.linpeilie.mapper.Titles;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AutoMapper(target = FrenchRelease.class, uses = Titles.class)
|
||||
public class EnglishRelease {
|
||||
|
||||
@AutoMapping(qualifiedByName = "EnglishToFrench")
|
||||
private String title;
|
||||
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package io.github.linpeilie.model;
|
||||
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import io.github.linpeilie.annotations.AutoMapping;
|
||||
import io.github.linpeilie.mapper.Titles;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AutoMapper(target = EnglishRelease.class, uses = Titles.class)
|
||||
public class FrenchRelease {
|
||||
|
||||
@AutoMapping(qualifiedByName = "FrenchToEnglish")
|
||||
private String title;
|
||||
|
||||
}
|
||||
@ -2,7 +2,10 @@ package io.github.linpeilie;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import io.github.linpeilie.model.Car;
|
||||
import io.github.linpeilie.model.EnglishRelease;
|
||||
import io.github.linpeilie.model.FrenchRelease;
|
||||
import io.github.linpeilie.model.Goods;
|
||||
import io.github.linpeilie.model.GoodsDto;
|
||||
import io.github.linpeilie.model.GoodsStateEnum;
|
||||
@ -16,6 +19,8 @@ import io.github.linpeilie.model.SVO;
|
||||
import io.github.linpeilie.model.Sku;
|
||||
import io.github.linpeilie.model.SysMenu;
|
||||
import io.github.linpeilie.model.SysMenuVo;
|
||||
import io.github.linpeilie.model.TreeNode;
|
||||
import io.github.linpeilie.model.TreeNodeDto;
|
||||
import io.github.linpeilie.model.User;
|
||||
import io.github.linpeilie.model.UserDto;
|
||||
import io.github.linpeilie.model.UserVO;
|
||||
@ -262,4 +267,48 @@ public class QuickStartTest {
|
||||
System.out.println(sDto1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConditionQualifiedByName() {
|
||||
TreeNode parent = new TreeNode();
|
||||
// children
|
||||
List<TreeNode> children = new ArrayList<>();
|
||||
for (int i = 0; i < 1; i++) {
|
||||
TreeNode child = new TreeNode();
|
||||
child.setParent(parent);
|
||||
children.add(child);
|
||||
}
|
||||
parent.setChildren(children);
|
||||
TreeNodeDto treeNodeDto1 = converter.convert(parent, TreeNodeDto.class);
|
||||
// 当 children 为 <2 时不进行转换
|
||||
Assert.isNull(treeNodeDto1.getChildren());
|
||||
for (int i = 0; i < 99; i++) {
|
||||
TreeNode child = new TreeNode();
|
||||
child.setParent(parent);
|
||||
children.add(child);
|
||||
}
|
||||
TreeNodeDto treeNodeDto2 = converter.convert(parent, TreeNodeDto.class);
|
||||
Assert.equals(treeNodeDto2.getChildren().size(), 100);
|
||||
|
||||
treeNodeDto2.getChildren().forEach(child -> {
|
||||
Assert.equals(child.getParent(), treeNodeDto2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQualifiedByName() {
|
||||
EnglishRelease englishRelease = new EnglishRelease();
|
||||
englishRelease.setTitle("Algorithms, 4th Edition");
|
||||
FrenchRelease frenchRelease1 = converter.convert(englishRelease, FrenchRelease.class);
|
||||
Assert.equals(frenchRelease1.getTitle(), "Inconnu et inconnu");
|
||||
englishRelease.setTitle("One Hundred Years of Solitude");
|
||||
FrenchRelease frenchRelease2 = converter.convert(englishRelease, FrenchRelease.class);
|
||||
Assert.equals(frenchRelease2.getTitle(), "Cent ans de solitude");
|
||||
|
||||
EnglishRelease englishRelease1 = converter.convert(frenchRelease1, EnglishRelease.class);
|
||||
Assert.equals(englishRelease1.getTitle(), "Unknown");
|
||||
frenchRelease2.setTitle("Cent ans de solitude");
|
||||
EnglishRelease englishRelease2 = converter.convert(frenchRelease2, EnglishRelease.class);
|
||||
Assert.equals(englishRelease2.getTitle(), "One Hundred Years of Solitude");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
2
pom.xml
2
pom.xml
@ -18,7 +18,7 @@
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<mapstruct-plus.version>1.4.0</mapstruct-plus.version>
|
||||
<mapstruct-plus.version>1.4.0-R1</mapstruct-plus.version>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user