43

I am using Spring MVC architecture with JPA in my web application. Where to convert data transfer object (DTO) to JPA entity and vice-versa, manually (that is, without using any framework)?

Adrian M.
  • 360
  • 7
  • 17
xyz
  • 2,160
  • 3
  • 20
  • 31

6 Answers6

44

This is an old question with accepted answer but thought to update it with easy way of doing it using model-mapper API.

<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>0.7.4</version>
</dependency>

Using this API, you avoid manual setter & getters as explained in accepted answer.

In my opinion, both conversions should happen at controller with the help of private utility methods and using Java8 stream's map ( if a Collection of DTOs is exchanged ) like illustrated in this article.

It should happen at controller because DTOs are meant to be exclusive transfer objects. I don't take my DTOs further way down.

You code your service & data access layers on entities and convert DTOs to entities before calling service methods & convert entities to DTOs before returning response from controller.

I prefer this approach because entities rarely change and data can be added / removed from DTOs as desired.

Detailed model mapper configuration and rules are described here

Sabir Khan
  • 9,826
  • 7
  • 45
  • 98
  • In my opinion, the controller is just controlling the input and output data. The conversion process must happen in a service class. Another thing, this modelmapper, does it use reflection to perform the conversion? – Thiago Cunha Nov 13 '19 at 02:01
  • 2
    @ThiagoCunha: I haven't looked at modelmapper source code but reflection seems only way to me. For the other part, since controller is responsible for controlling input / output data ( as per you ) so logically conversion should happen there too.Actually conversion code can be put in some utility class but conversion call should naturally happen from controller , though there are no hard & fast rules.These are just opinions. – Sabir Khan Nov 13 '19 at 06:51
  • So I prefer to create a design pattern while still in archaic mode (Laughter). This is because reflection does not go through optimization processes at compile time. I have used Converter Design Pattern through a generic code generation model. I apply a nice technique, and it has been productive despite the manual work. – Thiago Cunha Nov 13 '19 at 13:08
  • 2
    @ThiagoCunha: Hi , I would suggest to first go through model mapper code to be sure that its really using reflection because performance is really good. There are few more APIs in the market foe the same & might save you from manual work. – Sabir Khan Nov 14 '19 at 05:35
  • @ThiagoCunha Could you show code of how you do that with converter pattern? – parsecer May 31 '23 at 16:51
26

I suggest another approach without extra dependency:

import org.springframework.beans.BeanUtils
...
BeanUtils.copyProperties(sourceObject, targetObject);

Can be used to convert DTO to entity, or vice-versa, if they have same property types and names.

If you want to ignore some fields, just add them after the targetObject.

BeanUtils.copyProperties(sourceObj, targetObj, "propertyToIgnoreA", "propertyToIgnoreB", "propertyToIgnoreC");

Source: http://appsdeveloperblog.com/dto-to-entity-and-entity-to-dto-conversion/

I think this is the cleanest way. Remember to check the Javadoc for caveats!

WesternGun
  • 11,303
  • 6
  • 88
  • 157
  • I loved this answer. In the new world order, this looks like the perfect way without writing any boilerplate unless field names are different. – comiventor Mar 26 '19 at 11:58
  • Thank you for providing a sensible answer. – Adrian M. Jan 28 '20 at 12:58
  • 5
    While `BeanUtils.copyProperties` is a working solution it is still based on Reflection API and hence every conversion gives decent overhead in comparison with tools like MapStruct and Orika that are based on build-time code generation. MapStruct is pretty simple, I recommend to use it instead of BeanUtils in the cases where performance is important – Nikolai Shevchenko Sep 03 '20 at 09:08
  • Yes, that's the "caveat" that I mentioned... and there are others so the Javadoc is a must-read. – WesternGun Sep 03 '20 at 09:54
  • for performance issue I do not use it, it will cost much in the first time – fudy Oct 12 '20 at 08:42
  • What if a DTO object has a nested DTO object? Eg PersonDTO has List and it should all be converted to `PersonJpa` with `List` – parsecer May 31 '23 at 16:45
  • It would throw exception if some properties are null – parsecer May 31 '23 at 16:48
  • Yes, it will. Please read the javadoc and also the source code. Using reflection, it will try to call getter and setter on the fields. – WesternGun Jun 01 '23 at 09:16
17

I can recommend to use mapstruct library:

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-jdk8</artifactId>
    <version>1.2.0.Final</version>
</dependency>

For example, if you have such an entity:

public class Entity {
    private Integer status;
    private String someString;
    private Date startDate;
    private Date endDate;

    // SKIPPED

And DTO:

public class Dto {
    private Boolean status;
    private String someString;
    private Long startDate;
    private Long endDate;

    // SKIPPED

Then the transformation can be done in the service layer by this way:

@Service
public class SomeServiceImpl implements SomeService {

    @Autowired
    SomeDao someDao;

    @Autowired
    SomeMapper someMapper;


