1.项目中 VO 、DTO 、PO 、DO 都是怎么定义(概念)?包括这些 O 在项目 package 路径里是怎么定义的( com.xxx.model.vo )? 2.这些领域对象都是如何转换的?手动 new set get 非常痛苦 3.针对查询参数传递到 mybatis ,是否需要单独的写 xxxxParam 来传参呢? 4.若不写 VO ,是不是 swagger 里就不能显示响应格式、请求参数(body)了?
1
wolfie 2022-04-28 14:57:19 +08:00
mapstruct (快) 或者 BeanUtil (慢)
|
2
TuringHero 2022-04-28 15:04:23 +08:00
MapStruct 正解
|
3
sinnosong1 2022-04-28 15:05:18 +08:00
|
4
sinnosong1 2022-04-28 15:06:56 +08:00
手动转换痛苦可以用用类似 C#中的"AutoMapper"的库,java 也有只是一般是个人开源项目。
|
5
kytrun 2022-04-28 15:33:25 +08:00
推荐 IDEA 的插件 vo2dto ,减轻工作量: https://plugins.jetbrains.com/plugin/18262-vo2dto
|
6
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; } } |
7
sujin190 2022-04-28 15:41:30 +08:00
lombok 为啥不搞个这种支持。。用的地方这么多了,mapstruct 或者 BeanUtil 都不能完美支持静态类型检查,否则重构修改的时候不运行就不知道有问题
|
8
aguesuka 2022-04-28 16:09:31 +08:00
@sujin190 Java 是 Nominal type system, 如果完美支持静态类型检查就是 Structural type system 了, 而两个系统是冲突的.
当然可以通过改 IDE 插件来做一个穷人版的, 有时间我写一个 |
9
sujin190 2022-04-28 16:18:19 +08:00
@aguesuka #8 我的完美只是说比如 PO 改了字段类型或者删掉了某个字段,如果有 PO 转 DTO 的操作那么编译时就应该有提示,BeanUtil 这种运行时反射显然不行吧,不管 Java 是啥类型系统,我手写 getter setter 肯定是会报错毫无疑问的吧
IDE 插件似乎想做运行时反射类型检查应该不那么容易做吧,否则就是 generate 了,似乎还是不如 lombok 这种方便吧 |
10
jellywong 2022-04-28 16:23:04 +08:00
mapstruct
|
12
aguesuka 2022-04-28 16:32:11 +08:00
@sujin190 Structural type 是像 ts 那样, 是编译时不是运行时的. 如果 DO 和 DTO 字段完全一样, 那么它们就是一个类型, 如果少数字段不一样, 我们可以用解构语法, 并且是静态安全的.
|
13
Oktfolio 2022-04-28 16:37:17 +08:00
AutoMapper 类似的库有 orika-mapper 和 DozerMapper ,但是好像都不如 AutoMapper 好用
反正我不喜欢 MapStruct ,就像不喜欢 Lombok 一样 |
14
sujin190 2022-04-28 16:50:13 +08:00
@wolfie #11 这货烦人的就是不能像 Lombok 加个注解就搞定,interface 也不想加,否则就用 idea 的 generate 了
|
15
angryfish 2022-04-28 16:58:06 +08:00
1.项目中和数据库一致的,定义为 dto ,如果需要特别扩展属性的,加一个 vo 。其他什么 po,do 乱七八糟的一概不用。
2.路径按照功能模块,房子啊 com.xxx.model 3.mapstruct 4.dto ,vo 混用。别介意。 |
16
nothingistrue 2022-04-28 17:11:33 +08:00 1
这个已经过时了,除非你是在改当前项目,不建议再去深究了。
现在,不管是 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 )。 |
17
pocketz 2022-04-28 18:37:46 +08:00
|
18
oneisall8955 2022-04-28 19:05:03 +08:00
@sujin190 #7 Mapstruct 编译的时候就生成代码的,会抛出异常
|
19
qW7bo2FbzbC0 2022-04-28 19:13:15 +08:00
@nothingistrue #16 只有 getter setter 的后缀是什么? RD => Record?
|
20
lessMonologue 2022-04-28 19:17:02 +08:00
@nothingistrue 大佬请问有没有什么比较标准的开源项目可以参考一下的
|
21
MakHoCheung 2022-04-28 20:04:57 +08:00
看了下评论,能否通俗点?
1. DAO 返回给 Service 的是只返回 Entity ( PO )吗,如果涉及到连表的话是不是要返回 DTO ? 2. DAO 返回的 Entity 需要在 Service 转成 DTO 然后再 Controller 层转成 VO 返回给前端吗,如果三者内容一致呢,是不是有多余重复的复制操作了 C#、Go 开发 Web 有这么多规范吗 |
22
KingOfUSA 2022-04-28 20:32:24 +08:00
推荐这个库 https://github.com/ksprider/Surgical 至少在 controller 层可以无需转换成 vo ,也能将所需要的字段序列化掉,而不用新建一层 vo ,主要是这个 vo 复用性为 0 呐,
|
23
aragakiyuii 2022-04-28 21:41:06 +08:00
基本都是贫血模型搞这么多概念干嘛。。。需要去区分字段的时候定义一个新的就好了
|
24
Leviathann 2022-04-28 21:47:20 +08:00
一条垂直链路下来搞那么多纯属吃饱了撑的
我们的项目只有 entity aggregate 输出输入专用对象 三种,如果算上部分字段查询结果的对象那就是四种 逻辑都放在 aggregate 里 |
25
leeg810312 2022-04-28 22:03:35 +08:00
我同时用.net 和 Java 开发,所以行业内 2 个平台有各自的设计偏好我都有了解,现在实践中发现只用 2 种数据模型就足够了,Entity 用于数据表映射和 DTO 数据传输,太多模型分类都是过度设计,徒增无用的复杂性。ORM 获取数据都是 Entity ,DTO 顾名思义数据传输对象,那么不管转给中间服务层或是控制器都是 DTO ,不再分出其他的概念,不像楼上有人说会用得少。DTO 类的设计原则是根据业务需要,裁剪、增加、拼装 Entity 的属性,甚至可以增加一些辅助的方法,用平台常用的对象映射工具 AutoMapper/MapStruct 等进行 Entity 和 DTO 之间的转换。
|
26
twing37 2022-04-28 22:50:32 +08:00
1. VO 如果你写过脚本语言,如 php, 混在 V 层的模板对象就是 VO,
2. DTO 前后端分类的,接口对象. > 注 1: 1 与 2 的区别在于, 举个例子, 如 性别字段, vo 里面是男, dto 里面是 1, 3. 需要. 比如, http/grpc 的 json 与 proto --> service(use case)的逻辑层对象, 4. 参见 1 与 2 的区别. > 注 2: 在足够简单的项目中,确定架构不会改变的情况下,请忽略这些.不管是 clean arch 还是 ddd 的六边形 都不如梭哈. 我在之前的类似答案中也写过,在这个前提下,不如面条代码写来的更好.比如在 repo 下,只是简单的 curd, 完全可以嵌入整个 store 逻辑,因为该逻辑很难再去复用. |
27
hingbong 2022-04-29 00:07:54 +08:00 via Android
我用 GitHub copilot 生成转换代码
|
28
xuanbg 2022-04-29 06:59:11 +08:00
我的做法是 DTO 用来接收数据,VO 返回数据。然后它们相同的字段从一个不带 XO 的同名类继承。相互转换很简单啊,就是 A 序列化后再反序列化成 B 就行了呀。效率虽低,但基本没有影响。谁又会在意一个接口的响应时间慢了几个微秒呢。
|
29
MonkeyJon 2022-04-29 08:40:38 +08:00
自己写个装换器或者 MapStruct
|
30
securityCoding 2022-04-29 09:14:38 +08:00 via Android 1
这套东西扔了吧,一群学院派搞出来的高大上概念
|
31
nothingistrue 2022-04-29 09:27:19 +08:00
@hjahgdthab750 #19 后缀是由架构或规范决定的,没有特定规则。光凭“只有 getter setter ”这一个特点,是不知道这个类是干啥的,还要结合其他特点才能确切直到这个类是干啥的,之后才能命名。
@lessMonologue #20 没有。就算有也不具备参考意义,因为这本来就是规范不是规则,是随团队变化的。 @MakHoCheung #21 再严格一点,上下层之间不管调用还是返回都只能是 DTO ,DTO 负责对象的转换。例:Service 利用 DTO 的静态方法或者工厂方法将 BO 扔进 DTO ,再用这个 DTO 作为调用 DAO 的参数;反过来就是 DAO 将 Entity 利用工厂方法将扔进 DTO ,再返回 DTO 。这三者内容不可能一致的,Entity 是 ORM 的托管对象会带有 ORM 的信息,DTO 要负责数据转换会带有业务逻辑,BO 为 Service 服务会有业务逻辑的中间数据。然而,这随不是重复操作,但是属于杀鸡用牛刀的操作。 @leeg810312 #25 你这个好,用 DTO 把数据转换和数据对象都给包了,就只剩下 Entity 和 DTO 两种,没有纯数据的 Data 了。 |
32
notwaste 2022-04-29 10:05:30 +08:00
一路看下来都是经验之谈主观看法,那么我还是坚持我的想法跟着自身项目走就可以,本身就是为了规范而存在的
|
33
LLaMA2 2022-04-29 14:33:05 +08:00
我一直想问各位大佬,mapstruct 中转换树形数据,不确定层级的嵌套数据有什么好办法,我用的时候每天被折磨的很难过,后来我用 TS 写代码,typeorm 就没有这个苦恼了
|
34
Dlin 2022-04-29 14:48:18 +08:00
这些概念,往往都只是个概念,实践起来麻烦琐碎。
|
35
issakchill 2022-04-29 15:20:50 +08:00
@ye4tar #33
|
36
issakchill 2022-04-29 15:23:59 +08:00
@issakchill 错手回复了空内容.. 如果是树形转树形 mapstruct 好像是自动递归的
|
37
cco 2022-04-29 16:46:08 +08:00
VO: 返回给前端,或者响应给请求者的对象。
DTO: 请求参数、数据库查询列映射 负责数据传输。 Entity: 和数据库表一一对应的实体对象。 一般用这三个。这东西和接口与实现分包放一样,要不要都可以。对于数据库查询出来的数据,直接响应回去,那没必要 DTO (或者 Entity ) -> BO(可以理解为还是 DTO) -> VO ,直接返回即可。 至于转换关系。mapstruct 、BeanUtil 都可以,不管什么都不可避免要手动加点进去。如果你查出来的列名和你要响应的字段都不一致呢?这些类库以及 idea 的插件也只是方便而已,不可能实现一键转换。除非你转换前和转换后几乎是一模一样的。 |