51

In my application's architecture I usually send the object or list of objects from the data access layer to the web layer via the service layer, in which these objects get transformed from a DAO object to a DTO object and vice versa. The web layer don't have any access to DAO objects and the DAO layer do not use DTOs.

To demonstrate, I usually write the code as:

@Transactional(readOnly = true)
public List<UserDTO> getAllUserAsUserDTO() {
    List<UserDTO> userDTOs = new ArrayList<UserDTO>();

    for(User user : getAllUser()) {
        userDTOs.add(constructUserDTO(user));
    }

    return userDTOs;
}

private UserDTO constructUserDTO(User user) {
    UserDTO userDTO = new UserDTO();
    userDTO.setFullName(user.getFullName());
    userDTO.setId(user.getId());
    userDTO.setUsername(user.getUsername());
    userDTO.setRole(user.getRole());
    userDTO.setActive(user.isActive());
    userDTO.setActiveText(user.isActive() ? "Active" : "Inactive");
    return userDTO;
}

Here the user is the database entity:

@javax.persistence.Entity
@Table(name = "USER")
public class User extends Entity {

    @Transient
    private static final long serialVersionUID = -112950002831333869L;

    private String username;
    private String fullName;
    private boolean active;
    private String role;
    // other fields

    public User() {
        super();
    }

    @NaturalId
    @Column(name = "USERNAME", nullable = false)
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Column(name = "FULL_NAME")
    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    @Column(name = "ACTIVE", nullable = false)
    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }

    @Column(name = "ROLE")
    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }
}

And this is the UserDTO:

public class UserDTO extends BaseDTO {

    private static final long serialVersionUID = -3719463614753533782L;

    private String username;
    private String fullName;
    private String role;
    private String activeText;
    private Boolean active;
    //other properties

