java - CQRS 中 JPA 实体和 DTO 之间的映射

标签 java jpa cqrs data-transfer-objects

我希望在我正在从事的项目中使用 CQRS,但是我目前正在努力寻找实现 CQRS 查询方面的最佳方法。根据我有限的理解,有一个精简数据层(有时称为精简读取层),它查询数据库并返回带有查询结果的 DTO,供应用程序的 UI 层使用。

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

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

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

以下代码代表了两个实体和两个 DTO 的非常简化的 View ,我创建这些 View 是为了帮助解释这种情况。

@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

}

我的问题是:

  1. 使用 CQRS 时,DTO 是否应该只有 getter 和构造函数 或者也可以接受设置者吗?

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

  3. 是否有任何映射框架可以使用构造函数而不是 比二传手?

最佳答案

  1. 最好的方法是只使用 getter 和构造函数。那么你的 DTO 就是不可变的。
  2. 您可以通过两种方式生成 DTO。一种是使用 JPA 和查询,例如 select new my_package.MyDto(u.username, u.email) from User u。对于较大的 dto(包括许多表结果)更流行的第二种方法是使用非 jpa 技术,如 MyBatis 或 SpringJdbcTemplate。第二种方法更有意义,因为您不会从数据库中检索不必要的数据。因此,由于性能原因,您应该仅获取创建 DTO 所需的数据。
  3. 是的 - 例如,最流行的 JacksonMapper 可以使用构造函数而不是 setter。 Orika 等其他框架也可以创建此类映射。您可以在我的博客文章中找到详细信息 - http://www.kubrynski.com/2015/02/datatransferobject-myth-busting.html

关于java - CQRS 中 JPA 实体和 DTO 之间的映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29061393/

相关文章:

Java Final 关键字 vs C++ 常量指针 vs C++ 引用

java - 即使添加 classic.ClassicQueryTranslatorFactory 后,ClassNotFoundException : org. hibernate.hql.internal.ast.HqlToken

java - EntityListener 未调用

java - 从 Java Spring MVC 调用 Node.Js Rest API(POST)

Java 闭包类型、变量、数组和集合

java - 在 Android 中从浏览器外部访问 Web Storage 或 IndexedDB

java - 如何使用原生sql获取现有表

cqrs - 在事件源中处理大量事件

CQRS 和领域事件

c# - 如何使用 DDD/CQRS 编写功能