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 .