Статьи

MapStruct: передача данных из одного компонента в другой

Преобразование данных из одной формы в другую является широко используемой концепцией в ИТ-индустрии. MapStruct позволяет преобразовывать bean-компоненты на основе аннотаций, генерируя реализацию mapper во время компиляции. Это гарантирует, что во время выполнения не будет проблем с производительностью.

Что такое MapStruct?

MapStruct — это генератор кода, который значительно упрощает реализацию отображений между типами Java-бинов на основе соглашения о конфигурации.

Сгенерированный код отображения использует простые вызовы методов и поэтому быстр, безопасен для типов и прост для понимания.

Почему MapStruct?

Многоуровневые приложения часто требуют отображения между различными объектными моделями (например, объектами и DTO). Написание такого кода отображения является утомительной и подверженной ошибкам задачей. MapStruct стремится упростить эту работу, максимально автоматизируя ее.

В отличие от других структур отображения MapStruct генерирует отображения компонентов во время компиляции, что обеспечивает высокую производительность, обеспечивает быструю обратную связь с разработчиком и тщательную проверку ошибок.

Реализация

pom.xml

В web.xml добавьте « maven-compiler-plugin » и с идентификатором группы « org.apache.maven.plugins «. Вы можете добавить конкретную исходную / целевую версию jdk и получить последнюю доступную версию с
Сайт MapStruct .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-compiler-plugin</artifactId>
 <version>3.5.1</version>
 <configuration>
  <source>1.6</source> <!-- or higher, depending on your project -->
  <target>1.6</target> <!-- or higher, depending on your project -->
  <annotationProcessorPaths>
   <path>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.1.0.Beta1</version>
   </path>
  </annotationProcessorPaths>
 </configuration>
</plugin>

Теперь добавьте jar mapstruct в качестве зависимости.

1
2
3
4
5
<dependency>
 <groupId>org.mapstruct</groupId>
 <artifactId>mapstruct</artifactId>
 <version>1.1.0.Beta1</version>
</dependency>

Постановка проблемы и решение

Предположим, у нас есть два pojos, представляющих личные и деловые контакты, как указано ниже, и мы используем оба на конкретных jsps. Теперь для функциональности, где оба контакта одинаковы, нам нужно передавать данные из одного pojo в другое.

PrimaryContact.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class PrimaryContact {
 
 private String name;
 private String phone;
 private String email;
 
 public PrimaryContact() {
  super();
 }
 
 public PrimaryContact(String name, String phone, String email) {
  super();
  this.name = name;
  this.phone = phone;
  this.email = email;
 }
 
 public String getName() {
  return name;
 }
 
 public void setName(String name) {
  this.name = name;
 }
 
 public String getPhone() {
  return phone;
 }
 
 public void setPhone(String phone) {
  this.phone = phone;
 }
 
 public String getEmail() {
  return email;
 }
 
 public void setEmail(String email) {
  this.email = email;
 }
 
 @Override
 public String toString() {
  return "PrimaryContact [name=" + name + ", phone=" + phone + ", email=" + email + "]";
 }
 
}

BusinessContact.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class BusinessContact {
 
 private String firstName;
 private String lastName;
 private String businessPhone;
 private String businessEmail;
 private String businessCountry;
 
 public BusinessContact() {
  super();
 }
 
 public BusinessContact(String firstName, String lastName, String businessPhone, String businessEmail,
   String businessCountry) {
  super();
  this.firstName = firstName;
  this.lastName = lastName;
  this.businessPhone = businessPhone;
  this.businessEmail = businessEmail;
  this.businessCountry = businessCountry;
 }
 
 public String getFirstName() {
  return firstName;
 }
 
 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }
 
 public String getLastName() {
  return lastName;
 }
 
 public void setLastName(String lastName) {
  this.lastName = lastName;
 }
 
 public String getBusinessPhone() {
  return businessPhone;
 }
 
 public void setBusinessPhone(String businessPhone) {
  this.businessPhone = businessPhone;
 }
 
 public String getBusinessEmail() {
  return businessEmail;
 }
 
 public void setBusinessEmail(String businessEmail) {
  this.businessEmail = businessEmail;
 }
 
 public String getBusinessCountry() {
  return businessCountry;
 }
 
 public void setBusinessCountry(String businessCountry) {
  this.businessCountry = businessCountry;
 }
 
 @Override
 public String toString() {
  return "BusinessContact [firstName=" + firstName + ", lastName=" + lastName + ", businessPhone=" + businessPhone
    + ", businessEmail=" + businessEmail + ", businessCountry=" + businessCountry + "]";
 }
 
}

