«1. Обзор

В этом руководстве мы познакомимся с JMapper — быстрой и простой в использовании картографической структурой.

Мы обсудим различные способы настройки JMapper, как выполнять пользовательские преобразования, а также реляционное сопоставление.

2. Конфигурация Maven

Во-первых, нам нужно добавить зависимость JMapper в наш pom.xml:

<dependency>
    <groupId>com.googlecode.jmapper-framework</groupId>
    <artifactId>jmapper-core</artifactId>
    <version>1.6.0.1</version>
</dependency>

3. Исходная и целевая модели

Прежде чем мы перейдем к настройке, давайте рассмотрим посмотрите на простые bean-компоненты, которые мы будем использовать в этом уроке.

Во-первых, вот наш исходный компонент — базовый пользователь:

public class User {
    private long id;    
    private String email;
    private LocalDate birthDate;
}

И наш целевой компонент, UserDto:

public class UserDto {
    private long id;
    private String username;
}

Мы будем использовать библиотеку для сопоставления атрибутов нашего исходного компонента User с наш целевой компонент UserDto.

Существует три способа настройки JMapper: с помощью API, аннотаций и конфигурации XML.

В следующих разделах мы рассмотрим каждый из них.

4. Использование API

Давайте посмотрим, как настроить JMapper с помощью API.

Здесь нам не нужно добавлять какую-либо конфигурацию в исходный и конечный классы. Вместо этого всю настройку можно выполнить с помощью JMapperAPI, что делает его наиболее гибким методом настройки:

@Test
public void givenUser_whenUseApi_thenConverted(){
    JMapperAPI jmapperApi = new JMapperAPI() 
      .add(mappedClass(UserDto.class)
        .add(attribute("id").value("id"))
        .add(attribute("username").value("email")));

    JMapper<UserDto, User> userMapper = new JMapper<>
      (UserDto.class, User.class, jmapperApi);
    User user = new User(1L,"[email protected]", LocalDate.of(1980,8,20));
    UserDto result = userMapper.getDestination(user);

    assertEquals(user.getId(), result.getId());
    assertEquals(user.getEmail(), result.getUsername());
}

Здесь мы используем метод mappedClass() для определения нашего сопоставленного класса UserDto. Затем мы использовали метод attribute() для определения каждого атрибута и его сопоставленного значения.

Затем мы создали объект JMapper на основе конфигурации и использовали его метод getDestination() для получения результата UserDto.

5. Использование аннотаций

Давайте посмотрим, как мы можем использовать аннотацию @JMap для настройки нашего отображения:

public class UserDto {  
    @JMap
    private long id;

    @JMap("email")
    private String username;
}

А вот как мы будем использовать наш JMapper:


@Test
public void givenUser_whenUseAnnotation_thenConverted(){
    JMapper<UserDto, User> userMapper = new JMapper<>(UserDto.class, User.class);
    User user = new User(1L,"[email protected]", LocalDate.of(1980,8,20));
    UserDto result = userMapper.getDestination(user);

    assertEquals(user.getId(), result.getId());
    assertEquals(user.getEmail(), result.getUsername());        
}

Обратите внимание, что для Для атрибута id нам не нужно было указывать имя целевого поля, поскольку оно совпадает с именем исходного компонента, а для поля имени пользователя мы упоминаем, что оно соответствует полю электронной почты в классе User.

Затем нам нужно только передать исходный и целевой bean-компоненты в наш JMapper — дальнейшая настройка не требуется.

В целом, этот метод удобен тем, что использует наименьшее количество кода.

6. Использование XML-конфигурации

Мы также можем использовать XML-конфигурацию для определения нашего сопоставления.

Вот наш пример XML-конфигурации в user_jmapper.xml:

<jmapper>
  <class name="com.baeldung.jmapper.UserDto">
    <attribute name="id">
      <value name="id"/>
    </attribute>
    <attribute name="username">
      <value name="email"/>
    </attribute>
  </class>
</jmapper>

И нам нужно передать нашу XML-конфигурацию в JMapper:

