目录
1.概念叙述
2.类型转换
3.使用实例
1.概念叙述
VO:View Object,主要用于展示层。它的作用是把某个指定前端页面的所有数据封装起来。他的作用主要是减少传输数据量大小和保护数据库隐私数据(如用户密码、用户邮箱等相关信息)不外泄,同时保护数据库的结构不外泄。
DTO:Data Transfer Object,数据传输对象,用于展示层与服务层之间的数据传输对象。(注:实际开发中还存在BO,其作用和DTO类似,当业务逻辑不复杂时一般会被合并。)
PO:Persistant Object,持久化对象,和数据库形成映射关系。简单说PO就是每一个数据库中的数据表,一个字段对应PO中的一个变量。(也就是我们常用的Entities)
几者之间的关系如下图:
从前端页面中收到JSON格式数据,后端接口中将其封装为一个VO对象;接口接收到VO对象后将其转换为DTO对象,并调用业务类方法对其进行处理;然后处理为PO对象,调用Dao接口连接数据库进行数据访问(查询、插入、更新等)。
后端从数据库得到结果后,根据Dao接口将结果映射为PO对象,然后调用业务类方法将其转换为需要的DTO对象,再根据前端页面实际需求,转换为VO对象进行返回。
2.类型转换
上述过程中,VO/DTO/PO等实体类中字段常常会存在多数相同,根据业务需求少数不同。为避免频繁的set和get操作对其进行转换,spring为我们提供了多种方法。
(1)使用BeanUtils:(springframework包下)
UserDto user = new UserDto ();BeanUtils.copyProperties(userInfo ,user ,new String[]{"birthday"});
上述代码中,意思是将左边UserInfo实体类(可以视为一个VO对象)和UserDto实体类类(可以视为一个DTO对象)中一样的值进行赋值user对象中,new String[]{""}中是跳过赋值的字段,该属性可以为空。
(2)使用BeanUtils:(Apache包下)
UserDto user = new UserDto ();BeanUtils.copyProperties(user,userInfo);
上述代码中,意思是将右边UserInfo实体类(可以视为一个VO对象)和UserDto实体类类(可以视为一个DTO对象)中一样的值进行赋值user对象中。(和spring包下方法相反)
(3)使用modelMapper:
导入依赖:
<dependency> <groupId>org.modelmapper</groupId> <artifactId>modelmapper</artifactId> <version>2.3.9</version> </dependency>
配置文件:
@Configurationpublic class ModelMapperConfig { private Converter<Date, String> dateToStringConverter = new AbstractConverter<Date, String>() { @Override protected String convert(Date date) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return date == null ? null : simpleDateFormat.format(date); } }; @Bean public ModelMapper modelMapper() { ModelMapper modelMapper = new ModelMapper(); // 官方配置说明: http://modelmapper.org/user-manual/configuration/ // 完全匹配 modelMapper.getConfiguration().setFullTypeMatchingRequired(true); // 匹配策略使用严格模式 modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); modelMapper.addConverter(dateToStringConverter); configureUser(modelMapper); return modelMapper; } private void configureUser(ModelMapper modelMapper) { //将dto转为UserAccount实体类 modelMapper.typeMap(UserInfor.class, UserAccount.class) .addMappings(mapper -> mapper.map(UserInfor::getUsername, UserAccount::setUsername)) .addMappings(mapper -> mapper.map(UserInfor::getPassword, UserAccount::setPassword));}
使用:
UserAccount account= modelMapper.map(userInfo,UserAccount.class);
上述代码,根据配置文件中configureUser方法中配置的字段,将两个实体类中的字段进行复制赋值。
3.使用实例
下面我们模拟一个业务:后端接口从前端中接收到用户注册数据,进行注册;前端调用查询接口,获取用户的数据。
首先,我们定义VO、DTO、PO对象:
@Data@AllArgsConstructor@NoArgsConstructorpublic class UserInfo implements Serializable { //该实体类为封装好的前端传输页面VO对象 @NotBlank(message = "用户名不能为空") private String Username; @NotBlank(message = "密码不能为空") private String Password;}
@Data@AllArgsConstructor@NoArgsConstructorpublic class UserDTO implements Serializable { //该实体类为业务处理DTO对象 private int Account; private String Username; private String Password; private String Roles;}
@Data@AllArgsConstructor@NoArgsConstructor@TableName("user_account")public class UserAccount implements Serializable { //该实体类为用户账号表对应PO对象 @TableId private int SerialNum; private int Account; private String Username; private String Password; private String Roles; private String status; private String registerDate;}
然后,我们编写用户注册相关代码(基于springboot+mybatis plus),由于使用mybatis plus,此处省略mapper层代码,仅展示serviceImpl和controller中代码。
控制层:
//用户注册 @PostMapping("/register") public CommonResult<Object> userRegister(@RequestBody @Valid UserInfo user){ int result = userService.userRegister(user); if(result!=0){ return CommonResult.success(result); }else { return CommonResult.fail(result); } }
业务处理:
@Override public int userRegister(UserInfo user) { if(accountMapper.selectCount(new QueryWrapper<UserAccount>().eq("username",user.getUsername()))!=0){ log.info("用户名已存在"); return 0; } UserDTO userDTO = new UserDTO(); BeanUtils.copyProperties(userDTO,user); //加密密码 Md5Hash md5Hash = new Md5Hash(userDTO.getPassword(), userDTO.getUsername(),2); userDTO.setPassword(md5Hash.toString()); //模拟生成账号 userDTO.setAccount(12345678); //赋予权限 userDTO.setRoles("user"); return accountMapper.insert(userDTO); }
最终,我们可以往数据库中传入一条用户刚注册的账号数据,status和RegisterDate等属性取数据库设置好的默认值。
然后,我们再定义一个VO用于展示用户的信息:
@Data@AllArgsConstructor@NoArgsConstructorpublic class UserVo implements Serializable { //该VO用于展示用户信息,去除了用户密码等敏感信息 private int serialNum; private int account; private String username;}
进行用户信息的查询,获取所有的用户账号:
业务层:
public List<UserAccount> getAllUser() { return accountMapper.selectList(null); }
控制层:
@GetMapping("/get/all") public CommonResult<Object> getAllAccount() throws InvocationTargetException, IllegalAccessException { //查询所有数据 List<UserAccount> userAccountList = userService.getAllUser(); List<AdminInfo> resultList = new ArrayList<>(); //封装为VO进行展示 for(UserAccount u:userAccountList){ UserVo userVo = new UserVo(); BeanUtils.copyProperties(userVo,u); resultList.add(userVo); } //根据序列号进行排序,后注册的放在最前面 resultList.sort(new Comparator<UserVo>() { @Override public int compare(UserVo o1, UserVo o2) { return o2.getSerialNum() - o1.getSerialNum(); } }); return new CommonResult<>(200,"账号列表如下:",resultList,resultList.size()); }
然后我们就可查询得到所有的去除了敏感信息可用于展示的用户账号信息。
注:此处由于业务简单,没有将PO对象转换为DTO再转换为VO。