Мы пишем Mapper для передачи данных, как показано ниже. Аннотация @Mappings определяет, какие атрибуты из исходного pojo будут переданы конкретному атрибуту в целевом pojo. Аннотация определяет, что обратное отображение @InheritInverseConfiguration должно быть выполнено.

ContactMapper.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
/**
 * @author javareferencegv
 */
@Mapper
@DecoratedWith(ContactMapperDecorator.class)
public interface ContactMapper {
 ContactMapper INSTANCE = Mappers.getMapper(ContactMapper.class);
     
 /**
  * We define only those mappings which doesn't have same signature in source and target
  */  
    @Mappings({
     @Mapping(source = "phone", target = "businessPhone"),
     @Mapping(source = "email", target = "businessEmail"),
     @Mapping(target = "businessCountry", constant="USA")
    })
    BusinessContact primaryToBusinessContact(PrimaryContact primary);
    @InheritInverseConfiguration
    PrimaryContact businessToPrimaryContact(BusinessContact business);
    
}

Будут сценарии, в которых сопоставление не является прямым, и нам нужна настраиваемая логика перед сопоставлением одного атрибута с другим. Одним из таких примеров здесь является то, что основной контакт имеет полное имя, а деловой контакт — имя и фамилия. В таком сценарии мы используем Decorator для добавления пользовательской реализации. Это определенная аннотация @Decorated с добавлением в маппер. Реализация для декоратора выглядит следующим образом:

ContactMapperDecorator.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public abstract class ContactMapperDecorator implements ContactMapper{
  
 private final ContactMapper delegate;
   
    public ContactMapperDecorator(ContactMapper delegate) {
        this.delegate = delegate;
    }
     
    @Override
    public BusinessContact primaryToBusinessContact(PrimaryContact primary){
     BusinessContact business = delegate.primaryToBusinessContact(primary); //Executes the mapper
     String[] names = primary.getName().split(" ");
     business.setFirstName(names[0]);
     business.setLastName(names[1]);
     return business;
    }
     
    @Override
    public PrimaryContact businessToPrimaryContact(BusinessContact business){
     PrimaryContact primary = delegate.businessToPrimaryContact(business); //Executes the mapper
     primary.setName(business.getFirstName() + " " + business.getLastName());
     return primary;
    }
     
}

Исполнение:

После того, как мы создадим файл класса реализации, будет сгенерирован mapstruct. Мы все настроены на запуск картографа.

01
02
03
04
05
06
07
08
09
10
11
public class ContactConvertor {
 
 public static void main(String[] args) {
  PrimaryContact primary = new PrimaryContact("Jack Sparrow","9999999999","[email protected]");
  BusinessContact business = ContactMapper.INSTANCE.primaryToBusinessContact(primary);
  System.out.println(business);
  PrimaryContact primaryConverted = ContactMapper.INSTANCE.businessToPrimaryContact(business);
  System.out.println(primaryConverted);
 }
 
}

Выход :

1
2
BusinessContact [firstName=Jack, lastName=Sparrow, businessPhone=9999999999, businessEmail=test@javareferencegv.com, businessCountry=USA]
PrimaryContact [name=Jack Sparrow, phone=9999999999, email=test@javareferencegv.com]