Java 中的 VO、DTO、PO、DO 是如何定义和互相转换的?

2022-04-28 14:54:20 +08:00
 devswork

1.项目中 VO 、DTO 、PO 、DO 都是怎么定义(概念)?包括这些 O 在项目 package 路径里是怎么定义的( com.xxx.model.vo )? 2.这些领域对象都是如何转换的?手动 new set get 非常痛苦 3.针对查询参数传递到 mybatis ,是否需要单独的写 xxxxParam 来传参呢? 4.若不写 VO ,是不是 swagger 里就不能显示响应格式、请求参数(body)了?

5501 次点击
所在节点    Java
39 条回复
wolfie
2022-04-28 14:57:19 +08:00
mapstruct (快) 或者 BeanUtil (慢)
TuringHero
2022-04-28 15:04:23 +08:00
MapStruct 正解
sinnosong1
2022-04-28 15:05:18 +08:00
sinnosong1
2022-04-28 15:06:56 +08:00
手动转换痛苦可以用用类似 C#中的"AutoMapper"的库,java 也有只是一般是个人开源项目。
kytrun
2022-04-28 15:33:25 +08:00
推荐 IDEA 的插件 vo2dto ,减轻工作量: https://plugins.jetbrains.com/plugin/18262-vo2dto
VeryZero
2022-04-28 15:37:21 +08:00
将常用转换操作封装成静态方法写在对应的 POJO 里。

比如:
class A {
public Long aa;

public static B2A(B b){
var a = new A();
a.aa = B.bb;
return a;
}
}
sujin190
2022-04-28 15:41:30 +08:00
lombok 为啥不搞个这种支持。。用的地方这么多了,mapstruct 或者 BeanUtil 都不能完美支持静态类型检查,否则重构修改的时候不运行就不知道有问题
aguesuka
2022-04-28 16:09:31 +08:00
@sujin190 Java 是 Nominal type system, 如果完美支持静态类型检查就是 Structural type system 了, 而两个系统是冲突的.

当然可以通过改 IDE 插件来做一个穷人版的, 有时间我写一个
sujin190
2022-04-28 16:18:19 +08:00
@aguesuka #8 我的完美只是说比如 PO 改了字段类型或者删掉了某个字段,如果有 PO 转 DTO 的操作那么编译时就应该有提示,BeanUtil 这种运行时反射显然不行吧,不管 Java 是啥类型系统,我手写 getter setter 肯定是会报错毫无疑问的吧

IDE 插件似乎想做运行时反射类型检查应该不那么容易做吧,否则就是 generate 了,似乎还是不如 lombok 这种方便吧
jellywong
2022-04-28 16:23:04 +08:00
mapstruct
wolfie
2022-04-28 16:30:43 +08:00
@sujin190 #9
mapstruct 编译期类型不一致会抛异常。一般类型会帮转换。
aguesuka
2022-04-28 16:32:11 +08:00
@sujin190 Structural type 是像 ts 那样, 是编译时不是运行时的. 如果 DO 和 DTO 字段完全一样, 那么它们就是一个类型, 如果少数字段不一样, 我们可以用解构语法, 并且是静态安全的.
Oktfolio
2022-04-28 16:37:17 +08:00
AutoMapper 类似的库有 orika-mapper 和 DozerMapper ,但是好像都不如 AutoMapper 好用

反正我不喜欢 MapStruct ,就像不喜欢 Lombok 一样
sujin190
2022-04-28 16:50:13 +08:00
@wolfie #11 这货烦人的就是不能像 Lombok 加个注解就搞定,interface 也不想加,否则就用 idea 的 generate 了
angryfish
2022-04-28 16:58:06 +08:00
1.项目中和数据库一致的,定义为 dto ,如果需要特别扩展属性的,加一个 vo 。其他什么 po,do 乱七八糟的一概不用。
2.路径按照功能模块,房子啊 com.xxx.model
3.mapstruct
4.dto ,vo 混用。别介意。
nothingistrue
2022-04-28 17:11:33 +08:00
这个已经过时了,除非你是在改当前项目,不建议再去深究了。

现在,不管是 Hibernate 还是 Mybatis plus ,不管是 DDD 还是非 DDD ,Entity 都是一个特殊的对象类型,这个很好区分,他是跟数据库的表映射或者绑定的(如果是 DDD ,它还有行为方法)。

DTO 在是一个重对象,它还会一直用下去,但是很少会使用。它的区分也很简单,它是一个重对象,自带数据转换逻辑,并且通常跟工厂一起使用。只有 Getter/Setter 的轻对象,不会是 DTO 的,国内有些人把上下层之间传输的参数一律叫做 DTO ,这是很大的误区。

然后剩下的,VO 、PO 、DO 什么的,它们原本的定义是跟层绑定的,一个层使用一种 O ,禁止跨层使用(层与层要额外通过 DTO 来隔离,比如 VO 经 DTO 转换成 DO )或者只允许上层使用下层的。这些已经死翘翘了。前面已经说了,DTO 很重,没有人会采用“禁止跨层使用”的方式,都是采用“只允许上层使用下层”方式。然后,既然允许上层调用下层了,那为什么不直接调用 Entity 呢,所以最后全部都用 Entity 了。

简单来说,除了 Entity 、DTO ,剩下的本质上都是 Data ,为了层解耦才定义了那么多 O 。随着垂直分层模式的崩溃,这些 O 也崩溃了。

至于楼主的其他问题。2 ,如果你要负责任的话,那就必须手动转换,再痛苦也要转,业务太多变了,这玩意工具的作用很有限,当然有缓解的手段,对于一些纯内部使用的类,你可以考虑 lombok 的 chain 或 fluent 模式。3 ,是,专项专用,但是你可以通过合理规划查询 SQL ,使得多个 SQL 公用一个参数类。4.如果你要负责任的话,那必须给 Swagger ,或者说 Web 这一层,定义专门的数据对象( Swagger 叫做 model ,内部可定义为 value object VO ,也可以就叫做 Data )。
pocketz
2022-04-28 18:37:46 +08:00
oneisall8955
2022-04-28 19:05:03 +08:00
@sujin190 #7 Mapstruct 编译的时候就生成代码的,会抛出异常
qW7bo2FbzbC0
2022-04-28 19:13:15 +08:00
@nothingistrue #16 只有 getter setter 的后缀是什么? RD => Record?
lessMonologue
2022-04-28 19:17:02 +08:00
@nothingistrue 大佬请问有没有什么比较标准的开源项目可以参考一下的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/849815

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX