1. Overview
In this tutorial, we’ll explore JMapper – a fast and easy to use mapping framework.
We’ll discuss different ways to configure JMapper, how to perform custom conversions, as well as relational mapping.
2. Maven Configuration
First, we need to add the JMapper dependency to our pom.xml:
<dependency> <groupId>com.googlecode.jmapper-framework</groupId> <artifactId>jmapper-core</artifactId> <version>1.6.0.1</version> </dependency>
3. Source and Destination Models
Before we get to the configuration, let’s take a look at the simple beans we’re going to use throughout this tutorial.
First, here’s our source bean – a basic User:
public class User { private long id; private String email; private LocalDate birthDate; }
And our destination bean, UserDto:
public class UserDto { private long id; private String username; }
We’ll use the library to map attributes from our source bean User to our destination bean UserDto.
There are three ways to configure JMapper: by using the API, annotations and XML configuration.
In the following sections, we’ll go over each of these.
4. Using the API
Let’s see how to configure JMapper using the API.
Here, we don’t need to add any configuration to our source and destination classes. Instead, all the configuration can be done using JMapperAPI, which makes it the most flexible configuration method:
@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()); }
Here, we use the mappedClass() method to define our mapped class UserDto. Then, we used the attribute() method to define each attribute and its mapped value.
Next, we created a JMapper object based on the configuration and used its getDestination() method to obtain the UserDto result.
5. Using Annotations
Let’s see how we can use the @JMap annotation to configure our mapping:
public class UserDto { @JMap private long id; @JMap("email") private String username; }
And here’s how we’ll use our 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()); }
Note that for the id attribute, we didn’t need to provide a target field name as it’s the same name as the source bean, while for the username field we mention that it corresponds to the email field in the User class.
Then, we only need to pass source and destination beans to our JMapper – no further configuration needed.
Overall, this method is convenient as it uses the least amount of code.
6. Using XML Configuration
We can also use XML configuration to define our mapping.
Here’s our sample XML configuration at 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>
And we need to pass our XML configuration to 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()); }
We can also pass the XML configuration as a String directly to JMapper instead of a file name.
7. Global Mapping
We can take advantage of global mapping if we have multiple fields with the same name in both the source and destination beans.
For example, if we have a UserDto1 which has two fields, id and email:
public class UserDto1 { private long id; private String email; // standard constructor, getters, setters }
Global mapping will be easier to use as they are mapped to fields with the same name at User source bean.
7.1. Using the API
For JMapperAPI configuration, we’ll use 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. Using Annotations
For the annotation configuration, we’ll use @JGlobalMap at class level:
@JGlobalMap public class UserDto1 { private long id; private String email; }
And here’s a simple test:
@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 Configuration
And for the XML configuration, we have the <global/> element:
<jmapper> <class name="com.baeldung.jmapper.UserDto1"> <global/> </class> </jmapper>
And then pass the XML file name:
@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. Custom Conversions
Now, let’s see how to apply a custom conversion using JMapper.
We have a new field age in our UserDto, which we need to calculate from the User birthDate attribute:
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(); } }
So, we used @JMapConversion to apply a complex conversion from the User’s birthDate to the UserDto’s age attribute. Therefore the age field will be calculated when we map User to 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. Relational Mapping
Finally, we’ll discuss relational mapping. With this method, we need to define our JMapper using a target class each time.
If we already know the target classes, we can define them for each mapped field and use RelationalJMapper.
In this example, we have one source bean User:
public class User { private long id; private String email; }
And two destination beans UserDto1:
public class UserDto1 { private long id; private String username; }
And UserDto2:
public class UserDto2 { private long id; private String email; }
Let’s see how to take advantage of our RelationalJMapper.
9.1. Using the API
For our API configuration, we can define target classes for each attribute using 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()); }
Note that for each target class, we need to define the target attribute name.
The RelationalJMapper only takes one class – the mapped class.
9.2. Using Annotations
For the annotation approach, we’ll define the classes as well:
public class User { @JMap(classes = {UserDto1.class, UserDto2.class}) private long id; @JMap( attributes = {"username", "email"}, classes = {UserDto1.class, UserDto2.class}) private String email; }
As usual, no further configuration needed when we use annotations:
@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 Configuration
For the XML configuration, we use <classes> to define the target classes for each attribute.
Here’s our 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>
And then pass the XML configuration file to 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. Conclusion
In this tutorial, we learned different ways to configure JMapper and how to perform a custom conversion.
The full source code for the examples can be found over on GitHub.