2016-12-26 101 views
4

如果我們遵循DDD原則,則一個聚合根應該僅具有對另一個聚合根的引用(通過id)。DDD:如何正確實現與JPA/Hibernate實體的關係?

例子:

// Product Aggregate root 
class Product { 

    // References to categories Aggregate Roots (to ids) 
    Set<Long> categoryIds; 
} 

但如何才能將其與JPA實現/休眠? 在JPA中,如果我們希望有,例如,一對多的關係,我們把它定義爲如下:

// Product Aggregate root 
class Product { 

    // Holds category aggregate roots 
    @OneToMany(mappedBy = "", cascade = CascadeType.ALL) 
    Set<Category> categories; 
} 

所以JPA-S方式將持有的類別總根本身,這是不是在DDD建議。

您如何設計與JPA的關係,但是要符合DDD原則?

P.S .:我打算讓categories字符串類型的屬性和逗號分隔的類別ID列表,但有沒有更好的解決方案?

+0

'級聯= CascadeType.ALL'正在朝着錯誤的方向前進,因爲將兩個總計關聯到同一個事務 –

回答

1

你可以使用一個連接表,以避免分類彙總這樣的根源:

@Entity 
public class Product { 

    @Id 
    @GeneratedValue 
    private int id; 

    @OneToMany 
    @JoinTable 
    private Set<Category> categories; 

    // constructor, getters, setters, etc... 
} 


@Entity 
public class Category { 
    @Id 
    @GeneratedValue 
    private int id; 

    // constructor, getters, setters, etc... 
} 

只是作爲一個例子,我會插在一起的幾個:

for (int n = 0; n < 3; ++n) { 
    categoryRepository.save(new Category()); 
} 

Set<Category> categories = categoryRepository.findAll(); 

productRepository.save(new Product(categories)); 

,這導致以下(你沒有指定你的DBMS,所以我只是假設...)MySQL:

MariaDB [so41336455]> show tables; 
+----------------------+ 
| Tables_in_so41336455 | 
+----------------------+ 
| category    | 
| product    | 
| product_categories | 
+----------------------+ 
3 rows in set (0.00 sec) 

MariaDB [so41336455]> describe category; describe product; describe product_categories; 
+-------+---------+------+-----+---------+----------------+ 
| Field | Type | Null | Key | Default | Extra   | 
+-------+---------+------+-----+---------+----------------+ 
| id | int(11) | NO | PRI | NULL | auto_increment | 
+-------+---------+------+-----+---------+----------------+ 
1 row in set (0.00 sec) 

+-------+---------+------+-----+---------+----------------+ 
| Field | Type | Null | Key | Default | Extra   | 
+-------+---------+------+-----+---------+----------------+ 
| id | int(11) | NO | PRI | NULL | auto_increment | 
+-------+---------+------+-----+---------+----------------+ 
1 row in set (0.00 sec) 

+---------------+---------+------+-----+---------+-------+ 
| Field   | Type | Null | Key | Default | Extra | 
+---------------+---------+------+-----+---------+-------+ 
| product_id | int(11) | NO | PRI | NULL |  | 
| categories_id | int(11) | NO | PRI | NULL |  | 
+---------------+---------+------+-----+---------+-------+ 
2 rows in set (0.00 sec) 

和cour瑟毫不奇怪相對於它們的內容:

MariaDB [so41336455]> select * from category; select * from product; select * from product_categories; 
+----+ 
| id | 
+----+ 
| 1 | 
| 2 | 
| 3 | 
+----+ 
3 rows in set (0.00 sec) 

+----+ 
| id | 
+----+ 
| 1 | 
+----+ 
1 row in set (0.00 sec) 

+------------+---------------+ 
| product_id | categories_id | 
+------------+---------------+ 
|   1 |    1 | 
|   1 |    2 | 
|   1 |    3 | 
+------------+---------------+ 
3 rows in set (0.00 sec) 

另外我想避免當你使用關係數據庫存儲在一個逗號分隔的列表關係。它導致不健康的數據庫設計,並會導致您在某個時候頭痛。

+0

但是'Product'保存了一組'Category'。根據DDD,這是我們應該首先避免的。 –

+0

是的,在這種情況下,「類別」應該只是CategoryId – Teimuraz

0

在聚合之間進行導航時,最好堅持使用標識引用。在調用集合行爲之前,使用服務加載所需的對象。例如:

public class MyProductApplicationService { 
    ... 
    @Transactional 
    public void loadDependentDataAndCarryOutAggregateAction(Long productId, Long categoryId) { 

     Product product = productRepository.findOne(productId); 
     Category category = categoryRepository.findOne(categoryId); 

     product.doActionThatNeedsFullCategoryAndMayModifyProduct(category); 
    } 
} 

如果是太麻煩了,那麼至少不要跨越事務從一個聚合到另一個:

class Product { 

    @OneToMany(mappedBy = "product") 
    Set<Category> categories; 
} 

public class Category { 

    @ManyToOne 
    @JoinColumn(name = "productid", insertable = false, updatable = false) 
    private Product product; 
} 
+0

謝謝,但在第一種方法中,您將在哪裏存儲產品的類別參考? – Teimuraz

+0

@moreo只需將'@Column private Long productId'添加到'Category' –