问题描述
public class User {
public Integer id;
public String name;
public String username;
public Integer age;
public Address address;
public String phoneNumber;
public String email;
}
但是我并不总是需要前端的所有User属性。每个屏幕仅需要一些“用户”字段。最好为每个屏幕创建DTO类,因为它们访问不同的属性?像这样:
class UserToScreenADTO implements Serializable {
public String name;
public String email;
}
class UserToScreenBDTO implements Serializable {
public String phoneNumber;
public Address address;
}
class UserToScreenCDTO implements Serializable {
public Integer id;
public String username;
public String email;
}
解决方法
我只会创建一个DTO类,但例如传递给其构造函数
后端要拉出并设置的字段列表。
所有其他字段将为空。
字段列表将由前端传递。
我发现这种方法非常灵活/动态。
它还避免了维护多个类。
我不知道这种方法是否符合任何最佳实践或企业模式
但是创建多个DTO类肯定听起来更糟。
自OP said that the system is used from the same frontend and in the same context起,我认为这是不好的做法,因此不建议使用其他DTO。
理由:
现代前端通常管理后端接收的所有实体的存储。因此,前端可以在存储中查找实体,并且根据缓存策略,可以从存储中加载实体,而不是从服务器请求它们。因此,代替部分获取用户,该用户被发送一次。可以通过使用ETag
s来进一步改善。尽管使用Etag
几乎不会改善延迟,但是它可以改善网络负载,因为对匹配的ETag
的响应是没有正文(!)的304/Not Modified
而不是{{1 }}与身体。尽管200/OK
可与许多Dto对象一起使用,但可能会发生更多(部分)更新。例如,如果用户的电子邮件和电话号码发生更改,并且前端首先请求ETag
,则它将获得一个响应正文,其中包括新的电子邮件。然后,当它稍后再请求UserToScreenADTO
时,它将再次收到包含新电话号码的响应正文。仅使用一个DTO,前端将在第一个请求上收到一个更新的表示,并且所有后续请求(由匹配的UserToScreenBDTO
进行)都将产生ETag
。
此外,更多的类通常意味着更高的复杂性。因此,我建议尽量减少类的数量。如果不想使用304/Not Modified
和/或前端不保留服务器发送的实体的存储,我建议使用peter petrov's answer中描述的方法。
仅当上下文更改时,表示形式也应更改。如果表示形式(例如,用户前端和管理员前端)之间截然不同,则可以证明使用不同的DTO。
,直接“使用”一个DTO或实体会带来很高的成本,通常您以后才需要支付,因此,我建议任何人都像您在此处那样为每个用例创建一个DTO。
以下是一些原因/费用:
- 如果API的用户看到未加载状态的访问器,则将触发延迟加载,这将导致性能下降或延迟初始化异常。如果对象是通过会话传递的,则可能会丢失上下文,从而使对象变成原来的样子,因此,您可能无法始终知道加载的状态。
- 始终加载所有数据可能效率不高。如果您有一些包含大量数据的文本列,则必须通过电线将其传输并具体化为Java Objects等。如果您不使用这些数据,则根本没有意义地加载它。有人可能会说这可以忽略不计,但这取决于您的用例。可能发生的最坏情况? DBMS执行全表扫描或效率较低的索引扫描,而不是仅索引扫描,因为您指示DBMS加载列的值。
- 并非您要为客户端提供的所有状态都应该在关系表示中。如果您进行汇总或将表达式用于例如将列串联在一起,您需要一个DTO。
话虽如此,这是Blaze-Persistence Entity Views的完美用例。
我创建了该库,以允许在JPA模型与自定义接口或抽象类定义的模型之间轻松进行映射,例如类固醇上的Spring Data Projections。这个想法是,您可以按自己喜欢的方式定义目标结构(域模型),并通过JPQL表达式将属性(获取器)映射到实体模型。
针对您的用例的DTO模型可能与Blaze-Persistence Entity-Views相似,如下所示:
@EntityView(User.class)
public interface UserToScreenADTO extends Serializable {
String getName();
String getEmail();
}
@EntityView(User.class)
public interface UserToScreenBDTO extends Serializable {
String getPhoneNumber();
Address getAddress();
}
@EntityView(User.class)
public interface UserToScreenCDTO extends Serializable {
Integer getId();
String getUsername();
String getEmail();
}
查询是将实体视图应用于查询的问题,最简单的方法就是按ID查询。
UserToScreenADTO u = entityViewManager.find(entityManager,UserToScreenADTO.class,id);
Spring Data集成使您可以像使用Spring Data Projections一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features