当前位置: 首页 > 知识库问答 >
问题:

CQRS中JPA实体与DTO的映射

凌长恨
2023-03-14

我希望在我正在从事的一个项目中使用CQR,但是我目前正在努力寻找实现CQR查询端的最佳方法。基于我有限的理解,有一个瘦数据层(有时称为瘦读取层),用于查询数据库并返回DTO,其中包含应用程序UI层使用的查询结果。

由于这是一个Java的EE应用程序,我正在开发薄数据层,使用JPA使用EntityManager.createNamedQuery查询数据库,返回一个包含结果的实体,然后将其映射到DTO。

假设应用程序的查询端应该是“只读”的,DTO包含每个属性的getter,但不包含setter,以及一个用于在创建时填充属性的构造函数。

对于一个简单的查询,我可以使用构造函数手动将实体中的值映射到DTO,但是这对于更复杂的查询来说并不实用,特别是当实体包含需要映射到相应DTO的“一对多”关系时。我已经研究过如何使用映射框架,例如Dozer和ModelMapper,但是它们似乎都依赖于DTO设置器,并且似乎没有使用构造函数。

下面的代码表示两个实体和两个DTO的非常简化的视图,我创建它们是为了帮助解释这种情况。

@Entity
@Table(name = "ORDER")
public class Order {

  // Various named queries
  @Id
  @Column(name = "ORDER_ID")
  private UUID orderId;

  @Column(name = "ORDER_NUMBER")
  private long orderNumber;

  @Column(name = "ORDER_DATE")
  @Temporal(TemporalType.DATE)
  private Date orderDate;

  @Column(name = "CUSTOMER_NAME")
  private String customerName;

  @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, orphanRemoval=true)
  @JoinColumn(name = "ORDER_NUMBER", referencedColumnName = "ORDER_NUMBER")
  private List<OrderLine> orderLines;

  // Getters and setters, equals, hashCode, toString

}

@Entity
@Table(name = "ORDER_LINE")
public class OrderLine {

  @Id
  @Column(name = "ORDER_LINE_ID")
  private UUID orderLineId;

  @OneToOne(fetch=FetchType.EAGER)
  @JoinColumn(name = "ORDER_ID", referencedColumnName = "ORDER_ID")
  private Order order;

  @Column(name = "PART_NUMBER")
  private String partNumber;

  @Column(name = "DESCRIPTION")
  private String description;

  @Column(name = "UNIT_PRICE")
  private BigDecimal unitPrice;

  @Column(name = "QUANTITY")  
  private int quantity;

  // Getters and setters, equals, hashCode, toString

}

public class OrderDTO {

  private long orderNumber;
  private Date orderDate;
  private String customerName;
  private List<OrderLine> orderLines;

  public OrderDTO() {}

  public OrderDTO(long orderNumber, Date orderDate, String customerName, List<OrderLineDTO> orderLines) {
    this.orderNumber = orderNumber;
    this.orderDate = orderDate;
    this.customerName = customerName;
    this.orderLines = orderLines;
  }

  //Getters but no setters

}

public class OrderLineDTO {

  private String partNumber;
  private String description;
  private BigDecimal unitPrice;
  private int quantity;

  public OrderLineDTO() {}

  public OrderLineDTO(String partNumber, String description, BigDecimal unitPrice, int quantity) {
    this.partNumber = partNumber;
    this.description = description;
    this.unitPrice = unitPrice;
    this.quantity = quantity;
  }

  //Getters but no setters

}

我的问题是:

>

  • 当使用CQR时,DTO应该只有getter和构造函数,还是也可以有setter?

    如果DTO在理想情况下应该只有getter和构造函数,那么映射框架是否是填充DTO的最佳方法,其中实体返回了包含“一对多”关系的复杂结果集?

    是否有任何映射框架可以使用构造函数而不是setter?

  • 共有1个答案

    司徒耀
    2023-03-14
    1. 最好的方法是只有getters和构造函数。那么你的DTO是不可变的。
    2. 您可以通过两种方式产生DTO。一种是使用JPA和类似的查询选择新的my_package。MyDto(u.username,u.email)来自用户u。第二种对于包括许多表结果在内的较大dtos更受欢迎的方法是使用非jpa技术,如MyBatis或SpringJdbcTemboard。第二种方法更有意义,因为您没有从数据库中检索不必要的数据。因此,由于性能原因,您应该只获取创建DTO所需的数据。
    3. 是的-例如最流行的JacksonMapper可以使用构造函数而不是setters。像Orika这样的其他框架也可以创建这样的映射。你可以在我的博客上找到细节-http://www.kubrynski.com/2015/02/datatransferobject-myth-busting.html
     类似资料:
    • 我有一种情况,在一个DTO中有另一个DTO,我必须映射到它对应的实体。 我正在使用mapstruct,我已经有antherEntityMapper已经存在。 如何更改EntityMapper接口,以便我可以将一个另一个EntityDTO映射到另一个Entity? 谢谢

    • 在普通的DDD项目中,我希望从存储库中检索到的实体作为DTO从域层发送到应用层。 CQR存在的一个原因似乎是,应用层所需的查询(主要是读取操作)与域层所需的查询不同。因此,即使是同一对象的查询结果在不同层之间也可能不同。 在我的域层中,我已经将查询结果映射到域实体中。我对一些CQRS示例将查询结果直接映射到跳过匹配实体的DTO感到困惑。 假设数据库返回: 但是,实体布局将姓氏映射为姓氏: 如果在我

    • 问题内容: 我是Go语言的新手,具有C#背景并且对如何构造Go应用程序感到困惑。 假设我正在构建一个REST API,它将位于数据库之上。还要说,即使完成后,鉴于业务的变迁等,此应用程序可能仍需要频繁更改。 在带有诸如Entity Framework和DTO之类的工具的C#中,我通过从控制器给出的结果中抽象出数据库来缓解此问题。如果更改数据库中一堆字段的名称,则可能必须更改数据库访问逻辑,但是希望

    • 我需要一种标准方法来比较JPA实体与其DTO并确定它们是否代表相同的业务对象。我可以想到三种方法,每个DTO上的自定义方法,带有静态方法的接口或比较器。 基于若昂·迪亚斯的回答,方法4-继承。 优点/缺点 方法1-一路坏 方法2-使用接口支持组合而不是继承,但需要使用自定义方法名称的语义学() 方法3-不需要修改源代码 方法4-简化语义学,因为它使用标准但需要“样板 这些方法的任何其他优缺点或对其

    • 我正在尝试使用AutoMapper在LLBLGen实体和DTO之间创建映射。 我的DTO如下所示: ParentEntity包含一个与DTO列表同名的ChildCollection和一个Id(需要忽略其他LLBL字段)。因此,当ParentEntity映射到父d to时,它也应该将ChildCollection映射到一个子列表。 这就是我到目前为止得到的: 这会导致Id被映射,但List的计数为0

    • 如果实体之间存在继承,那么我应该如何在DTO中表示它?例如: 我应该如何使用它们?例如,我发现:我正在做一个web项目。网页的用户想要注册。他/她填写表单,并将其发送到服务器。在服务器端,我首先创建一个DTO,因为它的字段有验证。我从DTO创建一个实体并将其持久化到数据库中。当有对实体的请求时,我将被请求的实体转换为DTO,并将其提供给客户端的用户。这是一个很好的想象,还是不是?