聚合到JPA实体的映射 [英] Aggregate to JPA Entity mapping

查看:130
本文介绍了聚合到JPA实体的映射的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我参与的 DDD 项目中,我们正在寻找一些便捷的解决方案来映射实体对象转换为域对象,反之亦然。



该项目的开发人员同意将域模型与数据模型完全分离。
数据层使用 JPA(休眠)作为持久性技术。



我们都认为持久性是DDD中的实现细节,从开发人员的角度来看,我们都在寻求应用程序各个方面的最合适解决方案。



我们最担心的是将包含实体的列表的聚合映射到一个 JPA实体,而该实体又包含一个一对多关系。



看看下面的例子:



域模型



 公共类产品扩展了汇总{
private ProductId productId;
private Set< ProductBacklogItem> backlogItems;

//构造函数&为简便起见省略了方法
}

 公共类ProductBacklogItem扩展了DomainEntity {
private BacklogItemId backlogItemId;
私人int订购;
private ProductId productId;

//构造函数&为简便起见省略了方法
}



数据模型



 公共类ProductJpaEntity {
private String productId;
@OneToMany
private Set< ProductBacklogItemJpaEntity> backlogItems;

//构造函数&为简便起见省略了方法
}

 公共类ProductBacklogItemJpaEntity {
private String backlogItemId;
私人int订购;
private String productId;

//构造函数&为简便起见省略了方法
}



存储库



 公共接口ProductRepository {
Product findBy(ProductId productId);
无效保存(产品产品);
}

 类ProductJpaRepository实现ProductRepository {
@Override
public Product findBy(ProductId productId){
ProductJpaEntity实体= //通过productId
$ b查找实体$ b ProductBacklogItemJpaEntity backlogItemEntities = entity.getBacklogItemEntities();
Set< ProductBacklogItem> backlogItems = toBackLogItems(backlogItemEntities);

返回新产品(新ProductId(entity.getProductId()),backlogItems);
}

@Override
public void save(产品product){
ProductJpaEntity实体= //通过productId

的查找实体if(实体== null){
//将Product和ProductBacklogItems映射到其对应的实体,并保存
返回;
}

Set< ProductBacklogItem> backlogItems = product.getProductBacklogItems();
//我们如何知道哪些backlogItem是:new,deleted或adapted ...?
}
}

ProductJpaEntity code>已存在于 DB 中,我们需要更新所有内容。
如果有更新,则 ProductJpaEntity 在Hibernate PersistenceContext 中已经可用。
但是,我们需要找出哪些 ProductBacklogItems 已更改。





更具体地说:




  • ProductBacklogItem 可以被添加到 Collection

  • ProductBacklogItem 集合




$ b中删除$ b

每个 ProductBacklogItemJpaEntity 有一个主键指向 ProductJpaEntity
似乎检测到新的或已删除的 ProductBacklogItems 的唯一方法是通过主键进行匹配。
但是,主键不属于域模型...



还可以先删除所有 ProductBacklogItemJpaEntity < ProductJpaEntity 的/ code>实例(存在于数据库中),刷新到数据库,创建新的 ProductBacklogItemJpaEntity 实例并将其保存到数据库。
这将是一个糟糕的解决方案。每次保存产品都会导致多次删除插入

存在哪些解决方案可以解决此问题,而不会在Domain&数据模型?

解决方案

这是火焰持久性实体视图



我创建了该库以允许轻松映射在JPA模型和自定义接口或抽象类定义的模型之间,例如类固醇上的Spring Data Projections。想法是您可以按照自己喜欢的方式定义目标结构(域模型),并通过JPQL表达式将属性(获取器)映射到实体模型。



实体视图也可以可更新和/或可创建,即支持回冲更改,可以用作DDD设计的基础。
可更新的实体视图实现脏状态跟踪。您可以内省实际的更改或刷新更改的值。



您可以将可更新的实体视图定义为抽象类,以隐藏实现细节,例如这样的受保护修饰符后面的主键:

  @UpdatableEntityView 
@EntityView(ProductJpaEntity.class)
公共抽象类产品扩展了聚合{
@IdMapping
受保护的抽象ProductId getProductId();
公共摘要Set< ProductBacklogItem> getBacklogItems();
}
@UpdatableEntityView
@EntityView(ProductBacklogItemJpaEntity.class)
公共抽象类ProductBacklogItem扩展了DomainEntity {
@IdMapping
受保护的抽象BacklogItemId getBacklogItemId();
受保护的抽象ProductId getProductId();
public abstract int getOrdering();
}

查询是将实体视图应用于查询的问题,最简单的方法是



Product p = EntityViewManager.find(entityManager,Product.class,id);



保存即刷新更改也很容易



entityViewManager.save(entityManager,产品);



Spring Data集成使您可以像使用Spring Data Projections一样使用它: https://persistence.blazebit.com/documentation/entity-view/manual/ zh_CN / index.html#spring-data-features 并进行刷新更改,您可以在存储库中定义一个接受可更新实体视图的 save 方法 p>

In a DDD-project I'm contributing to, we're seeking for some convenient solutions to map entity objects to domain objects and visa versa.

Developers of this project agreed to fully decouple domain model from data model. The data layer uses JPA (Hibernate) as persistence technology.

As we all reckon that persistence is an implementation detail in DDD, from a developers' point of view we're all seeking for the most appropriate solution in every aspect of the application.

The biggest concern we're having is when an aggregate, containing a list of entities, is mapped to a JPA entity that in it's turn contains a one-to-many relationship.

Take a look at the example below:

Domain model

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
}

Data model

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
}

Repository

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...?
    }
}

When a ProductJpaEntity already exists in DB, we need to update everything. In case of an update, ProductJpaEntity is already available in Hibernate PersistenceContext. However, we need to figure out which ProductBacklogItems are changed.

More specifically:

  • ProductBacklogItem could have been added to the Collection
  • ProductBacklogItem could have been removed from the Collection

Each ProductBacklogItemJpaEntity has a Primary Key pointing to the ProductJpaEntity. It seems that the only way to detect new or removed ProductBacklogItems is to match them by Primary Key. However, primary keys don't belong in the domain model...

There's also the possibility to first remove all ProductBacklogItemJpaEntity instances (which are present in DB) of a ProductJpaEntity, flush to DB, create new ProductBacklogItemJpaEntity instances and save them to DB. This would be a bad solution. Every save of a Product would lead to several delete and insert statements in DB.

Which solution exists to solve this problem without making too many sacrifices on Domain & Data model?

解决方案

This is a perfect use case for Blaze-Persistence Entity Views.

I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.

Entity views can also be updatable and/or creatable i.e. support flushing changes back, which can be used as a basis for a DDD design. Updatable entity views implement dirty state tracking. You can introspect the actual changes or flush changed values.

You can define your updatable entity views as abstract classes to hide "implementation specifics" like e.g. the primary key behind the protected modifier like this:

@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();
}

Querying is a matter of applying the entity view to a query, the simplest being just a query by id.

Product p = entityViewManager.find(entityManager, Product.class, id);

Saving i.e. flushing changes is easy as well

entityViewManager.save(entityManager, product);

The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features and for flushing changes, you can define a save method in your repository that accepts the updatable entity view

这篇关于聚合到JPA实体的映射的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