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

17

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

例如,以下是一个实体和保存修改后对象的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类相同的表,但仅提供客户的姓名和地址。这是真正的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并将它们复制回实际实体的东西,以便可以将它们与EntityManager合并。


1
为了节省一些代码行数,您可以使用Apache Commons-BeanUtils库。请在此处查看http://commons.apache.org/proper/commons-beanutils/apidocs/org/apache/commons/beanutils/BeanUtils.html它具有使用反射从/到POJO复制属性的实用方法。 - gipinani
3个回答

4
如果实体与DTO之间的大小差异不大,您可以选择发送实体。
使用DTO时,为了解决并发问题(如丢失更新),必须在DTO中合并实体版本。
如果您不使用实体版本,并且在REST GET和PUT方法之间更改了基础行,则会覆盖用户没有真正意识到的更改。
每当我必须更改实体(创建、更新、删除),我依赖于JPA和Hibernate乐观锁定机制
对于UI列表、表格、搜索结果,DTO是一个可行的选项,因为您只对源实体的投影感兴趣。这样可以加快检索速度,并且可以受益于其他SQL功能(窗口函数),这些功能不受JPA支持。

1
有时,发送整个实体并将其合并回来可能是危险的。想象一个客户实体,它具有诸如creationDate或login之类的字段,这些字段不应该被特定屏幕修改(但它们在实体上未定义为只读)。如果将整个实体作为JSON发送,然后接收并合并回来,如何确保仅更改了适当的字段?在这种情况下(我认为相当普遍),原始解决方案与实体“视图”似乎更合适,不是吗?而且@Version可防止丢失更新,对吧? - Lucian
1
你可以在那些属性上设置 updatable=false。 - Vlad Mihalcea

0

3
价值对象通常意味着与链接文章中描述的不同。虽然这些术语没有被编码,但最好遵循主流。例如,请参见http://www.adam-bien.com/roller/abien/entry/value_object_vs_data_transfer - xmedeko
谢谢您的评论 +1 - Diversity

0
听起来你所描述的正是Blaze-Persistence Entity Views的用途。目前的版本只支持创建读模型,即你发送给客户端的模型,但写模型部分也快完成了。实体视图在接口/抽象类DTO表示和实体模型之间进行映射。该库尽可能地利用映射信息来实现各种性能优化。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接