Шаблон 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 метод, с помощью которого я могу скопировать все эти свойства для создания списка других объектов?

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


person Tapas Bose    schedule 27.02.2013    source источник
comment
Возможный дубликат любого инструмента для сопоставления объектов java с объектами?   -  person tkruse    schedule 12.12.2017


Ответы (6)


Вы можете взглянуть на дозатор, который является

Java Bean to Java Bean mapper, который рекурсивно копирует данные из одного объекта в другой. Обычно эти Java Bean-компоненты относятся к разным сложным типам.

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

person pgras    schedule 27.02.2013

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

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

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

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

person Lan    schedule 02.03.2013
comment
Правда, это антипаттерн в мире EJB. Но в новом мире интеллектуальных клиентов (т.е. MVC на стороне клиента) это быстро становится необходимостью. Вы не хотите перетягивать весь граф объектов на клиентскую сторону, а только то, что вам действительно нужно. Следовательно, DTO. - person Kees de Kooter; 28.05.2013
comment
Это хорошее предложение. Но что, если DTO имеет только 4 свойства, а фактический объект имеет 50 свойств. В моем случае, когда я использую copyProperties, как упоминалось здесь, он перезаписывается реальным объектом только с помощью свойств. Остальные 46 свойств становятся нулевыми. Ожидается ли, что так оно и будет вести себя? - person HopeKing; 15.05.2017
comment
это глубокая копия? - person Kalpesh Soni; 17.01.2019

У меня было приложение, которое мне нужно было преобразовать из объекта 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);
    }
}
person Reza    schedule 25.02.2016

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

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

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

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

Примеры см. Также здесь ...

person Ashkan Aryan    schedule 12.03.2013

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

person Skip Head    schedule 27.02.2013

Я предлагаю вам использовать одну из библиотек картографов: Mapstruct, ModelMapper и т. д. С Mapstruct ваш картограф будет выглядеть так:

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

    UserDTO toDto(User user);
}

Настоящий объект со всеми геттерами и сеттерами будет автоматически сгенерирован из этого интерфейса. Вы можете использовать это как:

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

Вы также можете добавить логику для вашего файла activeText, используя @AfterMapping аннотации.

person Anton Orlov    schedule 20.06.2018