Образец DTO: лучший способ скопировать свойства между двумя объектами

В моей архитектуре приложения я обычно посылаю объект или список объектов с уровня доступа к данным на веб-уровень через сервисный уровень, в котором эти объекты преобразуются из DAO для объекта DTO и наоборот. Веб-уровень не имеет доступа к объектам DAO, а слой DAO не использует DTO.

Чтобы продемонстрировать, я обычно пишу код как:

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

Здесь пользователь является объектом базы данных:

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

И это 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;
    }
}

Итак, мне было интересно, является ли это единственным способом копирования свойств между двумя объектами. Наверное, я не уверен. Также я использую lambdaj, так есть метод в этом API, с помощью которого я могу скопировать все эти свойства, чтобы создать список других объектов

Этот раздел может звучать субъективно, но я действительно хочу узнать у вас экспертов, как можно преобразовать объект из одной формы в другую, где максимальные поля имеют одну и ту же строку.

Ответ 1

Вы можете посмотреть dozer, который является

Java Bean в Java Bean mapper, который рекурсивно копирует данные с одного объекта на другой. Как правило, эти Java Beans будут иметь разные сложные типы.

Еще одна лучшая ссылка...

Ответ 2

Вы можете использовать Apache Commmons Beanutils. API

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

Он копирует значения свойств из "origin" bean в "destination" bean для всех случаев, когда имена свойств одинаковы.

Теперь я собираюсь отключиться от темы. Использование DTO в основном рассматривается как анти-шаблон в EJB3. Если ваш DTO и ваши объекты домена очень похожи, нет необходимости дублировать коды. DTO по-прежнему имеет свои достоинства, особенно для экономии пропускной способности сети, когда задействован удаленный доступ. У меня нет деталей о вашей архитектуре приложения, но если слои, о которых вы говорили, являются логическими слоями и не пересекают сеть, я не вижу необходимости в DTO.

Ответ 3

У меня было приложение, которое мне нужно было преобразовать из JPA в DTO, и я подумал об этом и, наконец, придумал org.springframework.beans.BeanUtils.copyProperties для копирования простых свойств, а также расширения и использования org.springframework.binding.convert.service.DefaultConversionService для преобразования сложных свойств.

Подробно моя служба была примерно такой:

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

Ответ 4

Вы можете использовать отражение, чтобы найти все методы get в объектах DAO и вызвать эквивалентный метод set в DTO. Это будет работать, только если все такие методы существуют. Для этого должно быть легко найти пример кода.

Ответ 5

Не будет lambdaj функция проекта делает то, что вы ищете?

Он будет выглядеть примерно так:

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

(Определите конструктор для UserDTO соответственно...)

Также см. здесь для примеров...