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,"[email protected]", 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,"[email protected]", 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,"[email protected]", 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,"[email protected]", 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,"[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 Configuration

And for the XML configuration, we have the 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,"[email protected]", 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,"[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. 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,"[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());            
}

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,"[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 Configuration

For the XML configuration, we use 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,"[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. 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.