通常,我们需要将域模型转换为DTO(数据传输对象),反之亦然。将数据传输到前端或远程接口时,这是必需的。手动处理复杂的映射变得很麻烦,并可能导致错误。

在本文中,我想介绍MapStruct,这是一个Java注释处理器,可以在编译时为Java bean生成映射器实现。它使用简单的Java方法调用来映射对象,并且不涉及任何反射或运行时处理。
我将引导您完成将MapStruct集成到Spring Boot应用程序中的步骤。
Maven依赖
让我们创建一个示例Spring Boot应用程序,并在pom.xml中添加MapStruct及其处理器依赖性。
所述mapstruct处理器用于在编译期间产生映射器实现。
org.mapstruct
mapstruct-jdk8
1.3.0.Final
org.mapstruct
mapstruct-processor
1.3.0.Final
provided
创建JPA实体和DTO
让我们创建两个实体Library和Book。
所述mapstruct处理器用于在编译期间产生映射器实现。
package
com.swathisprasad.mapstruct.dao.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Data
@EqualsAndHashCode
@Entitypublic
class Library implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue (generator = "uuid")
@GenericGenerator (name = "uuid", strategy = "org.hibernate.id.UUIDGenerator")
@Column (name = "id", updatable = false, nullable = false)
private UUID id;
@NotNull
@Column (name = "name", nullable = false)
private String name;
@EqualsAndHashCode.Exclude
@OneToMany(mappedBy = "library", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List books = new ArrayList<>();}
package com.swathisprasad.mapstruct.dao.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.UUID;
@Data
@EqualsAndHashCode
@Entitypublic
class Book implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue (generator="uuid")
@GenericGenerator (name="uuid", strategy="org.hibernate.id.UUIDGenerator")
@Column (name="id", updatable=false, nullable=false)
private UUID id;
@NotNull
@Column (name="name", nullable=false)
private String name;
@NotNull
@Column (name="author", nullable=false)
private String author;
@NotNull
@Column (name="published_date", nullable=false)
private LocalDateTime publishedDate;
@EqualsAndHashCode.Exclude
@ToString.Exclude
@ManyToOne(optional=false)
@JoinColumn (name="library_id", nullable=false)
private Library library;
}
在这里,我们在Library和Book之间存在一对多的双向关系。另外,我在项目中添加了Lombok,以避免在实体类中编写样板代码,例如getter,setter,hashCode()和Equals()。
让我们创建相应的DTO类。
package com.swathisprasad.mapstruct.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Datapublic class LibraryDTO implements Serializable {
private UUID id;
@NotNull
private String name;
@NotNull
private List bookDTOs = new ArrayList<>();
}
package com.swathisprasad.mapstruct.dto;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.UUID;
@Datapublic
class BookDTO implements Serializable{
private UUID id;
@NotNull
private String name;
@NotNull
private String author;
@NotNull
private LocalDateTime publishedDate;
private UUID libraryId;
}
Bean Mappers
现在,我们将创建使用MapStruct的映射器接口。
package com.swathisprasad.mapstruct.dto.mapper;
import com.swathisprasad.mapstruct.dao.entity.Library;
import com.swathisprasad.mapstruct.dto.LibraryDTO;
import org.mapstruct.Mapper;import org.mapstruct.Mapping;
import java.util.UUID;
@Mapper(componentModel = "spring", uses = {BookMapper.class})
public interface LibraryMapper extends IEntityMapper {
@Mapping(source = "books", target = "bookDTOs")
LibraryDTO toDto(final Library library);
Library toEntity(final LibraryDTO libraryDTO);
default Library fromId(final UUID id) {
if (id == null) {
return null;
}
final Library library=new Library();
library.setId(id);
return library;
}
正如您在此处注意到的那样,由于MapStruct为我们生成了代码,因此我们没有编写任何实现。映射器接口必须使用@Mapper注释进行注释。在这里,componentModel属性生成了一个单例作用域的Spring bean映射器,可以在需要时直接注入它。由于我们已经定义了Library和Book之间的一对多关系,因此我们需要映射子对象(即Book对象的列表),因此可以通过uses属性注入其他BookMapper。
我们告诉Mapstruct映射一对多关系中的子对象,即在本例中通过@Mapping注释进行预订。
package com.swathisprasad.mapstruct.dto.mapper;
import com.swathisprasad.mapstruct.dao.entity.Book;
import com.swathisprasad.mapstruct.dto.BookDTO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import java.util.List;import java.util.UUID;
@Mapper(componentModel = "spring", uses = {LibraryMapper.class})
public interface BookMapper extends IEntityMapper {
@Mapping(source="library.id", target="libraryId")
BookDTO toDto(final Book book);
List toDto(final List book);
@Mapping(source="libraryId", target="library")
Book toEntity(final BookDTO bookDTO);
List toEntity(final List bookDTOs);
default Book fromId(final UUID id) {
if (id == null) {
return null;
}
final Book book=new Book();
book.setId(id);
return book;
}}
我们可以执行mvn clean install或mvn clean verify来触发MapStruct处理,这将在/ target / generate-sources / annotations /下生成实现类。
测试映射器接口
让我们为Library和Book创建JPA存储库。
package com.swathisprasad.mapstruct.dao.repository;
import com.swathisprasad.mapstruct.dao.entity.Library;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@Repositorypublic
interface ILibraryRepository extends JpaRepository {}
package com.swathisprasad.mapstruct.dao.repository;
import com.swathisprasad.mapstruct.dao.entity.Book;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@Repositorypublic interface IBookRepository extends JpaRepository {}
创建一个LibraryService以将Library和Book实体保存在数据库中。
package com.swathisprasad.mapstruct.service;
import com.swathisprasad.mapstruct.dao.entity.Book;
import com.swathisprasad.mapstruct.dao.entity.Library;
import com.swathisprasad.mapstruct.dao.repository.IBookRepository;
import com.swathisprasad.mapstruct.dao.repository.ILibraryRepository;
import com.swathisprasad.mapstruct.dto.LibraryDTO;
import com.swathisprasad.mapstruct.dto.mapper.BookMapper;
import com.swathisprasad.mapstruct.dto.mapper.LibraryMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
@Service
@Transactionalpublic
class LibraryService {
private final ILibraryRepository libraryRepository;
private final IBookRepository bookRepository;
private final LibraryMapper libraryMapper;
private final BookMapper bookMapper;
public LibraryService(final ILibraryRepository libraryRepository, final IBookRepository bookRepository,final LibraryMapper libraryMapper, final BookMapper bookMapper) {
this.libraryRepository = libraryRepository;this.bookRepository = bookRepository;this.libraryMapper = libraryMapper;
this.bookMapper = bookMapper;
}
public LibraryDTO save(final LibraryDTO libraryDTO) {
final Library library = libraryMapper.toEntity(libraryDTO);
final Library createdLibrary = libraryRepository.save(library);
final List books = new ArrayList<>();
libraryDTO.getBookDTOs().forEach(bookDTO -> {
final Book book = bookMapper.toEntity(bookDTO);
book.setLibrary(createdLibrary);
books.add(book);
});
createdLibrary.setBooks(books);
bookRepository.saveAll(books);
return libraryMapper.toDto(createdLibrary);
}}
这是测试用例:
package com.swathisprasad.mapstruct.service;
import com.swathisprasad.mapstruct.Application;
import com.swathisprasad.mapstruct.dto.BookDTO;
import com.swathisprasad.mapstruct.dto.LibraryDTO;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith (SpringExtension.class)
@SpringBootTest (classes = Application.class)public class LibraryServiceTest{
@Autowiredprivate LibraryService libraryService;
@Testpublic void saveLibrary()
{
final LibraryDTO libraryDTO=new LibraryDTO();
libraryDTO.setName("Library");
final List books=new ArrayList<>();
final BookDTO book1=new BookDTO();
book1.setName("Book1");
book1.setAuthor("Author1");
book1.setPublishedDate(LocalDateTime.now());
books.add(book1);
final BookDTO book2=new BookDTO();
book2.setName("Book2");
book2.setAuthor("Author2");
book2.setPublishedDate(LocalDateTime.now().minusDays(10));
books.add(book2);
libraryDTO.setBookDTOs(books);
final LibraryDTO createdLibraryDTO = libraryService.save(libraryDTO);
final List createdBooks=createdLibraryDTO.getBookDTOs();
assertThat(createdBooks).hasSize(2);
createdBooks.forEach(bookDTO -> assertThat(bookDTO.getLibraryId()).isEqualTo(createdLibraryDTO.getId()));
}}
结论
旨在通过使其尽可能自动化来简化工作。本文只是介绍了MapStruct的功能。