Перейти к основному содержимому

Руководство по JMapper

· 7 мин. чтения

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,"john@test.com", 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,"john@test.com", 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.foreach.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,"john@test.com", LocalDate.of(1980,8,20));
UserDto result = userMapper.getDestination(user);

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

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

7. Глобальное картографирование

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

Например, если у нас есть UserDto1 с двумя полями, id и email :

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,"john@test.com", 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,"john@test.com", LocalDate.of(1980,8,20));
UserDto1 result = userMapper.getDestination(user);

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

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

А для конфигурации XML у нас есть элемент <global/> :

<jmapper>
<class name="com.foreach.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,"john@test.com", LocalDate.of(1980,8,20));
UserDto1 result = userMapper.getDestination(user);

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

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

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

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

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 для применения сложного преобразования из даты рождения пользователя в атрибут возраста UserDto . Поэтому поле age будет вычисляться, когда мы сопоставляем User с UserDto :

@Test
public void whenUseAnnotationExplicitConversion_thenConverted(){
JMapper<UserDto, User> userMapper = new JMapper<>(
UserDto.class, User.class);
User user = new User(
1L,"john@test.com", 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,"john@test.com");
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,"john@test.com");
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 мы используем <classes> для определения целевых классов для каждого атрибута.

Вот наш user_jmapper2.xml :

<jmapper>
<class name="com.foreach.jmapper.relational.User">
<attribute name="id">
<value name="id"/>
<classes>
<class name="com.foreach.jmapper.relational.UserDto1"/>
<class name="com.foreach.jmapper.relational.UserDto2"/>
</classes>
</attribute>
<attribute name="email">
<attributes>
<attribute name="username"/>
<attribute name="email"/>
</attributes>
<classes>
<class name="com.foreach.jmapper.relational.UserDto1"/>
<class name="com.foreach.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,"john@test.com");
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 .