web-dev-qa-db-fra.com

Modèle DTO: meilleur moyen de copier des propriétés entre deux objets

Dans l'architecture de mon application, j'envoie généralement l'objet ou la liste d'objets de la couche d'accès aux données à la couche Web via la couche de service, dans laquelle ces objets sont transformés à partir de DAO objet à un DTO objet et vice versa. La couche Web n'a aucun accès aux objets DAO et la couche DAO n'utilise pas de DTO.

Pour démontrer, j'écris habituellement le code comme:

@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;
}

Ici, l'utilisateur est l'entité de base de données:

@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;
    }
}

Et voici le 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;
    }
}

Je me demandais donc si c'était le seul moyen de copier des propriétés entre deux objets. Je suppose que je ne suis pas sûr. De plus, j'utilise lambdaj , existe-t-il une méthode dans cette API grâce à laquelle je peux copier toutes ces propriétés pour créer une liste d'autres objets?

Ce sujet peut sembler subjectif, mais je tiens vraiment à vous informer de la manière dont vous pouvez transformer un objet d’un formulaire à un autre lorsque les champs maximaux ont la même chaîne.

44
Tapas Bose

Vous pouvez jeter un oeil à dozer qui est un

Java Bean to Java Bean mapper qui copie de manière récursive les données d'un objet à un autre. Généralement, ces Java Beans seront de types complexes différents.

n autre meilleur lien ...

22
pgras

Vous pouvez utiliser Apache Commmons Beanutils . L'API est

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

Il copie les valeurs de propriété du bean "Origin" vers le bean "destination" pour tous les cas où les noms de propriété sont identiques.

Maintenant je vais hors sujet. L'utilisation de DTO est principalement considérée comme un anti-motif dans EJB3. Si votre DTO et vos objets de domaine sont très semblables, il n’est vraiment pas nécessaire de dupliquer les codes. DTO a toujours des avantages, en particulier pour économiser la bande passante du réseau lorsque l’accès à distance est impliqué. Je n'ai pas de détails sur votre architecture d'application, mais si les couches dont vous avez parlé sont des couches logiques et ne traversent pas le réseau, je ne vois pas la nécessité du DTO.

27
Lan

J'avais une application que je devais convertir d’une entité JPA à DTO, et j’y ai réfléchi avant de finalement utiliser org.springframework.beans.BeanUtils.copyProperties pour copier des propriétés simples et pour étendre et utiliser org.springframework.binding.convert.service.DefaultConversionService pour la conversion de propriétés complexes.

En détail, mon service ressemblait à ceci:

@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);
    }
}
6
Reza

Est-ce que lambdaj fonction du projet ne ferait pas ce que vous cherchez?

Ça va ressembler à quelque chose comme ça:

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

(Définissez le constructeur pour UserDTO en conséquence ...)

Voir aussi ici pour des exemples ...

2
Ashkan Aryan

Vous pouvez utiliser la réflexion pour rechercher toutes les méthodes get dans vos objets DAO et appeler la méthode équivalente set dans le groupe - DTO . Cela ne fonctionnera que si toutes ces méthodes existent. Il devrait être facile de trouver un exemple de code pour cela.

2
Skip Head

Je vous suggère d'utiliser l'une des bibliothèques des mappeurs: Mapstruct , ModelMapper , etc. Avec Mapstruct, votre mappeur ressemblera à ceci:

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

    UserDTO toDto(User user);
}

L'objet réel avec tous les getters et les setters sera automatiquement généré à partir de cette interface. Vous pouvez l'utiliser comme:

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

Vous pouvez également ajouter une logique à votre fichier activeText en utilisant @ AfterMapping annotation.

1
Anton Orlov