Atlas Mapper 教程系列 (5/10):集合映射与嵌套对象处理

🎯 学习目标

通过本篇教程,你将学会:

  • 掌握 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 转换
            ));
}

🎯 本章小结

通过本章学习,你应该掌握了:

  1. 集合映射:List、Set、Map、Array 等集合类型的映射
  2. 嵌套对象:单层和多层嵌套对象的处理
  3. 复杂结构:自引用、循环引用、树形结构的映射
  4. 性能优化:避免无限递归和提升映射性能的技巧

📖 下一步学习

在下一篇教程中,我们将学习:

  • Spring Boot 集成和自动配置
  • 依赖注入和 Bean 管理
  • 配置属性和自定义配置
相关推荐
ERP老兵_冷溪虎山2 小时前
Python/JS/Go/Java同步学习(第十三篇)四语言“字符串转码解码“对照表: 财务“小南“纸式转码术处理凭证乱码崩溃(附源码/截图/参数表/避坑指南)
java·后端·python
是2的10次方啊2 小时前
如何设计10万QPS秒杀系统?缓存+消息队列+分布式锁架构实战
java
心灵宝贝2 小时前
Tomcat Connectors 1.2.37 源码编译安装教程(mod_jk 详细步骤)
java·tomcat
杨杨杨大侠2 小时前
Atlas Mapper 教程系列 (6/10):Spring Boot 集成与自动配置
java·开源·github
傻傻虎虎2 小时前
【Docker】容器端口暴露+镜像生成实战
java·docker·容器
练习时长一年2 小时前
搭建langchain4j+SpringBoot的Ai项目
java·spring boot·后端
九术沫3 小时前
装饰器模式在Spring中的案例
java·spring·装饰器模式
Rysxt_3 小时前
Spring Boot 集成 Spring AI OpenAI Starter 教程
java·spring boot·后端·ai
青云交3 小时前
Java 大视界 -- Java 大数据在智能家居场景联动与用户行为模式挖掘中的应用
java·大数据·智能家居·边缘计算·户型适配·行为挖掘·场景联动