🎯 学习目标
通过本篇教程,你将学会:
- 掌握 List、Set、Map 等集合类型的映射
- 理解嵌套对象的深度映射机制
- 学会处理复杂的对象图结构
- 掌握循环引用和性能优化技巧
📋 概念讲解:集合映射机制
集合映射层次结构
graph TB
subgraph Collection["集合类型映射"]
A1[List映射] --> A2["List<Entity> → List<DTO>"]
A3[Set映射] --> A4["Set<Entity> → Set<DTO>"]
A5[Map映射] --> A6["Map<K,V> → Map<K,DTO>"]
A7[Array映射] --> A8["Entity[] → DTO[]"]
end
subgraph Nested["嵌套对象映射"]
B1[单层嵌套] --> B2["User.address → UserDto.addressDto"]
B3[多层嵌套] --> B4["Order.user.address → OrderDto.userDto.addressDto"]
B5[双向关联] --> B6["User ↔ Order"]
B7[自引用] --> B8["Category.parent → CategoryDto.parentDto"]
end
subgraph Strategy["映射策略"]
C1[自动映射] --> C2[类型匹配自动转换]
C3[uses引用] --> C4[指定依赖映射器]
C5[qualifiedByName] --> C6[指定转换方法]
C7[循环检测] --> C8[防止无限递归]
end
A1 --> B1
B1 --> C1
style A1 fill:#e8f5e8
style B1 fill:#e3f2fd
style C1 fill:#fff3e0
映射执行流程
flowchart TD
A[开始集合映射] --> B{是否为null?}
B -->|是| C[返回null]
B -->|否| D{是否为空集合?}
D -->|是| E[返回空集合]
D -->|否| F[遍历集合元素]
F --> G{元素是否为基础类型?}
G -->|是| H[直接复制]
G -->|否| I{是否有对应映射器?}
I -->|有| J[调用映射器转换]
I -->|无| K{是否有uses引用?}
K -->|有| L[使用引用的映射器]
K -->|无| M[尝试自动映射]
M --> N{映射成功?}
N -->|是| O[添加到结果集合]
N -->|否| P[抛出编译错误]
H --> O
J --> O
L --> O
O --> Q{还有更多元素?}
Q -->|是| F
Q -->|否| R[返回结果集合]
C --> S[完成]
E --> S
R --> S
P --> S
style J fill:#c8e6c9
style L fill:#e8f5e8
style M fill:#fff3e0
style P fill:#ffcdd2
🔧 实现步骤:集合映射详解
步骤 1:基础集合映射
List 映射
java
// 用户实体类
public class User {
private Long id;
private String name;
private String email;
// getter/setter...
}
// 用户 DTO 类
public class UserDto {
private Long id;
private String name;
private String email;
// getter/setter...
}
/**
* 基础 List 映射
*/
@Mapper(componentModel = "spring")
public interface UserMapper {
// 🔥 单个对象映射
UserDto toDto(User user);
// 🔥 List 映射 - 自动推断
List<UserDto> toDtoList(List<User> users);
// 🔥 反向映射
User toEntity(UserDto dto);
List<User> toEntityList(List<UserDto> dtos);
}
Set 和 Array 映射
java
/**
* Set 和 Array 映射示例
*/
@Mapper(componentModel = "spring")
public interface CollectionMapper {
// Set 映射
Set<UserDto> toDtoSet(Set<User> users);
Set<User> toEntitySet(Set<UserDto> dtos);
// Array 映射
UserDto[] toDtoArray(User[] users);
User[] toEntityArray(UserDto[] dtos);
// 不同集合类型之间的转换
List<UserDto> setToList(Set<User> users);
Set<UserDto> listToSet(List<User> users);
}
步骤 2:Map 映射
java
// 产品实体类
public class Product {
private Long id;
private String name;
private BigDecimal price;
// getter/setter...
}
// 产品 DTO 类
public class ProductDto {
private Long id;
private String name;
private String price; // 格式化后的价格字符串
// getter/setter...
}
/**
* Map 映射示例
*/
@Mapper(componentModel = "spring")
public interface ProductMapper {
// 单个对象映射
@Mapping(target = "price", source = "price", numberFormat = "¥#,##0.00")
ProductDto toDto(Product product);
// 🔥 Map<String, Entity> → Map<String, DTO>
Map<String, ProductDto> toDtoMap(Map<String, Product> products);
// 🔥 Map<Long, Entity> → Map<Long, DTO>
Map<Long, ProductDto> toDtoMapWithLongKey(Map<Long, Product> products);
// 🔥 Map<String, List<Entity>> → Map<String, List<DTO>>
Map<String, List<ProductDto>> toDtoListMap(Map<String, List<Product>> productsByCategory);
// 反向映射
Product toEntity(ProductDto dto);
Map<String, Product> toEntityMap(Map<String, ProductDto> dtos);
}
步骤 3:嵌套对象映射
单层嵌套映射
java
// 地址实体类
public class Address {
private String province;
private String city;
private String district;
private String detail;
// getter/setter...
}
// 地址 DTO 类
public class AddressDto {
private String province;
private String city;
private String district;
private String detail;
private String fullAddress; // 组合字段
// getter/setter...
}
// 用户实体类(包含地址)
public class UserWithAddress {
private Long id;
private String name;
private String email;
private Address address; // 🔥 嵌套对象
private List<Address> addresses; // 🔥 嵌套对象列表
// getter/setter...
}
// 用户 DTO 类(包含地址 DTO)
public class UserWithAddressDto {
private Long id;
private String name;
private String email;
private AddressDto address; // 🔥 嵌套 DTO
private List<AddressDto> addresses; // 🔥 嵌套 DTO 列表
// getter/setter...
}
/**
* 地址映射器
*/
@Mapper(componentModel = "spring")
public interface AddressMapper {
@Mapping(target = "fullAddress",
expression = "java(address.getProvince() + address.getCity() + address.getDistrict() + address.getDetail())")
AddressDto toDto(Address address);
Address toEntity(AddressDto dto);
// 集合映射
List<AddressDto> toDtoList(List<Address> addresses);
List<Address> toEntityList(List<AddressDto> dtos);
}
/**
* 用户映射器(使用地址映射器)
*/
@Mapper(
componentModel = "spring",
uses = AddressMapper.class // 🔥 引用地址映射器
)
public interface UserWithAddressMapper {
// 🔥 自动使用 AddressMapper 处理嵌套对象
UserWithAddressDto toDto(UserWithAddress user);
UserWithAddress toEntity(UserWithAddressDto dto);
// 🔥 自动处理嵌套对象列表
List<UserWithAddressDto> toDtoList(List<UserWithAddress> users);
}
多层嵌套映射
java
// 公司实体类
public class Company {
private Long id;
private String name;
private Address address;
// getter/setter...
}
// 公司 DTO 类
public class CompanyDto {
private Long id;
private String name;
private AddressDto address;
// getter/setter...
}
// 部门实体类
public class Department {
private Long id;
private String name;
private Company company; // 🔥 二级嵌套
// getter/setter...
}
// 部门 DTO 类
public class DepartmentDto {
private Long id;
private String name;
private CompanyDto company; // 🔥 二级嵌套 DTO
// getter/setter...
}
// 员工实体类
public class Employee {
private Long id;
private String name;
private String email;
private Department department; // 🔥 三级嵌套:Employee -> Department -> Company -> Address
// getter/setter...
}
// 员工 DTO 类
public class EmployeeDto {
private Long id;
private String name;
private String email;
private DepartmentDto department; // 🔥 三级嵌套 DTO
// getter/setter...
}
/**
* 公司映射器
*/
@Mapper(
componentModel = "spring",
uses = AddressMapper.class
)
public interface CompanyMapper {
CompanyDto toDto(Company company);
Company toEntity(CompanyDto dto);
}
/**
* 部门映射器
*/
@Mapper(
componentModel = "spring",
uses = CompanyMapper.class // 🔥 引用公司映射器
)
public interface DepartmentMapper {
DepartmentDto toDto(Department department);
Department toEntity(DepartmentDto dto);
}
/**
* 员工映射器
*/
@Mapper(
componentModel = "spring",
uses = DepartmentMapper.class // 🔥 引用部门映射器
)
public interface EmployeeMapper {
// 🔥 自动处理多层嵌套:Employee -> Department -> Company -> Address
EmployeeDto toDto(Employee employee);
Employee toEntity(EmployeeDto dto);
List<EmployeeDto> toDtoList(List<Employee> employees);
}
步骤 4:复杂集合和嵌套组合
java
// 订单项实体类
public class OrderItem {
private Long id;
private Product product; // 嵌套产品对象
private Integer quantity;
private BigDecimal unitPrice;
// getter/setter...
}
// 订单项 DTO 类
public class OrderItemDto {
private Long id;
private ProductDto product; // 嵌套产品 DTO
private Integer quantity;
private String unitPrice; // 格式化价格
private String totalPrice; // 计算字段
// getter/setter...
}
// 订单实体类
public class Order {
private Long id;
private String orderNo;
private UserWithAddress customer; // 嵌套客户对象
private List<OrderItem> items; // 🔥 嵌套对象列表
private Map<String, String> metadata; // 🔥 元数据 Map
private Set<String> tags; // 🔥 标签 Set
private LocalDateTime createdAt;
// getter/setter...
}
// 订单 DTO 类
public class OrderDto {
private Long id;
private String orderNo;
private UserWithAddressDto customer; // 嵌套客户 DTO
private List<OrderItemDto> items; // 🔥 嵌套 DTO 列表
private Map<String, String> metadata; // 🔥 直接映射 Map
private Set<String> tags; // 🔥 直接映射 Set
private String createdAt; // 格式化时间
private String totalAmount; // 计算总金额
private Integer totalItems; // 计算总商品数
// getter/setter...
}
/**
* 订单项映射器
*/
@Mapper(
componentModel = "spring",
uses = ProductMapper.class
)
public interface OrderItemMapper {
@Mapping(target = "unitPrice", source = "unitPrice", numberFormat = "¥#,##0.00")
@Mapping(target = "totalPrice",
expression = "java(formatTotalPrice(orderItem.getUnitPrice(), orderItem.getQuantity()))")
OrderItemDto toDto(OrderItem orderItem);
OrderItem toEntity(OrderItemDto dto);
List<OrderItemDto> toDtoList(List<OrderItem> items);
List<OrderItem> toEntityList(List<OrderItemDto> dtos);
/**
* 计算总价
*/
default String formatTotalPrice(BigDecimal unitPrice, Integer quantity) {
if (unitPrice == null || quantity == null) {
return "¥0.00";
}
BigDecimal total = unitPrice.multiply(new BigDecimal(quantity));
DecimalFormat df = new DecimalFormat("¥#,##0.00");
return df.format(total);
}
}
/**
* 订单映射器
*/
@Mapper(
componentModel = "spring",
uses = {UserWithAddressMapper.class, OrderItemMapper.class} // 🔥 引用多个映射器
)
public interface OrderMapper {
@Mapping(target = "createdAt", source = "createdAt", dateFormat = "yyyy-MM-dd HH:mm:ss")
@Mapping(target = "totalAmount",
expression = "java(calculateTotalAmount(order.getItems()))")
@Mapping(target = "totalItems",
expression = "java(calculateTotalItems(order.getItems()))")
OrderDto toDto(Order order);
@Mapping(target = "createdAt", source = "createdAt", dateFormat = "yyyy-MM-dd HH:mm:ss")
Order toEntity(OrderDto dto);
List<OrderDto> toDtoList(List<Order> orders);
/**
* 计算订单总金额
*/
default String calculateTotalAmount(List<OrderItem> items) {
if (items == null || items.isEmpty()) {
return "¥0.00";
}
BigDecimal total = items.stream()
.filter(item -> item.getUnitPrice() != null && item.getQuantity() != null)
.map(item -> item.getUnitPrice().multiply(new BigDecimal(item.getQuantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
DecimalFormat df = new DecimalFormat("¥#,##0.00");
return df.format(total);
}
/**
* 计算商品总数
*/
default Integer calculateTotalItems(List<OrderItem> items) {
if (items == null) {
return 0;
}
return items.stream()
.filter(item -> item.getQuantity() != null)
.mapToInt(OrderItem::getQuantity)
.sum();
}
}
💻 示例代码:完整的集合映射示例
示例 1:电商系统复杂映射
java
// 分类实体类(自引用结构)
public class Category {
private Long id;
private String name;
private String description;
private Category parent; // 🔥 自引用:父分类
private List<Category> children; // 🔥 自引用:子分类列表
private List<Product> products; // 🔥 分类下的产品列表
// getter/setter...
}
// 分类 DTO 类
public class CategoryDto {
private Long id;
private String name;
private String description;
private CategoryDto parent; // 🔥 自引用 DTO
private List<CategoryDto> children; // 🔥 自引用 DTO 列表
private List<ProductDto> products; // 🔥 产品 DTO 列表
private Integer productCount; // 🔥 产品数量统计
private Integer childrenCount; // 🔥 子分类数量统计
// getter/setter...
}
/**
* 分类映射器(处理自引用和循环引用)
*/
@Mapper(
componentModel = "spring",
uses = ProductMapper.class
)
public interface CategoryMapper {
@Mapping(target = "productCount",
expression = "java(category.getProducts() != null ? category.getProducts().size() : 0)")
@Mapping(target = "childrenCount",
expression = "java(category.getChildren() != null ? category.getChildren().size() : 0)")
CategoryDto toDto(Category category);
Category toEntity(CategoryDto dto);
List<CategoryDto> toDtoList(List<Category> categories);
/**
* 浅层映射 - 避免深度递归
*/
@Mapping(target = "parent", ignore = true)
@Mapping(target = "children", ignore = true)
@Mapping(target = "products", ignore = true)
@Mapping(target = "productCount", constant = "0")
@Mapping(target = "childrenCount", constant = "0")
CategoryDto toShallowDto(Category category);
List<CategoryDto> toShallowDtoList(List<Category> categories);
}
示例 2:社交网络复杂关系映射
java
// 用户实体类(复杂关系)
public class SocialUser {
private Long id;
private String username;
private String nickname;
private String avatar;
// 🔥 多对多关系:关注的用户
private Set<SocialUser> following;
// 🔥 多对多关系:粉丝用户
private Set<SocialUser> followers;
// 🔥 一对多关系:发布的帖子
private List<Post> posts;
// 🔥 多对多关系:点赞的帖子
private Set<Post> likedPosts;
// getter/setter...
}
// 帖子实体类
public class Post {
private Long id;
private String content;
private String imageUrl;
private LocalDateTime createdAt;
// 🔥 多对一关系:作者
private SocialUser author;
// 🔥 多对多关系:点赞用户
private Set<SocialUser> likedBy;
// 🔥 一对多关系:评论
private List<Comment> comments;
// getter/setter...
}
// 评论实体类
public class Comment {
private Long id;
private String content;
private LocalDateTime createdAt;
// 🔥 多对一关系:评论者
private SocialUser author;
// 🔥 多对一关系:所属帖子
private Post post;
// getter/setter...
}
// 用户 DTO 类(简化版)
public class SocialUserDto {
private Long id;
private String username;
private String nickname;
private String avatar;
// 🔥 简化关系:只包含基本信息
private List<SimpleUserDto> following;
private List<SimpleUserDto> followers;
private List<PostSummaryDto> recentPosts; // 最近帖子
// 🔥 统计信息
private Integer followingCount;
private Integer followersCount;
private Integer postsCount;
private Integer totalLikes;
// getter/setter...
}
// 简单用户 DTO
public class SimpleUserDto {
private Long id;
private String username;
private String nickname;
private String avatar;
// getter/setter...
}
// 帖子摘要 DTO
public class PostSummaryDto {
private Long id;
private String content;
private String imageUrl;
private String createdAt;
private Integer likesCount;
private Integer commentsCount;
// getter/setter...
}
/**
* 简单用户映射器
*/
@Mapper(componentModel = "spring")
public interface SimpleUserMapper {
SimpleUserDto toDto(SocialUser user);
List<SimpleUserDto> toDtoList(List<SocialUser> users);
Set<SimpleUserDto> toDtoSet(Set<SocialUser> users);
}
/**
* 帖子摘要映射器
*/
@Mapper(componentModel = "spring")
public interface PostSummaryMapper {
@Mapping(target = "createdAt", source = "createdAt", dateFormat = "MM-dd HH:mm")
@Mapping(target = "likesCount",
expression = "java(post.getLikedBy() != null ? post.getLikedBy().size() : 0)")
@Mapping(target = "commentsCount",
expression = "java(post.getComments() != null ? post.getComments().size() : 0)")
PostSummaryDto toDto(Post post);
List<PostSummaryDto> toDtoList(List<Post> posts);
}
/**
* 社交用户映射器
*/
@Mapper(
componentModel = "spring",
uses = {SimpleUserMapper.class, PostSummaryMapper.class}
)
public interface SocialUserMapper {
@Mapping(target = "following", source = "following")
@Mapping(target = "followers", source = "followers")
@Mapping(target = "recentPosts",
expression = "java(getRecentPosts(user.getPosts()))")
@Mapping(target = "followingCount",
expression = "java(user.getFollowing() != null ? user.getFollowing().size() : 0)")
@Mapping(target = "followersCount",
expression = "java(user.getFollowers() != null ? user.getFollowers().size() : 0)")
@Mapping(target = "postsCount",
expression = "java(user.getPosts() != null ? user.getPosts().size() : 0)")
@Mapping(target = "totalLikes",
expression = "java(calculateTotalLikes(user.getPosts()))")
SocialUserDto toDto(SocialUser user);
List<SocialUserDto> toDtoList(List<SocialUser> users);
/**
* 获取最近的帖子(最多5条)
*/
default List<Post> getRecentPosts(List<Post> posts) {
if (posts == null || posts.isEmpty()) {
return Collections.emptyList();
}
return posts.stream()
.sorted((p1, p2) -> p2.getCreatedAt().compareTo(p1.getCreatedAt()))
.limit(5)
.collect(Collectors.toList());
}
/**
* 计算总点赞数
*/
default Integer calculateTotalLikes(List<Post> posts) {
if (posts == null) {
return 0;
}
return posts.stream()
.filter(post -> post.getLikedBy() != null)
.mapToInt(post -> post.getLikedBy().size())
.sum();
}
}
示例 3:树形结构映射
java
// 组织架构实体类(树形结构)
public class Organization {
private Long id;
private String name;
private String code;
private String description;
private Integer level; // 层级
private Organization parent; // 父组织
private List<Organization> children; // 子组织
private List<Employee> employees; // 员工列表
// getter/setter...
}
// 组织架构 DTO 类
public class OrganizationDto {
private Long id;
private String name;
private String code;
private String description;
private Integer level;
private OrganizationDto parent; // 父组织 DTO
private List<OrganizationDto> children; // 子组织 DTO 列表
private List<EmployeeDto> employees; // 员工 DTO 列表
// 🔥 统计信息
private Integer directEmployeeCount; // 直属员工数
private Integer totalEmployeeCount; // 总员工数(包含子组织)
private Integer childrenCount; // 子组织数
private String organizationPath; // 组织路径
// getter/setter...
}
/**
* 组织架构映射器
*/
@Mapper(
componentModel = "spring",
uses = EmployeeMapper.class
)
public interface OrganizationMapper {
@Mapping(target = "directEmployeeCount",
expression = "java(org.getEmployees() != null ? org.getEmployees().size() : 0)")
@Mapping(target = "totalEmployeeCount",
expression = "java(calculateTotalEmployeeCount(org))")
@Mapping(target = "childrenCount",
expression = "java(org.getChildren() != null ? org.getChildren().size() : 0)")
@Mapping(target = "organizationPath",
expression = "java(buildOrganizationPath(org))")
OrganizationDto toDto(Organization org);
Organization toEntity(OrganizationDto dto);
List<OrganizationDto> toDtoList(List<Organization> orgs);
/**
* 树形结构映射 - 限制深度避免性能问题
*/
@Mapping(target = "children",
expression = "java(mapChildrenWithDepthLimit(org.getChildren(), 3))")
@Mapping(target = "directEmployeeCount",
expression = "java(org.getEmployees() != null ? org.getEmployees().size() : 0)")
@Mapping(target = "totalEmployeeCount",
expression = "java(calculateTotalEmployeeCount(org))")
@Mapping(target = "childrenCount",
expression = "java(org.getChildren() != null ? org.getChildren().size() : 0)")
@Mapping(target = "organizationPath",
expression = "java(buildOrganizationPath(org))")
OrganizationDto toDtoWithDepthLimit(Organization org);
/**
* 计算总员工数(递归)
*/
default Integer calculateTotalEmployeeCount(Organization org) {
if (org == null) {
return 0;
}
int total = org.getEmployees() != null ? org.getEmployees().size() : 0;
if (org.getChildren() != null) {
for (Organization child : org.getChildren()) {
total += calculateTotalEmployeeCount(child);
}
}
return total;
}
/**
* 构建组织路径
*/
default String buildOrganizationPath(Organization org) {
if (org == null) {
return "";
}
List<String> path = new ArrayList<>();
Organization current = org;
while (current != null) {
path.add(0, current.getName());
current = current.getParent();
}
return String.join(" > ", path);
}
/**
* 限制深度的子组织映射
*/
default List<OrganizationDto> mapChildrenWithDepthLimit(List<Organization> children, int maxDepth) {
if (children == null || maxDepth <= 0) {
return Collections.emptyList();
}
return children.stream()
.map(child -> {
OrganizationDto dto = new OrganizationDto();
dto.setId(child.getId());
dto.setName(child.getName());
dto.setCode(child.getCode());
dto.setDescription(child.getDescription());
dto.setLevel(child.getLevel());
// 递归映射子组织(减少深度)
if (maxDepth > 1) {
dto.setChildren(mapChildrenWithDepthLimit(child.getChildren(), maxDepth - 1));
}
// 统计信息
dto.setDirectEmployeeCount(child.getEmployees() != null ? child.getEmployees().size() : 0);
dto.setChildrenCount(child.getChildren() != null ? child.getChildren().size() : 0);
dto.setOrganizationPath(buildOrganizationPath(child));
return dto;
})
.collect(Collectors.toList());
}
}
🎬 效果演示:测试集合映射功能
创建测试控制器
java
@RestController
@RequestMapping("/api/collection-mapping")
public class CollectionMappingController {
@Autowired
private UserMapper userMapper;
@Autowired
private OrderMapper orderMapper;
@Autowired
private CategoryMapper categoryMapper;
@Autowired
private SocialUserMapper socialUserMapper;
@Autowired
private OrganizationMapper organizationMapper;
/**
* 演示基础集合映射
*/
@GetMapping("/users")
public List<UserDto> getUsers() {
List<User> users = Arrays.asList(
createUser(1L, "张三", "zhangsan@example.com"),
createUser(2L, "李四", "lisi@example.com"),
createUser(3L, "王五", "wangwu@example.com")
);
// 🎯 List 映射
List<UserDto> dtos = userMapper.toDtoList(users);
System.out.println("原始用户列表: " + users.size() + " 个用户");
System.out.println("映射结果: " + dtos.size() + " 个 DTO");
return dtos;
}
/**
* 演示 Map 映射
*/
@GetMapping("/products-map")
public Map<String, ProductDto> getProductsMap() {
Map<String, Product> products = new HashMap<>();
products.put("phone", createProduct(1L, "iPhone 15", new BigDecimal("8999")));
products.put("laptop", createProduct(2L, "MacBook Pro", new BigDecimal("15999")));
products.put("tablet", createProduct(3L, "iPad Pro", new BigDecimal("6999")));
// 🎯 Map 映射
Map<String, ProductDto> dtoMap = productMapper.toDtoMap(products);
System.out.println("原始产品 Map: " + products.size() + " 个产品");
System.out.println("映射结果: " + dtoMap.size() + " 个 DTO");
return dtoMap;
}
/**
* 演示嵌套对象映射
*/
@GetMapping("/order/{id}")
public OrderDto getOrder(@PathVariable Long id) {
// 创建复杂的订单对象
Order order = createComplexOrder(id);
// 🎯 复杂嵌套映射
OrderDto dto = orderMapper.toDto(order);
System.out.println("原始订单: " + order.getOrderNo());
System.out.println("订单项数量: " + order.getItems().size());
System.out.println("映射结果: " + dto.getOrderNo());
System.out.println("总金额: " + dto.getTotalAmount());
System.out.println("总商品数: " + dto.getTotalItems());
return dto;
}
/**
* 演示自引用映射
*/
@GetMapping("/categories")
public List<CategoryDto> getCategories() {
List<Category> categories = createCategoryTree();
// 🎯 自引用结构映射
List<CategoryDto> dtos = categoryMapper.toDtoList(categories);
System.out.println("原始分类树: " + categories.size() + " 个根分类");
System.out.println("映射结果: " + dtos.size() + " 个 DTO");
return dtos;
}
/**
* 演示浅层映射(避免深度递归)
*/
@GetMapping("/categories-shallow")
public List<CategoryDto> getCategoriesShallow() {
List<Category> categories = createCategoryTree();
// 🎯 浅层映射
List<CategoryDto> dtos = categoryMapper.toShallowDtoList(categories);
System.out.println("浅层映射结果: " + dtos.size() + " 个 DTO");
return dtos;
}
/**
* 演示社交网络复杂关系映射
*/
@GetMapping("/social-user/{id}")
public SocialUserDto getSocialUser(@PathVariable Long id) {
SocialUser user = createSocialUserWithRelations(id);
// 🎯 复杂关系映射
SocialUserDto dto = socialUserMapper.toDto(user);
System.out.println("原始用户: " + user.getUsername());
System.out.println("关注数: " + user.getFollowing().size());
System.out.println("粉丝数: " + user.getFollowers().size());
System.out.println("帖子数: " + user.getPosts().size());
System.out.println("映射结果 - 总点赞数: " + dto.getTotalLikes());
return dto;
}
/**
* 演示组织架构树形映射
*/
@GetMapping("/organization/{id}")
public OrganizationDto getOrganization(@PathVariable Long id) {
Organization org = createOrganizationTree(id);
// 🎯 树形结构映射(限制深度)
OrganizationDto dto = organizationMapper.toDtoWithDepthLimit(org);
System.out.println("原始组织: " + org.getName());
System.out.println("子组织数: " + (org.getChildren() != null ? org.getChildren().size() : 0));
System.out.println("映射结果 - 总员工数: " + dto.getTotalEmployeeCount());
System.out.println("组织路径: " + dto.getOrganizationPath());
return dto;
}
// 辅助方法创建测试数据
private User createUser(Long id, String name, String email) {
User user = new User();
user.setId(id);
user.setName(name);
user.setEmail(email);
return user;
}
private Product createProduct(Long id, String name, BigDecimal price) {
Product product = new Product();
product.setId(id);
product.setName(name);
product.setPrice(price);
return product;
}
private Order createComplexOrder(Long id) {
// 创建客户地址
Address address = new Address();
address.setProvince("广东省");
address.setCity("深圳市");
address.setDistrict("南山区");
address.setDetail("科技园南区");
// 创建客户
UserWithAddress customer = new UserWithAddress();
customer.setId(1001L);
customer.setName("张三");
customer.setEmail("zhangsan@example.com");
customer.setAddress(address);
// 创建订单项
List<OrderItem> items = Arrays.asList(
createOrderItem(1L, createProduct(1L, "iPhone 15", new BigDecimal("8999")), 1, new BigDecimal("8999")),
createOrderItem(2L, createProduct(2L, "保护壳", new BigDecimal("99")), 2, new BigDecimal("99"))
);
// 创建订单
Order order = new Order();
order.setId(id);
order.setOrderNo("ORD" + System.currentTimeMillis());
order.setCustomer(customer);
order.setItems(items);
order.setMetadata(Map.of("source", "mobile", "channel", "app"));
order.setTags(Set.of("urgent", "vip"));
order.setCreatedAt(LocalDateTime.now());
return order;
}
private OrderItem createOrderItem(Long id, Product product, Integer quantity, BigDecimal unitPrice) {
OrderItem item = new OrderItem();
item.setId(id);
item.setProduct(product);
item.setQuantity(quantity);
item.setUnitPrice(unitPrice);
return item;
}
private List<Category> createCategoryTree() {
// 创建分类树结构
Category electronics = new Category();
electronics.setId(1L);
electronics.setName("电子产品");
electronics.setDescription("各类电子设备");
Category phones = new Category();
phones.setId(2L);
phones.setName("手机");
phones.setDescription("智能手机");
phones.setParent(electronics);
Category laptops = new Category();
laptops.setId(3L);
laptops.setName("笔记本电脑");
laptops.setDescription("便携式电脑");
laptops.setParent(electronics);
electronics.setChildren(Arrays.asList(phones, laptops));
return Arrays.asList(electronics);
}
private SocialUser createSocialUserWithRelations(Long id) {
// 创建用户及其关系网络
SocialUser user = new SocialUser();
user.setId(id);
user.setUsername("user" + id);
user.setNickname("用户" + id);
user.setAvatar("avatar" + id + ".jpg");
// 创建关注和粉丝
Set<SocialUser> following = new HashSet<>();
Set<SocialUser> followers = new HashSet<>();
for (int i = 1; i <= 3; i++) {
SocialUser followingUser = new SocialUser();
followingUser.setId((long) (id + i));
followingUser.setUsername("following" + i);
followingUser.setNickname("关注用户" + i);
following.add(followingUser);
SocialUser follower = new SocialUser();
follower.setId((long) (id + i + 10));
follower.setUsername("follower" + i);
follower.setNickname("粉丝" + i);
followers.add(follower);
}
user.setFollowing(following);
user.setFollowers(followers);
// 创建帖子
List<Post> posts = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
Post post = new Post();
post.setId((long) i);
post.setContent("这是第" + i + "条帖子");
post.setCreatedAt(LocalDateTime.now().minusDays(i));
post.setAuthor(user);
// 模拟点赞
Set<SocialUser> likedBy = new HashSet<>(followers);
post.setLikedBy(likedBy);
posts.add(post);
}
user.setPosts(posts);
return user;
}
private Organization createOrganizationTree(Long id) {
// 创建组织架构树
Organization company = new Organization();
company.setId(id);
company.setName("科技有限公司");
company.setCode("TECH");
company.setLevel(1);
Organization techDept = new Organization();
techDept.setId(id + 1);
techDept.setName("技术部");
techDept.setCode("TECH-DEV");
techDept.setLevel(2);
techDept.setParent(company);
Organization frontendTeam = new Organization();
frontendTeam.setId(id + 2);
frontendTeam.setName("前端团队");
frontendTeam.setCode("TECH-DEV-FE");
frontendTeam.setLevel(3);
frontendTeam.setParent(techDept);
Organization backendTeam = new Organization();
backendTeam.setId(id + 3);
backendTeam.setName("后端团队");
backendTeam.setCode("TECH-DEV-BE");
backendTeam.setLevel(3);
backendTeam.setParent(techDept);
techDept.setChildren(Arrays.asList(frontendTeam, backendTeam));
company.setChildren(Arrays.asList(techDept));
// 添加员工
List<Employee> employees = Arrays.asList(
createEmployee(1L, "张三", "zhangsan@company.com"),
createEmployee(2L, "李四", "lisi@company.com")
);
frontendTeam.setEmployees(employees);
return company;
}
private Employee createEmployee(Long id, String name, String email) {
Employee employee = new Employee();
employee.setId(id);
employee.setName(name);
employee.setEmail(email);
return employee;
}
}
测试映射效果
bash
# 测试基础集合映射
curl http://localhost:8080/api/collection-mapping/users
# 期望返回:
# [
# {"id": 1, "name": "张三", "email": "zhangsan@example.com"},
# {"id": 2, "name": "李四", "email": "lisi@example.com"},
# {"id": 3, "name": "王五", "email": "wangwu@example.com"}
# ]
# 测试 Map 映射
curl http://localhost:8080/api/collection-mapping/products-map
# 期望返回:
# {
# "phone": {"id": 1, "name": "iPhone 15", "price": "¥8,999.00"},
# "laptop": {"id": 2, "name": "MacBook Pro", "price": "¥15,999.00"},
# "tablet": {"id": 3, "name": "iPad Pro", "price": "¥6,999.00"}
# }
# 测试复杂嵌套映射
curl http://localhost:8080/api/collection-mapping/order/1001
# 期望返回:
# {
# "id": 1001,
# "orderNo": "ORD1704787200000",
# "customer": {
# "id": 1001,
# "name": "张三",
# "email": "zhangsan@example.com",
# "address": {
# "province": "广东省",
# "city": "深圳市",
# "district": "南山区",
# "detail": "科技园南区",
# "fullAddress": "广东省深圳市南山区科技园南区"
# }
# },
# "items": [
# {
# "id": 1,
# "product": {"id": 1, "name": "iPhone 15", "price": "¥8,999.00"},
# "quantity": 1,
# "unitPrice": "¥8,999.00",
# "totalPrice": "¥8,999.00"
# }
# ],
# "totalAmount": "¥9,197.00",
# "totalItems": 3,
# "metadata": {"source": "mobile", "channel": "app"},
# "tags": ["urgent", "vip"]
# }
# 测试自引用映射
curl http://localhost:8080/api/collection-mapping/categories
# 测试组织架构映射
curl http://localhost:8080/api/collection-mapping/organization/1000
❓ 常见问题
Q1: 如何避免循环引用导致的无限递归?
A: 几种解决方案:
java
// 方案1:使用 @Mapping(ignore = true) 忽略循环引用字段
@Mapping(target = "parent", ignore = true)
CategoryDto toDto(Category category);
// 方案2:创建专门的浅层映射方法
@Mapping(target = "children", ignore = true)
@Mapping(target = "parent", ignore = true)
CategoryDto toShallowDto(Category category);
// 方案3:使用深度限制
default List<CategoryDto> mapWithDepthLimit(List<Category> categories, int maxDepth) {
if (maxDepth <= 0) return Collections.emptyList();
// 递归映射逻辑...
}
Q2: 集合映射的性能如何优化?
A: 性能优化策略:
java
// 1. 使用并行流处理大集合
default List<UserDto> toDtoListParallel(List<User> users) {
return users.parallelStream()
.map(this::toDto)
.collect(Collectors.toList());
}
// 2. 批量查询避免 N+1 问题
@Mapping(target = "extraInfo", ignore = true)
UserDto toDtoWithoutExtra(User user);
// 3. 使用缓存避免重复转换
private final Map<Long, UserDto> cache = new ConcurrentHashMap<>();
default UserDto toDtoWithCache(User user) {
return cache.computeIfAbsent(user.getId(), id -> toDto(user));
}
Q3: 如何处理不同类型集合之间的转换?
A: Atlas Mapper 支持自动转换:
java
// List ↔ Set 自动转换
List<UserDto> listToSet(Set<User> users);
Set<UserDto> setToList(List<User> users);
// Array ↔ List 自动转换
List<UserDto> arrayToList(User[] users);
User[] listToArray(List<UserDto> dtos);
// 自定义转换逻辑
default Set<UserDto> toUniqueSet(List<User> users) {
return users.stream()
.map(this::toDto)
.collect(Collectors.toSet());
}
Q4: Map 映射时如何处理复杂的 Key 类型?
A: 处理复杂 Key 的方法:
java
// 简单 Key 类型直接映射
Map<String, UserDto> stringKeyMap(Map<String, User> users);
Map<Long, UserDto> longKeyMap(Map<Long, User> users);
// 复杂 Key 类型需要自定义转换
default Map<String, UserDto> complexKeyMap(Map<UserKey, User> users) {
return users.entrySet().stream()
.collect(Collectors.toMap(
entry -> entry.getKey().toString(), // Key 转换
entry -> toDto(entry.getValue()) // Value 转换
));
}
🎯 本章小结
通过本章学习,你应该掌握了:
- 集合映射:List、Set、Map、Array 等集合类型的映射
- 嵌套对象:单层和多层嵌套对象的处理
- 复杂结构:自引用、循环引用、树形结构的映射
- 性能优化:避免无限递归和提升映射性能的技巧
📖 下一步学习
在下一篇教程中,我们将学习:
- Spring Boot 集成和自动配置
- 依赖注入和 Bean 管理
- 配置属性和自定义配置