    public Dto getSomething(SomeRequest request) throws SomeException {
        return someDao.getSomething(request.getSomeData())
                .map(SomeMapper::mapEntityToDto)
                .orElseThrow(() -> new SomeException("..."));
    }

Mapper can be represented as follows:

@Mapper 
public interface SomeMapper {
    @Mappings(
            {@Mapping(target = "entity", 
                      expression = "java(entity.getStatus() == 1 ? Boolean.TRUE : Boolean.FALSE)"),
             @Mapping(target = "endDate", source = "endDate"),
             @Mapping(target = "startDate", source = "startDate")
            })

    Dto mapEntityToDto(Entity entity);
}
  • 2
    In addition to using MapStruct I'd also recommend using this extension https://github.com/Pozo/mapstruct-kotlin this allows you to keep your kotlin data class immutable by not requiring you to explicitly declare a no-arg ctor anymore i.e. constructor() : this(null, null) – FearlessHyena Jun 05 '19 at 06:45
  • I prefer `MapStruct` than `ModelMapper` which has 5k github stars(on 2022). – wonsuc Mar 18 '22 at 04:11
16

I think you are asking about where to write whole entity-->DTO conversion logic.

Like Your entity

class StudentEntity {
 int age ;
 String name;

 //getter
 //setter

 public StudentDTO _toConvertStudentDTO(){
    StudentDTO dto = new StudentDTO();
    //set dto values here from StudentEntity
    return dto;
 }

}

Your DTO Should be like

class StudentDTO  {
 int age ;
 String name;

 //getter
 //setter

 public StudentEntity _toConvertStudentEntity(){
    StudentEntity entity = new StudentEntity();
    //set entity values here from StudentDTO
    return entity ;
 }

}

And Your Controller should be like

@Controller
class MyController {

    public String my(){

    //Call the conversion method here like
    StudentEntity entity = myDao.getStudent(1);
    StudentDTO dto = entity._toConvertStudentDTO();

    //As vice versa

    }

}
NIrav Modi
  • 6,038
  • 8
  • 32
  • 47
  • A question doesn't have any modularity stuff. It's a simple question of how to do that conversion. – NIrav Modi Nov 29 '17 at 07:49
  • 26
    If one cares about modularity, this approach would force DTOs to depend on entities and vice versa. Moreover, in a multi module project, this will result in a cyclic dependency if your entities & DTOs are in separate module (usually the case). Checkout [this answer](https://stackoverflow.com/a/44561593/3381334) – Parth Nov 29 '17 at 14:44
  • 11
    DTO's are simple objects that should not contain any business logic, i.e. they should not be aware of entities (value objects) or how to convert themselves to an entity (value object). – Nathan McKenzie Mar 15 '19 at 16:12
  • 5
    Your answer violates The OOP principles. – Arash Nov 09 '19 at 17:28
  • 2
    When it comes to clean code, I believe the conversion process will be within a service, not at the application control layer. Another point to note is, still related to DDD, is that there is a business rule touching your entity, and the entity has only one principle, and it is not added to the business value. Martin (2013) also points out in his clean code book on DTO. Take a read on how to use it. At first, I was hurting, putting conversion into DTO, when in fact it needs to be in a specific conversion class for this business. – Thiago Cunha Nov 13 '19 at 01:58
  • 6
    1. DTO shouldnt have any logic. 2. DTO shouldnt know about entities and vice versa. 3. You breaking SRP – Eldar Agalarov Nov 22 '19 at 18:56
  • 1
    I agree with you @EldarAgalarov – NIrav Modi Nov 25 '19 at 09:13
  • ....and it's too verbose, to boot. The question wasn't: how can inefficiently use accessors and mutators to map from an entity to a dto, and vice-versa? – Adrian M. Jan 28 '20 at 13:05
7

In my opinion

  • the Entity -> DTO conversion should be done in the Controller before dispatching the jsp page
  • the DTO -> Entity conversion should be done in the Controller as well after validating the DTO returned from the jsp page

Its gives you more control over the process and you do not have to change the service/persistence classes every time some logic populating the Entity is changed.

segaurav
  • 217
  • 2
  • 5
  • Agreed. Also, if you combine the conversion logic in both the Entity and DTO, the two could not be separated into separate JAR artifacts. For example, the DTO class could not be given to a third-party without also including the Entity, which (in most cases) you would NOT want to do. – Jack Straw Jun 13 '17 at 23:47
  • 1
    What about if you have a lazy loading property in your entity? If you do the conversion in the controller the property will be loaded and this can be undesired. Should we have an extra layer for domain objects? (this can be expensive) – Henry Aug 02 '18 at 01:40
0

Used mapstruct library. Additionally added the following in build.gradle

sourceSets {
    main.java.srcDirs += "build/generated/sources/annotationProcessor/java/main/"
}
bhagyashree1990
  • 41
  • 1
  • 1
  • 5