DTO 快速转换 VO 实现

7 篇文章 0 订阅

应用场景

在前后端开发的过程中,经常会遇到需要将从数据库查询出来的 DTO 对象转换为传递给前端的 VO 对象,这里以Account 对象转换为 AuthorizeVO 对象为例子:

@Data
@AllArgsConstructor
@TableName("db_account")
public class Account {

    @TableId(type = IdType.AUTO)
    Integer id;

    String username;

    String password;

    String email;

    String role;

    Date registerTime;
}
@Data
public class AuthorizeVO {
    
    String username;
    
    String role;
    
    String token;
    
    Date expire;
}

对象转换的过程如下:

// 从数据库中查询出对象
Account account = accountService.findAccountByNameOrEmail(user.getUsername());
AuthorizeVO vo = new AuthorizeVO();
vo.setExpire(jwtUtils.expireTime());
vo.setRole(account.getRole());
vo.setToken(token);
vo.setUsername(account.getUsername());

可以看到上面的转换过程需要用户新建一个 AuthorizeVO 对象,并且把原 Account 对象的属性一个一个设置到AuthorizeVO 对象中,非常的不优雅和简洁。

下面的介绍方法可以一次性把 DTO 对象里面的属性赋值到 VO 对象中,并且代码更加简洁,优雅。

源码

除了可以使用 BeanUtils.copyProperties(); 方法实现两个对象属性的快速赋值,我们还可以自己写一个接口方法,只需将 DTO 类继承此接口即可实现对象属性快速赋值,这里用到了 Java 中的反射机制,实现源码如下:

/**
 * 用于DTO快速转换VO实现,只需将DTO类继承此类即可使用
 */
public interface BaseData {
    /**
     * 创建指定的VO类并将当前DTO对象中的所有成员变量值直接复制到VO对象中
     * @param clazz 指定VO类型
     * @param consumer 返回VO对象之前可以使用Lambda进行额外处理
     * @return 指定VO对象
     * @param <V> 指定VO类型
     */
    default <V> V asViewObject(Class<V> clazz, Consumer<V> consumer) {
        V v = this.asViewObject(clazz);
        consumer.accept(v);
        return v;
    }

    /**
     * 创建指定的VO类并将当前DTO对象中的所有成员变量值直接复制到VO对象中
     * @param clazz 指定VO类型
     * @return 指定VO对象
     * @param <V> 指定VO类型
     */
    default <V> V asViewObject(Class<V> clazz) {
        try {
            Field[] fields = clazz.getDeclaredFields();
            Constructor<V> constructor = clazz.getConstructor();
            V v = constructor.newInstance();
            Arrays.asList(fields).forEach(field -> convert(field, v));
            return v;
        } catch (ReflectiveOperationException exception) {
            Logger logger = LoggerFactory.getLogger(BaseData.class);
            logger.error("在VO与DTO转换时出现了一些错误", exception);
            throw new RuntimeException(exception.getMessage());
        }
    }

    /**
     * 内部使用,快速将当前类中目标对象字段同名字段的值复制到目标对象字段上
     * @param field 目标对象字段
     * @param target 目标对象
     */
    private void convert(Field field, Object target){
        try {
            Field source = this.getClass().getDeclaredField(field.getName());
            field.setAccessible(true);
            source.setAccessible(true);
            field.set(target, source.get(this));
        } catch (IllegalAccessException | NoSuchFieldException ignored) {}
    }
}

分析

这段代码定义了一个名为 BaseData 的接口,其中包含了三个方法和一个私有方法:

  1. asViewObject(Class<V> clazz, Consumer<V> consumer) 方法用于创建指定的 VO 类并将当前 DTO 对象中的所有成员变量值直接复制到 VO 对象中。在返回 VO 对象之前,可以使用 Lambda 表达式进行额外处理。该方法首先调用 asViewObject(Class<V> clazz) 方法创建 VO 对象,然后对其进行额外处理后返回。
  2. asViewObject(Class<V> clazz) 方法用于创建指定的 VO 类并将当前 DTO 对象中的所有成员变量值直接复制到 VO 对象中。该方法通过反射获取指定 VO 类的构造函数和字段,然后创建 VO 对象并将当前 DTO 对象中的成员变量值复制到 VO 对象中的同名字段中。
  3. convert(Field field, Object target) 方法为私有方法,用于在内部快速将当前类中目标对象字段的同名字段的值复制到目标对象字段上。该方法通过反射获取当前类和目标对象字段的值,并将当前类的字段值复制到目标对象字段上。

这段代码的作用是提供了一种便捷的方式来将 DTO 对象转换为 VO 对象,实现了字段值的直接复制。通过该接口,可以方便地实现 DTO 到 VO 的转换,减少重复的转换代码编写工作,并提高代码的可维护性和可读性

用例

使用自定义接口前的转换过程:

// 从数据库中查询出对象
Account account = accountService.findAccountByNameOrEmail(user.getUsername());
AuthorizeVO vo = new AuthorizeVO();
vo.setExpire(jwtUtils.expireTime());
vo.setRole(account.getRole());
vo.setToken(token);
vo.setUsername(account.getUsername());

使用自定义接口后的转换过程:

Account 对象实现 BaseData接口:

@Data
@AllArgsConstructor
@TableName("db_account")
public class Account implements BaseData {

    @TableId(type = IdType.AUTO)
    Integer id;

    String username;

    String password;

    String email;

    String role;

    Date registerTime;
}
Account account = accountService.findAccountByNameOrEmail(user.getUsername());
AuthorizeVO vo = account.asViewObject(AuthorizeVO.class, v -> {
            v.setExpire(jwtUtils.expireTime());
            v.setToken(token);
        });

可以看出使用了自定义的接口方法后,对象的转换变得优雅且简洁,提高了代码的可维护性和可读性

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于Dto(数据传输对象)、Entity(实体对象)和Vo(视图对象)之间的相互转换,可以使用一些常用的转换方法: 1. Entity to Dto: - 手动映射:通过手动设置Dto的每个属性,将Entity的属性值复制到Dto中。 - 使用BeanUtils.copyProperties:使用类库(如Apache Commons BeanUtils)来自动复制Entity的属性值到Dto中,属性名相同的字段会被复制。 2. Dto to Entity: - 手动映射:通过手动设置Entity的每个属性,将Dto的属性值复制到Entity中。 - 使用BeanUtils.copyProperties:使用类库来自动复制Dto的属性值到Entity中,属性名相同的字段会被复制。 3. Entity to Vo: - 手动映射:通过手动设置Vo的每个属性,将Entity的属性值复制到Vo中。 - 使用BeanUtils.copyProperties:使用类库来自动复制Entity的属性值到Vo中,属性名相同的字段会被复制。 4. Vo to Entity: - 手动映射:通过手动设置Entity的每个属性,将Vo的属性值复制到Entity中。 - 使用BeanUtils.copyProperties:使用类库来自动复制Vo的属性值到Entity中,属性名相同的字段会被复制。 需要注意的是,自动映射可能存在一些问题,例如字段名不一致、类型不匹配等,因此在使用自动映射时需要进行一些额外的配置和处理。另外,在某些复杂的场景下,可能需要使用专门的转换工具或框架,如MapStruct、ModelMapper等来简化转换过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值