聚合到JPA实体Map

yeotifhr  于 2022-11-14  发布在  其他
关注(0)|答案(3)|浏览(145)

在我参与的一个DDD-项目中,我们正在寻找一些方便的解决方案来将entity objectsMap到domain objects,反之亦然。
该项目的开发人员同意将领域模型与数据模型完全解耦,数据层使用JPA (Hibernate)作为持久化技术。
我们都认为持久性是DDD中的一个 * 实现细节 *,从开发人员的Angular 来看,我们都在寻找应用程序各个方面的最合适的解决方案。
我们最担心的是,当一个aggregate,包含一个entities的列表,被Map到一个JPA entity,而这个JPA entity又包含一个one-to-many关系。
请看下面的例子:

域模型

public class Product extends Aggregate {
    private ProductId productId;
    private Set<ProductBacklogItem> backlogItems;

    // constructor & methods omitted for brevity
}
public class ProductBacklogItem extends DomainEntity {
    private BacklogItemId backlogItemId;
    private int ordering;
    private ProductId productId;

    // constructor & methods omitted for brevity
}

数据模型

public class ProductJpaEntity {
    private String productId;
    @OneToMany
    private Set<ProductBacklogItemJpaEntity> backlogItems;

    // constructor & methods omitted for brevity
}
public class ProductBacklogItemJpaEntity {
    private String backlogItemId;
    private int ordering;
    private String productId;

    // constructor & methods omitted for brevity
}

存储库

public interface ProductRepository {        
    Product findBy(ProductId productId);
    void save(Product product);
}
class ProductJpaRepository implements ProductRepository {        
    @Override
    public Product findBy(ProductId productId) {
        ProductJpaEntity entity = // lookup entity by productId

        ProductBacklogItemJpaEntity backlogItemEntities = entity.getBacklogItemEntities();        
        Set<ProductBacklogItem> backlogItems = toBackLogItems(backlogItemEntities);

        return new Product(new ProductId(entity.getProductId()), backlogItems);
    }

    @Override
    public void save(Product product) {
        ProductJpaEntity entity = // lookup entity by productId

        if (entity == null) {
          // map Product and ProductBacklogItems to their corresponding entities and save
          return;
        }

        Set<ProductBacklogItem> backlogItems = product.getProductBacklogItems();
        // how do we know which backlogItems are: new, deleted or adapted...?
    }
}

DB中已经存在一个ProductJpaEntity时,我们需要更新所有的内容。在更新的情况下,ProductJpaEntity在Hibernate PersistenceContext中已经可用。但是,我们需要找出哪些ProductBacklogItems被更改了。
更具体地说:

  • ProductBacklogItem可能已添加到Collection
  • ProductBacklogItem可能已从Collection中删除

每个ProductBacklogItemJpaEntity都有一个指向ProductJpaEntityPrimary Key。似乎检测新的或删除的ProductBacklogItems的唯一方法是通过Primary Key来匹配它们。但是,主键不属于域模型...
也有可能首先删除ProductJpaEntity的所有ProductBacklogItemJpaEntity示例(存在于DB中),刷新到DB,创建新的ProductBacklogItemJpaEntity示例并将其保存到DB中。这将是一个糟糕的解决方案。每次保存Product都会导致DB中出现几个deleteinsert语句。
哪种解决方案可以解决此问题,而又不会对域和数据模型造成太多牺牲?

8gsdolmq

8gsdolmq1#

您可以让JPA/Hibernate为您解决问题。

public void save(Product product) {
    ProductJpaEntity entity = convertToJpa(product);
    entityManager.merge(entity);
    // I think that actually save(entity) would call merge for you,
    // if it notices that this entity already exists in database
}

这样做的结果是:

  • 它将获取新创建的JPA实体并附加它
  • 它将检查数据库中的内容,并相应地更新所有关系,优先级将赋予您创建的实体(如果Map设置正确)
c86crjj0

c86crjj02#

这是Blaze-Persistence实体视图的完美用例。
我创建这个库是为了允许在JPA模型和自定义接口或抽象类定义的模型之间进行简单的Map,就像Spring Data Projections一样。其思想是您可以按照自己喜欢的方式定义目标结构(域模型),并通过JPQL表达式将属性(getter)Map到实体模型。
实体视图也可以是 * 可更新 * 和/或 * 可创建 * 的,即支持刷新更改,这可用作DDD设计的基础。可更新的实体视图实施脏状态跟踪。您可以检查实际更改或刷新更改的值。
您可以将可更新的实体视图定义为抽象类,以隐藏“实现细节”,例如protected修饰符后面的主键,如下所示:

@UpdatableEntityView
@EntityView(ProductJpaEntity.class)
public abstract class Product extends Aggregate {
    @IdMapping
    protected abstract ProductId getProductId();
    public abstract Set<ProductBacklogItem> getBacklogItems();
}
@UpdatableEntityView
@EntityView(ProductBacklogItemJpaEntity.class)
public abstract class ProductBacklogItem extends DomainEntity {
    @IdMapping
    protected abstract BacklogItemId getBacklogItemId();
    protected abstract ProductId getProductId();
    public abstract int getOrdering();
}

查询是将实体视图应用于查询的问题,最简单的是按id查询。
Product p = entityViewManager.find(entityManager, Product.class, id);
保存(即刷新)更改也很容易
entityViewManager.save(entityManager, product);
Spring Data 集成允许您像Spring Data 投影一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features,为了刷新更改,您可以在存储库中定义一个接受可更新实体视图的save方法

insrf1ej

insrf1ej3#

我相信你需要用不同的方式来解决这个问题。当你有一个复杂的对象图时,很难确定哪些被改变了。但是,应该有其他人(也许是一个服务)真正知道哪些被改变了。
实际上,我在您的问题中没有看到真实的的业务“Service”,也没有看到一个解决业务逻辑的类,这个才是解决这个问题的,所以。您将在您的存储库中拥有一些更具体的removeProductBacklogItem(BacklogItemId idToRemove)或... addProductBacklogItem(ProductId toProductId, ProductBacklogItem itemToAdd)。这将迫使您以其他方式管理和识别更改...而服务将负责。

相关问题