«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.