VO(视图模型) 与 DTO(数据传输对象)
前言
VO(View Object):VO是显示视图模型,视图对象,用于展示层,它的作用是把某个指定页面(或组件)的所有数据封装起来。举例:展示层将DTO传送过来男性显示成帅哥(客户端1),或者显示成靓仔(客户端2);将帅哥或者靓仔,转换成男性,以DTO形式请求服务端。
DTO(Data Transfer Object):DTO是传输模型,数据传输对象,在这里泛指用于展示层与服务层之间的数据传输对象。如果是一个DTO对应一个VO,则DTO=VO;但是如果一个DTO对应多个VO,则展示层需要把VO转换为服务层对应方法所要求的DTO,传送给服务层。从而达到服务层与展示层解耦的效果。数据传输目标往往是数据访问对象从数据库中检索数据。假设你数据库中定义了User类,包含用户名、密码、邮箱、手机号等等;当用户登录时一般只需要输入用户名和密码,那么传入服务端的用户名和密码就可以在controller层封装到UserDTO实体类中。
一、DTO
一个DTO可以对应多个从仓储层返回的DO(Domain Object,领域模型)的json数组,这里可以使用AutoMapper来进行自适配。DTO不是为MVC的视图而存在的模型,而是为了适应来自前端请求而存在的。DTO模型把来自前端的请求(这个请求不管来自前后端分离的页面,还是mvc的视图页面)封装在DTO模型中,然后服务端处理转换成Entity Framework中的领域模型。
在一个DTO对应一个VO的时候,DTO = 视图模型。 DTO 和 VO 的属性值基本是一致的,而且他们通常都是 json对象,因此没必要多此一举,但不要忘记这是实现层面的思维,对于设计层面来说,概念上还是应该存在 VO 和 DTO ,因为两者有着本质的区别,DTO 代表服务层需要接收的数据和返回的数据,而 VO 代表展示层需要显示的数据。应用时,可以在前端将DTO包成VO,也可以在服务端的Controller层,service层包成VO。VO转换成DTO同理。在这种情况下可以实现,前后端相关的东西只有api的url和vo,而且大多数情况也是只有一个客户端的。
在一个DTO对应多个VO的时候,DTO ≠ 视图模型。此时需要在前端将男性打包成帅哥或者靓仔显示;请求数据时,也需要前端将帅哥或者靓仔转换成男性,以DTO形式请求服务端。
- 用户发出请求(可能是填写表单),表单的数据在展示层被匹配为VO。
- 展示层把VO转换为服务层对应方法所要求的DTO,传送给服务层。
- 服务层首先根据DTO的数据构造(或重建)一个DO,调用DO的业务方法完成具体业务。
- 服务层把DO转换为持久层对应的PO(可以使用ORM工具,也可以不用),调用持久层的持久化方法,把PO传递给它,完成持久化操作。
对于一个逆向操作,如读取数据,也是用类似的方式转换和传递。
二、MapStruct
<!--mapStruct相关依赖-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
<!--mapStruct相关依赖-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
使用MapStruct需要编写convert接口
import com.pbteach.java.mapstruct.model.UserDetailVo;
import com.pbteach.java.mapstruct.model.UserDto;
import com.pbteach.java.mapstruct.model.UserVo;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper//接口上加此注解mapstruct自动实现该接口
public interface UserConvert {
UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
/**
* 一个对象映射为一个对象
@Mapping:属性映射,当源对象属性与目标对象属性名一致时无需定义,否则需要定义映射对应的属性
source:源属性
target:目标属性
*/
@Mappings({
@Mapping(source = "fullname", target = "name")
})
UserDto vo2dto(UserVo vo);
//两个对象映射为一个对象
@Mappings({
@Mapping(source = "vo.fullname", target = "name")
})
UserDto vo2dto(UserVo vo, UserDetailVo userDetailVo);
//dto转成vo
@Mappings({
@Mapping(source = "name", target = "fullname")
})
UserVo dto2vo(UserDto dto);
}
将vo转为dto
UserDto userDto = UserConvert.INSTANCE.vo2dto(userVo);