# scaffold 项目之 POJO 对象转换和翻译

# 简介

POJO(Plain Old Java Object,普通老式 Java 对象)通常指的是简单的 Java 对象,它们通常用于封装数据,没有继承特殊的类或实现特殊的接口。尽管 POJO 对象在 Java 应用中非常常见,但在某些情况下,POJO 对象需要进行转换和翻译,原因如下:

  1. 数据模型匹配
    • 不同层级或组件之间可能使用不同的数据模型。例如,数据库层可能使用 Entity 对象,服务层可能使用 DTO(Data Transfer Object),而前端可能需要 VO(View Object)。POJO 对象转换确保这些不同模型之间的数据一致性和正确性。
  2. 数据安全和隐私
    • POJO 对象可能包含敏感信息,不适合直接暴露给客户端或其他系统。通过转换和翻译,可以过滤掉敏感字段,保护数据安全。
  3. 业务逻辑封装
    • POJO 对象转换可以在不同层之间传递数据时,封装业务逻辑,如数据验证、格式化和计算字段值。
  4. 性能优化
    • 通过转换,可以减少不必要的数据传输,只传递所需要的。

# 对象转换

对象转换是指 A 类型对象转成 B 类型对象,例如我有一个 UserDO 对象,需要转成 UserVO 类型或者 UserDTO 类型对象。

关于上述类型说明:

  • DO:与数据库表结构紧密相关,用于数据库操作。
  • VO:用于前端展示,根据用户界面的需求定制数据结构。
  • DTO:用于系统间的数据传输,可以跨越不同的服务和系统。

市面上有很多对象类型转换工具, MapStruct、Dozer、BeanUtil、BeanCopier 等等,本项目使用 MapStruct 和 BeanUtil

# MapStruct

项目使用 MapStruct 来实现对象之间的转换,定义在规范在每个模块下有一个 convert 包, 用于所有 DO 的转换, 每个 DO 对应一个转换类,例如:

可以看到下面的代码在 com.tz.scaffold.module.system.convert 包下, 下面提供的方法就是把 DO 转成 VO

package com.tz.scaffold.module.system.convert.auth;
/**
 * <p> Project: scaffold - AuthConvert </p>
 *
 * Auth Convert
 * @author Tz
 * @date 2024/01/09 23:45
 * @version 1.0.0
 * @since 1.0.0
 */
@Mapper
public interface AuthConvert {
    AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class);
    AuthLoginRespVO convert(OAuth2AccessTokenDO bean);
    // 自己定义的转换
    default AuthPermissionInfoRespVO convert(AdminUserDO user, List<RoleDO> roleList, List<MenuDO> menuList) {
        return AuthPermissionInfoRespVO.builder()
                .user(AuthPermissionInfoRespVO.UserVO.builder().id(user.getId()).nickname(user.getNickname()).avatar(user.getAvatar()).build())
                .roles(convertSet(roleList, RoleDO::getCode))
                // 权限标识信息
                .permissions(convertSet(menuList, MenuDO::getPermission))
                // 菜单树
                .menus(buildMenuTree(menuList))
                .build();
    }
    //MapStruct 会自己帮我们实现转换的逻辑
    AuthPermissionInfoRespVO.MenuVO convertTreeNode(MenuDO menu);
}

# 数据翻译

数据翻译,指的是将 A 类型对象的某个字段,“翻译” 成 B 类型对象的某个字段。例如说,我们有一个 UserVO 的 deptId 字段,读取对应 DeptDO 的 name 字段,最终设置到 UserVO 的 deptName 字段。

有两种处理方法:

  1. 在查询的时候联表查询
  2. 数据库多次单表查询,让后在 java 代码中进行数据拼接。

一般采用第二张方式,减少数据库压力,列如

public CommonResult<UserRespVO> getUser(@RequestParam("id") Long id) {
        AdminUserDO user = userService.getUser(id);
        // 获得部门数据
        DeptDO dept = deptService.getDept(user.getDeptId());
        return success(UserConvert.INSTANCE.convert(user).setDept(UserConvert.INSTANCE.convert(dept)));
    }
@Schema(description = "管理后台 - 用户分页时的信息 Response VO,相比用户基本信息来说,会多部门信息")
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class UserPageItemRespVO extends UserRespVO {
    /**
     * 所在部门
     */
    private Dept dept;
    @Schema(description = "部门")
    @Data
    public static class Dept {
        @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
        private Long id;
        @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部")
        private String name;
    }
}

如果嫌这种方式麻烦可以采用 easy-trans 框架来实现数据翻译,只需要加注解使用,方便简单。

# 扩展

后续切换为使用 easy-trans