JPA 模式:从实体生成数据传输对象 DTO 并将 DTO 合并到数据库

2022-09-03 02:58:46

我正在寻找一种从JPA实体创建数据传输对象(DTO)的好方法,反之亦然。我想将 DTO 作为 JSON 发送到客户端,然后接收修改后的 DTO 并将其保存回数据库。在接收的对象从 JSON 解析为其 Java 类之后,从 EntityManager 对它执行合并方法是最简单的。

例如,有以下实体和 Rest 方法用于保存修改后的对象:


@Entity
@Table(name="CUSTOMER")
public class Customer {
    @Id
    Long id;
    @Version
    Long version;
    String name;
    String address;
    String login;
    String password;
    String creditCardNumber;
    @OneToMany(cascade = CascadeType.ALL)
    List<Foo> fooList;

    ... Getter() and Setter()
}

private EntityManager em;
@POST
@Path("/saveCustomer")
public void saveCustomer ( Customer  customer)   {               
   em.merge(customer);
   return;
}  

只要我将整个实体类作为 JSON 发送并接收回整个实体,这就可以正常工作。然后,EntityManager 会将修改后的对象合并到数据库中。但是,当我只想提供实体的子集(例如仅提供客户的名称和地址)时,将出现问题:

  1. 创建实体子集的最佳方式是什么?

    -手动为实体编写 DTO?这将为实体的每个子集生成重复的代码,必须对其进行维护。

  2. 如何将作为实体子集的DTO合并回数据库?

    -使用 EntityManager 的 merge() 方法不起作用。起初,DTO不是实体,因此无法合并。并且仅从 DTO 创建实体,实体中就会有一些未设置的值。合并后,数据库中的值将为 NULL。


我想出的一个想法是,为我想要的实体的每个子集指定其他实体。(类似于数据库视图)这将是重复的代码,但它可以解决将 DTO 合并到数据库的问题。(也许这段代码可以自动生成)

例如,实体 CustomerView1 链接到与 Customer 类相同的表,但仅提供客户的名称和地址。它是真实客户类的 DTO,可以作为 JSON 发送并在服务器外部进行修改。然后,此类还可以由 EntityManager 合并到数据库中。

@Entity
@Table(name="CUSTOMER")
public class CustomerView1 {
    @Id
    Long id;
    @Version
    Long version;
    String name;
    String address;
    
        ... Getter() and Setter()
}    

但是我对这个解决方案有疑问,我不知道这是否会扰乱JPA的实体缓存并可能导致一些问题。


我的问题是,是否有一种模式来解决DTO的代码重复并将DTO合并回数据库?

还是有用于此目的的图书馆?- 某些东西,例如DTO的自动生成以及将DTO复制回真实实体,以便将它们与EntityManager合并。


答案 1

如果实体和 DTO 之间的大小差异不大,您可以选择发送实体。

使用 DTO 时,若要解决并发问题(如更新丢失),必须将实体版本合并到 DTO 中。

如果不使用实体版本,并且在 REST GET 和 PUT 方法之间更改了基础行,则将重写最终用户尚未真正意识到的更改。

每当我必须更改实体(创建,更新,删除)时,我都依赖于JPA和Hibernate Optimistic Locking机制

对于 UI 列表、表、搜索结果 DTO 是一个可行的选项,因为您只对原始实体的投影感兴趣。通过这种方式,您可以加快检索速度,并且可以从JPA不支持的其他SQL功能(窗口功能)中受益。


答案 2

看看值对象设计模式,它直接解决了你的问题。

本教程很好地介绍了值对象。

http://www.javastuff.in/2012/04/value-object-pattern.html


推荐