    public UserDTO() {
        super();
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public String getActiveText() {
        return activeText;
    }

    public void setActiveText(String activeText) {
        this.activeText = activeText;
    }

    public Boolean getActive() {
        return active;
    }

    public void setActive(Boolean active) {
        this.active = active;
    }
}

So I was wondering if this is the only way to copy properties between two objects. I guess I am not sure. Also I am using lambdaj, so is there a method in this API by which I can copy all these properties to create list of other objects?

This topic may be sounds subjective, but I really want to know from you experts the ways by which the transformation of object from one form to another can be done where the maximum fields are having same string.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tapas Bose
  • 28,796
  • 74
  • 215
  • 331
  • Possible duplicate of [any tool for java object to object mapping?](https://stackoverflow.com/questions/1432764/any-tool-for-java-object-to-object-mapping) – tkruse Dec 12 '17 at 05:24

7 Answers7

28

You can use Apache Commmons Beanutils. The API is

org.apache.commons.beanutils.PropertyUtilsBean.copyProperties(Object dest, Object orig).

It copies property values from the "origin" bean to the "destination" bean for all cases where the property names are the same.

Now I am going to off topic. Using DTO is mostly considered an anti-pattern in EJB3. If your DTO and your domain objects are very alike, there is really no need to duplicate codes. DTO still has merits, especially for saving network bandwidth when remote access is involved. I do not have details about your application architecture, but if the layers you talked about are logical layers and does not cross network, I do not see the need for DTO.

Lan
  • 6,470
  • 3
  • 26
  • 37
  • 16
    True, this is an anti-pattern in EJB land. But in the new smart client world (i.e. client side MVC) it is quickly becoming a necessity. You do not want to pull your entire object graph to the client side but only the stuff you really need there. Hence a DTO. – Kees de Kooter May 28 '13 at 19:37
  • This is a good suggestion. But what if the DTO has only 4 properties and the actual object has 50 properties. I my case, when I use the copyProperties as mentioned here, it overwrites by actual object with only properties. The rest of the 46 properties become null. Is this how it is expected to behave ? – HopeKing May 15 '17 at 12:44
  • 1
    is this deep copy? – Kalpesh Soni Jan 16 '19 at 21:41
22

You can have a look at dozer which is a

Java Bean to Java Bean mapper that recursively copies data from one object to another. Typically, these Java Beans will be of different complex types.

Another better link...

pgras
  • 12,614
  • 4
  • 38
  • 46
  • From their [GitHub](https://github.com/DozerMapper/dozer/): The project is currently not active and will more than likely be deprecated in the future. If you are looking to use Dozer on a greenfield project, we would discourage that. – tgray Nov 30 '21 at 00:35
7

I had an application that I needed to convert from a JPA entity to DTO, and I thought about it and finally came up using org.springframework.beans.BeanUtils.copyProperties for copying simple properties and also extending and using org.springframework.binding.convert.service.DefaultConversionService for converting complex properties.

In detail my service was something like this:

@Service("seedingConverterService")
public class SeedingConverterService extends DefaultConversionService implements ISeedingConverterService  {
    @PostConstruct
    public void init(){
        Converter<Feature,FeatureDTO> featureConverter = new Converter<Feature, FeatureDTO>() {

            @Override
            public FeatureDTO convert(Feature f) {
                FeatureDTO dto = new FeatureDTO();
                //BeanUtils.copyProperties(f, dto,"configurationModel");
                BeanUtils.copyProperties(f, dto);
                dto.setConfigurationModelId(f.getConfigurationModel()==null?null:f.getConfigurationModel().getId());
                return dto;
            }
        };

        Converter<ConfigurationModel,ConfigurationModelDTO> configurationModelConverter = new Converter<ConfigurationModel,ConfigurationModelDTO>() {
            @Override
            public ConfigurationModelDTO convert(ConfigurationModel c) {
                ConfigurationModelDTO dto = new ConfigurationModelDTO();
                //BeanUtils.copyProperties(c, dto, "features");
                BeanUtils.copyProperties(c, dto);
                dto.setAlgorithmId(c.getAlgorithm()==null?null:c.getAlgorithm().getId());
                List<FeatureDTO> l = c.getFeatures().stream().map(f->featureConverter.convert(f)).collect(Collectors.toList());
                dto.setFeatures(l);
                return dto;
            }
        };
        addConverter(featureConverter);
        addConverter(configurationModelConverter);
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Reza
  • 1,516
  • 14
  • 23
4

I suggest you should use one of the mappers' libraries: Mapstruct, ModelMapper, etc. With Mapstruct your mapper will look like:

@Mapper
public interface UserMapper {     
    UserMapper INSTANCE = Mappers.getMapper( UserMapper.class ); 

    UserDTO toDto(User user);
}

The real object with all getters and setters will be automatically generated from this interface. You can use it like:

UserDTO userDTO = UserMapper.INSTANCE.toDto(user);

You can also add some logic for your activeText filed using @AfterMapping annotation.

2

You can use reflection to find all the get methods in your DAO objects and call the equivalent set method in the DTO. This will only work if all such methods exist. It should be easy to find example code for this.

Mohammad Faisal
  • 5,783
  • 15
  • 70
  • 117
Skip Head
  • 7,580
  • 1
  • 30
  • 34
2

Wouldn't lambdaj's project function do what you are looking for?

It'll look something like this:

List<UserDTO> userNDtos = project(users, UserDTO.class, on(User.class).getUserName(), on(User.class).getFullName(), .....);

(Define the constructor for UserDTO accordingly...)

Also see here for examples...

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ashkan Aryan
  • 3,504
  • 4
  • 30
  • 44
0

The simplest trick is SERIALIZE the Object_A and DESERIALIZE it using Object_B.class:

if you are using Spring or Spring-boot probably you already have Jackson library in your classpath:

import com.fasterxml.jackson.databind.ObjectMapper;

then:

ObjectMapper objectMapper = new ObjectMapper();
Object_B objB = objectMapper.readValue(objectMapper.writeValueAsString(objA), Object_B.class);
Mike D3ViD Tyson
  • 1,701
  • 1
  • 21
  • 31