【问题标题】:DTO pattern: Best way to copy properties between two objectsDTO 模式:在两个对象之间复制属性的最佳方式
【发布时间】:2013-02-13 13:46:03
【问题描述】:

在我的应用程序架构中,我通常将对象或对象列表从数据访问层通过服务层发送到 Web 层,其中这些对象从 DAO 对象转换为 DTO 对象,反之亦然反之亦然。 Web 层无权访问 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 中有没有一种方法可以复制所有这些属性来创建其他对象的列表?

这个话题可能听起来很主观,但我真的很想向各位专家了解在最大字段具有相同字符串的情况下,如何将对象从一种形式转换为另一种形式。

【问题讨论】:

标签: java object lambdaj


【解决方案1】:

您可以使用Apache Commmons Beanutils。 API 是

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

对于属性名称相同的所有情况,它会将属性值从“origin”bean 复制到“destination”bean。

现在我要跑题了。在 EJB3 中,使用 DTO 主要被认为是一种反模式。如果您的 DTO 和您的域对象非常相似,则确实不需要重复代码。 DTO 仍然有其优点,尤其是在涉及远程访问时可以节省网络带宽。我没有关于你的应用程序架构的详细信息,但是如果你谈到的层是逻辑层并且不跨网络,我认为不需要 DTO。

【讨论】:

  • 没错,这是 EJB 领域的反模式。但在新的智能客户端世界(即客户端 MVC)中,它正迅速成为必需品。您不想将整个对象图拉到客户端,而只想将您真正需要的东西拉到客户端。因此是 DTO。
  • 这是个好建议。但是如果 DTO 只有 4 个属性,而实际对象有 50 个属性会怎样。我的情况是,当我使用此处提到的 copyProperties 时,它会被仅具有属性的实际对象覆盖。其余 46 个属性变为空。这是预期的行为方式吗?
  • 这是深拷贝吗?
【解决方案2】:

你可以看看dozer这是一个

Java Bean 到 Java Bean 的映射器,递归地将数据从一个对象复制到另一个对象。通常,这些 Java Bean 将具有不同的复杂类型。

Another better link...

【讨论】:

  • 来自他们的GitHub:该项目目前不活跃,将来很可能会被弃用。如果您希望在新建项目中使用 Dozer,我们不鼓励这样做。
【解决方案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】:

    lambdaj 的project function 不会满足您的需求吗?

    它看起来像这样:

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

    (相应地为 UserDTO 定义构造函数...)

    有关示例,另请参阅 here...

    【讨论】:

      【解决方案5】:

      您可以使用反射在 DAO 对象中查找所有 get 方法,并在 DTO 中调用等效的 set 方法。这仅在所有此类方法都存在时才有效。这应该很容易找到示例代码。

      【讨论】:

        【解决方案6】:

        我建议您应该使用映射器的库之一:MapstructModelMapper 等。 使用 Mapstruct,您的映射器将如下所示:

        @Mapper
        public interface UserMapper {     
            UserMapper INSTANCE = Mappers.getMapper( UserMapper.class ); 
        
            UserDTO toDto(User user);
        }
        

        具有所有 getter 和 setter 的真实对象将从该接口自动生成。你可以像这样使用它:

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

        您还可以使用 @AfterMapping 注释为您的 activeText 文件添加一些逻辑。

        【讨论】:

          猜你喜欢
          • 2012-04-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-19
          • 2010-10-08
          相关资源
          最近更新 更多