My logo
Published on

Java-Mapstruct 实践

1. 基本映射

1.1 简单字段映射

@Mapper
public interface UserMapper {
    @Mapping(source = "firstName", target = "name")
    @Mapping(source = "age", target = "userAge")
    UserDto userToUserDto(User user);
}

1.2 多个源对象映射到一个目标对象

@Mapper
public interface OrderMapper {
    @Mapping(source = "order.id", target = "orderId")
    @Mapping(source = "customer.name", target = "customerName")
    OrderDto orderAndCustomerToOrderDto(Order order, Customer customer);
}

2. 集合映射

2.1 列表映射

@Mapper
public interface ProductMapper {
    List<ProductDto> productsToProductDtos(List<Product> products);
}

🔊🔊 当进行列表映射,且源实体和目标实体的属性名称不同时,MapStruct 提供了几种处理方法。以下是详细说明:

首先,让我们定义源实体和目标实体:

public class Product {
    private Long id;
    private String productName;
    private BigDecimal cost;
    // getters and setters
}

public class ProductDto {
    private Long productId;
    private String name;
    private BigDecimal price;
    // getters and setters
}

然后,我们可以这样定义映射器:

@Mapper
public interface ProductMapper {
    @Mapping(source = "id", target = "productId")
    @Mapping(source = "productName", target = "name")
    @Mapping(source = "cost", target = "price")
    ProductDto productToProductDto(Product product);

    List<ProductDto> productsToProductDtos(List<Product> products);
}

在这个例子中(🧑‍💻🧑‍💻):

  • productToProductDto 方法定义了单个对象的映射规则。
  • productsToProductDtos 方法用于列表映射。MapStruct 会自动使用 productToProductDto 方法的规则来映射列表中的每个元素。

自定义映射方法

@Mapper
public abstract class ProductMapper {
    @Mapping(source = "id", target = "productId")
    @Mapping(source = "productName", target = "name")
    @Mapping(source = "cost", target = "price")
    abstract ProductDto productToProductDto(Product product);

    public List<ProductDto> productsToProductDtos(List<Product> products) {
        if (products == null) {
            return null;
        }

        List<ProductDto> list = new ArrayList<>(products.size());
        for (Product product : products) {
            list.add(productToProductDto(product));
        }
        return list;
    }

    // 可以添加一些自定义的辅助方法
    protected BigDecimal adjustPrice(BigDecimal cost) {
        // 例如,增加 10% 的利润
        return cost.multiply(new BigDecimal("1.1"));
    }
}

使用 @IterableMapping

如果需要对列表映射应用额外的配置,可以使用 @IterableMapping 注解:

@Mapper
public interface ProductMapper {
    @Mapping(source = "id", target = "productId")
    @Mapping(source = "productName", target = "name")
    @Mapping(source = "cost", target = "price")
    ProductDto productToProductDto(Product product);

    @IterableMapping(elementTargetType = ProductDto.class)
    List<ProductDto> productsToProductDtos(List<Product> products);
}

这在处理泛型或需要明确指定目标类型时特别有用。

处理不同类型的集合,MapStruct 也支持在不同类型的集合之间进行映射:

@Mapper
public interface ProductMapper {
    @Mapping(source = "id", target = "productId")
    @Mapping(source = "productName", target = "name")
    @Mapping(source = "cost", target = "price")
    ProductDto productToProductDto(Product product);

    List<ProductDto> productsToProductDtos(Collection<Product> products);

    Set<ProductDto> productsToProductDtoSet(List<Product> products);
}

带有条件的列表映射,如果需要在映射过程中应用一些条件,可以结合使用 @Mapping 和自定义方法:

@Mapper
public abstract class ProductMapper {
    @Mapping(source = "id", target = "productId")
    @Mapping(source = "productName", target = "name")
    @Mapping(source = "cost", target = "price")
    @Mapping(target = "onSale", expression = "java(isOnSale(product))")
    abstract ProductDto productToProductDto(Product product);

    abstract List<ProductDto> productsToProductDtos(List<Product> products);

    protected boolean isOnSale(Product product) {
        // 实现判断商品是否在销售的逻辑
        return product.getCost().compareTo(new BigDecimal("100")) < 0;
    }
}

2.2 Map映射

@Mapper
public interface InventoryMapper {
    Map<String, Integer> inventoryToStockMap(Map<Product, Integer> inventory);
}

3. 嵌套对象映射

@Mapper
public interface AddressMapper {
    @Mapping(source = "user.address.street", target = "streetName")
    @Mapping(source = "user.address.city", target = "cityName")
    AddressDto userToAddressDto(User user);
}

4. 默认值和常量

@Mapper
public interface EmployeeMapper {
    @Mapping(target = "employeeId", source = "entity.id")
    @Mapping(target = "employeeName", source = "entity.name")
    @Mapping(target = "employeeStartDt", source = "entity.startDate", dateFormat = "dd-MM-yyyy HH:mm:ss")
    @Mapping(target = "status", constant = "ACTIVE")
    EmployeeDto employeeToEmployeeDTO(Employee entity);
}

5. 自定义方法

@Mapper
public abstract class UserMapper {
    @Mapping(target = "fullName", expression = "java(fullName(user))")
    public abstract UserDto userToUserDto(User user);

    protected String fullName(User user) {
        return user.getFirstName() + " " + user.getLastName();
    }
}

6. 继承映射

@Mapper
public interface VehicleMapper {
    VehicleDto vehicleToVehicleDto(Vehicle vehicle);

    @InheritConfiguration
    CarDto carToCarDto(Car car);
}

7. 更新已存在的实例

@Mapper
public interface CustomerMapper {
    void updateCustomerFromDto(CustomerDto dto, @MappingTarget Customer customer);
}

8. 忽略某些字段

@Mapper
public interface ProductMapper {
    @Mapping(target = "id", ignore = true)
    @Mapping(target = "createdAt", ignore = true)
    Product dtoToProduct(ProductDto dto);
}

9. 条件映射

@Mapper
public interface OrderMapper {
    @Mapping(target = "customerName", source = "customer.name")
    @Mapping(target = "priorityOrder", source = "priority",
             condition = "java(priority > 5)")
    OrderDto orderToOrderDto(Order order);
}

参考链接