基础使用

ModelMapper

ModelMapper 是轻量级的帮助我们在不同的 Java POJO 之间进行转换的工具库,其引入方式如下:

<dependency>
  <groupId>org.modelmapper</groupId>
  <artifactId>modelmapper</artifactId>
  <version>2.3.0</version>
</dependency>

基础使用

Mapping

// Source Model
// Assume getters and setters on each class
class Order {
  Customer customer;
  Address billingAddress;
}

class Customer {
  Name name;
}

class Name {
  String firstName;
  String lastName;
}

class Address {
  String street;
  String city;
}

// Destination Model
// Assume getters and setters
class OrderDTO {
  String customerFirstName;
  String customerLastName;
  String billingStreet;
  String billingCity;
}

然后可以使用 ModelMapper 来将 order 实例映射到 OrderDTO:

ModelMapper modelMapper = new ModelMapper();
OrderDTO orderDTO = modelMapper.map(order, OrderDTO.class);

assertEquals(order.getCustomer().getName().getFirstName(), orderDTO.getCustomerFirstName());
assertEquals(order.getCustomer().getName().getLastName(), orderDTO.getCustomerLastName());
assertEquals(order.getBillingAddress().getStreet(), orderDTO.getBillingStreet());
assertEquals(order.getBillingAddress().getCity(), orderDTO.getBillingCity());

Explicit Mapping

很多时候我们也可以手动地指明映射规则:

modelMapper.addMappings(mapper -> {
  mapper.map(src -> src.getBillingAddress().getStreet(),
      Destination::setBillingStreet);
  mapper.map(src -> src.getBillingAddress().getCity(),
      Destination::setBillingCity);
});

Spring 中 Entity 与 DTO 的转化

DTO 层

以 Post 为例,我们的 DTO 定义如下:

public class PostDto {
  private static final SimpleDateFormat dateFormat = new SimpleDateFormat(
    "yyyy-MM-dd HH:mm"
  );

  private Long id;

  private String title;

  private String url;

  private String date;

  private UserDto user;

  public Date getSubmissionDateConverted(String timezone)
    throws ParseException {
    dateFormat.setTimeZone(TimeZone.getTimeZone(timezone));
    return dateFormat.parse(this.date);
  }

  public void setSubmissionDate(Date date, String timezone) {
    dateFormat.setTimeZone(TimeZone.getTimeZone(timezone));
    this.date = dateFormat.format(date);
  }
// standard getters and setters
}

这里值得注意的是对于两个日期相关的处理方法:

  • getSubmissionDateConverted() 方法根据传入的 timezone 将当前时间转化为服务端内置的 Post 实例所需要的时区。
  • setSubmissionDate 则是将 Post 时区转化为用户当前的时区,以返回给用户。

Service 层

服务层的操作如下:

public List<Post> getPostsList(
  int page, int size, String sortDir, String sort) {

    PageRequest pageReq
     = PageRequest.of(page, size, Sort.Direction.fromString(sortDir), sort);

    Page<Post> posts = postRepository
      .findByUser(userService.getCurrentUser(), pageReq);

    return posts.getContent();
}

Controller 层

Controller 中会调用对应的 Service 中的方法来获取数据,并且将其转化为用户侧的 DTO 数据:

@Controller
class PostRestController {

    @Autowired
    private IPostService postService;

    @Autowired
    private IUserService userService;

    @Autowired
    private ModelMapper modelMapper;

    @GetMapping
    @ResponseBody
    public List<PostDto> getPosts(...) {
        //...
        List<Post> posts = postService.getPostsList(page, size, sortDir, sort);
        return posts.stream()
          .map(this::convertToDto)
          .collect(Collectors.toList());
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    @ResponseBody
    public PostDto createPost(@RequestBody PostDto postDto) {
        Post post = convertToEntity(postDto);
        Post postCreated = postService.createPost(post));
        return convertToDto(postCreated);
    }

    @GetMapping(value = "/{id}")
    @ResponseBody
    public PostDto getPost(@PathVariable("id") Long id) {
        return convertToDto(postService.getPostById(id));
    }

    @PutMapping(value = "/{id}")
    @ResponseStatus(HttpStatus.OK)
    public void updatePost(@RequestBody PostDto postDto) {
        Post post = convertToEntity(postDto);
        postService.updatePost(post);
    }
}

我们实际的转化函数定义如下:

private PostDto convertToDto(Post post) {
    PostDto postDto = modelMapper.map(post, PostDto.class);
    postDto.setSubmissionDate(post.getSubmissionDate(),
        userService.getCurrentUser().getPreference().getTimezone());
    return postDto;
}

private Post convertToEntity(PostDto postDto) throws ParseException {
    Post post = modelMapper.map(postDto, Post.class);
    post.setSubmissionDate(postDto.getSubmissionDateConverted(
      userService.getCurrentUser().getPreference().getTimezone()));

    if (postDto.getId() != null) {
        Post oldPost = postService.getPostById(postDto.getId());
        post.setRedditID(oldPost.getRedditID());
        post.setSent(oldPost.isSent());
    }
    return post;
}
下一页