我希望在我正在从事的项目中使用 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
}
我的问题是:
使用 CQRS 时,DTO 是否应该只有 getter 和构造函数 或者也可以接受设置者吗?
如果 DTO 理想情况下应该只有 getter 和构造函数,那么是 映射框架是填充实体所在 DTO 的最佳方式 返回包含“一对多”的复杂结果集 关系?
是否有任何映射框架可以使用构造函数而不是 比二传手?
最佳答案
- 最好的方法是只使用 getter 和构造函数。那么你的 DTO 就是不可变的。
- 您可以通过两种方式生成 DTO。一种是使用 JPA 和查询,例如
select new my_package.MyDto(u.username, u.email) from User u
。对于较大的 dto(包括许多表结果)更流行的第二种方法是使用非 jpa 技术,如 MyBatis 或 SpringJdbcTemplate。第二种方法更有意义,因为您不会从数据库中检索不必要的数据。因此,由于性能原因,您应该仅获取创建 DTO 所需的数据。 - 是的 - 例如,最流行的 JacksonMapper 可以使用构造函数而不是 setter。 Orika 等其他框架也可以创建此类映射。您可以在我的博客文章中找到详细信息 - http://www.kubrynski.com/2015/02/datatransferobject-myth-busting.html
关于java - CQRS 中 JPA 实体和 DTO 之间的映射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29061393/