@Test
public void givenUser_whenUseXml_thenConverted(){
    JMapper<UserDto, User> userMapper = new JMapper<>
      (UserDto.class, User.class,"user_jmapper.xml");
    User user = new User(1L,"[email protected]", LocalDate.of(1980,8,20));
    UserDto result = userMapper.getDestination(user);

    assertEquals(user.getId(), result.getId());
    assertEquals(user.getEmail(), result.getUsername());            
}

Мы также можем вместо этого передать XML-конфигурацию в виде строки непосредственно в JMapper. имени файла.

7. Глобальное сопоставление

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


Например, если у нас есть UserDto1 с двумя полями, идентификатором и адресом электронной почты:

public class UserDto1 {  
    private long id;
    private String email;
    
    // standard constructor, getters, setters
}

Глобальное сопоставление будет проще использовать, поскольку они сопоставлены с полями с тем же именем в пользовательском исходном компоненте.

7.1. Использование API

Для конфигурации JMapperAPI мы будем использовать global():

@Test
public void givenUser_whenUseApiGlobal_thenConverted() {
    JMapperAPI jmapperApi = new JMapperAPI()
      .add(mappedClass(UserDto.class).add(global())) ;
    JMapper<UserDto1, User> userMapper1 = new JMapper<>
      (UserDto1.class, User.class,jmapperApi);
    User user = new User(1L,"[email protected]", LocalDate.of(1980,8,20));
    UserDto1 result = userMapper1.getDestination(user);

    assertEquals(user.getId(), result.getId());
    assertEquals(user.getEmail(), result.getEmail());
}

7.2. Использование аннотаций

Для настройки аннотаций мы будем использовать @JGlobalMap на уровне класса:

@JGlobalMap
public class UserDto1 {  
    private long id;
    private String email;
}

А вот простой тест:

@Test
public void whenUseGlobalMapAnnotation_thenConverted(){
    JMapper<UserDto1, User> userMapper= new JMapper<>(
      UserDto1.class, User.class);
    User user = new User(
      1L,"[email protected]", LocalDate.of(1980,8,20));
    UserDto1 result = userMapper.getDestination(user);

    assertEquals(user.getId(), result.getId());
    assertEquals(user.getEmail(), result.getEmail());        
}

7.3. Конфигурация XML

А для конфигурации XML у нас есть элемент \u003cglobal/\u003e:

<jmapper>
  <class name="com.baeldung.jmapper.UserDto1">
    <global/>
  </class>
</jmapper>

И затем передать имя файла XML:

@Test
public void givenUser_whenUseXmlGlobal_thenConverted(){
    JMapper<UserDto1, User> userMapper = new JMapper<>
      (UserDto1.class, User.class,"user_jmapper1.xml");
    User user = new User(1L,"[email protected]", LocalDate.of(1980,8,20));
    UserDto1 result = userMapper.getDestination(user);

    assertEquals(user.getId(), result.getId());
    assertEquals(user.getEmail(), result.getEmail());            
}

8. Пользовательские преобразования

Теперь давайте узнайте, как применить пользовательское преобразование с помощью JMapper.


У нас есть новое поле age в UserDto, которое нам нужно вычислить из атрибута User BirthdayDate:

public class UserDto {
    @JMap
    private long id;

    @JMap("email")
    private String username;
    
    @JMap("birthDate")
    private int age;

    @JMapConversion(from={"birthDate"}, to={"age"})
    public int conversion(LocalDate birthDate){
        return Period.between(birthDate, LocalDate.now())
          .getYears();
    }
}

Итак, мы использовали @JMapConversion для применения сложного преобразования из UserDatebirthDate в атрибут age UserDto . Поэтому поле age будет вычисляться, когда мы сопоставляем User с UserDto:

@Test
public void whenUseAnnotationExplicitConversion_thenConverted(){
    JMapper<UserDto, User> userMapper = new JMapper<>(
      UserDto.class, User.class);
    User user = new User(
      1L,"[email protected]", LocalDate.of(1980,8,20));
    UserDto result = userMapper.getDestination(user);

    assertEquals(user.getId(), result.getId());
    assertEquals(user.getEmail(), result.getUsername());     
    assertTrue(result.getAge() > 0);
}

9. Реляционное отображение

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

Если мы уже знаем целевые классы, мы можем определить их для каждого отображаемого поля и использовать RelationalJMapper.

В этом примере у нас есть один исходный компонент User:

public class User {
    private long id;    
    private String email;
}

И два целевых компонента UserDto1:

public class UserDto1 {  
    private long id;
    private String username;
}

И UserDto2:

public class UserDto2 {
    private long id;
    private String email;
}

Давайте посмотрим, как воспользоваться преимуществом нашего RelationalJMapper. .


9.1. Использование API

«Для нашей конфигурации API мы можем определить целевые классы для каждого атрибута, используя targetClasses():

@Test
public void givenUser_whenUseApi_thenConverted(){
    JMapperAPI jmapperApi = new JMapperAPI()
      .add(mappedClass(User.class)
      .add(attribute("id")
        .value("id")
        .targetClasses(UserDto1.class,UserDto2.class))
      .add(attribute("email")
        .targetAttributes("username","email")
        .targetClasses(UserDto1.class,UserDto2.class)));
    
    RelationalJMapper<User> relationalMapper = new RelationalJMapper<>
      (User.class,jmapperApi);
    User user = new User(1L,"[email protected]");
    UserDto1 result1 = relationalMapper
      .oneToMany(UserDto1.class, user);
    UserDto2 result2 = relationalMapper
      .oneToMany(UserDto2.class, user);

    assertEquals(user.getId(), result1.getId());
    assertEquals(user.getEmail(), result1.getUsername());
    assertEquals(user.getId(), result2.getId());
    assertEquals(user.getEmail(), result2.getEmail());            
}

Обратите внимание, что для каждого целевого класса нам нужно определить имя целевого атрибута.

RelationalJMapper принимает только один класс — отображаемый класс.

9.2. Использование аннотаций

Для подхода с аннотациями мы также определим классы:

public class User {
    @JMap(classes = {UserDto1.class, UserDto2.class})
    private long id;    
    
    @JMap(
      attributes = {"username", "email"}, 
      classes = {UserDto1.class, UserDto2.class})
    private String email;
}

Как обычно, при использовании аннотаций дальнейшая настройка не требуется:

@Test
public void givenUser_whenUseAnnotation_thenConverted(){
    RelationalJMapper<User> relationalMapper
      = new RelationalJMapper<>(User.class);
    User user = new User(1L,"[email protected]");
    UserDto1 result1 = relationalMapper
      .oneToMany(UserDto1.class, user);
    UserDto2 result2= relationalMapper
      .oneToMany(UserDto2.class, user);

    assertEquals(user.getId(), result1.getId());
    assertEquals(user.getEmail(), result1.getUsername());  
    assertEquals(user.getId(), result2.getId());
    assertEquals(user.getEmail(), result2.getEmail());          
}

9.3. Конфигурация XML

Для конфигурации XML мы используем \u003cclasses\u003e для определения целевых классов для каждого атрибута.

Вот наш user_jmapper2.xml:

<jmapper>
  <class name="com.baeldung.jmapper.relational.User">
    <attribute name="id">
      <value name="id"/>
      <classes>
        <class name="com.baeldung.jmapper.relational.UserDto1"/>
        <class name="com.baeldung.jmapper.relational.UserDto2"/>
      </classes>
    </attribute>
    <attribute name="email">
      <attributes>
        <attribute name="username"/>
        <attribute name="email"/>
      </attributes>
      <classes>
        <class name="com.baeldung.jmapper.relational.UserDto1"/>
        <class name="com.baeldung.jmapper.relational.UserDto2"/>
      </classes>      
    </attribute>
  </class>
</jmapper>

А затем передайте файл конфигурации XML в RelationalJMapper:


@Test
public void givenUser_whenUseXml_thenConverted(){
    RelationalJMapper<User> relationalMapper
     = new RelationalJMapper<>(User.class,"user_jmapper2.xml");
    User user = new User(1L,"[email protected]");
    UserDto1 result1 = relationalMapper
      .oneToMany(UserDto1.class, user);
    UserDto2 result2 = relationalMapper
      .oneToMany(UserDto2.class, user);

    assertEquals(user.getId(), result1.getId());
    assertEquals(user.getEmail(), result1.getUsername());
    assertEquals(user.getId(), result2.getId());
    assertEquals(user.getEmail(), result2.getEmail());         
}

10. Заключение

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

Полный исходный код примеров можно найти на GitHub.