存储库模式 - 如何理解它以及它如何与“复杂”实体一起使用?

Posted

技术标签:

【中文标题】存储库模式 - 如何理解它以及它如何与“复杂”实体一起使用?【英文标题】:Repository Pattern - How to understand it and how does it work with "complex" entities? 【发布时间】:2015-09-27 02:28:43 【问题描述】:

我很难理解存储库模式。

关于这个话题有很多意见,比如Repository pattern done right,还有其他的东西,比如Repository is the new Singleton,或者Don't use DAO use Repository,或者只是Spring JPA Data + Hibernate + mysql + MAVEN,在某种程度上,存储库似乎与DAO相同对象。

我已经厌倦了阅读这些东西,因为恕我直言,这不是一件难事,因为它在很多文章中都有展示。

我是这样看的:看来我想要的是这样的:

         ------------------------------------------------------------------------
         |                            Server                                    |
         ------------------------------------------------------------------------
         |                    |                        |                        |
Client <-|-> Service Layer  <-|->  Repository Layer  <-|-> ORM / Database Layer |
         |                    |                        |                        |  
         ------------------------------------------------------------------------

Service Layer 获取 *DTOobjects 并将它们传递给 Repository Layer,这基本上就是知道如何可以存储实体的“人”。

例如假设你有一些工具的组合(请注意这只是伪代码

@Entity
class ToolSet 
  @Id
  public Long id;
  @OneToOne
  public Tool tool1;
  @OneToOne
  public Tool tool2;


@Entity
class Tool 
  @Id
  public Long id;
  @OneToMany
  public ToolDescription toolDescription;


@Entity
class ToolDescription 
  @Id
  public Long id;
  @NotNull
  @OneToOne
  public Language language

  public String name;
  public String details;

我没有得到的是我从客户端获取ToolSetDTO 对象的部分。

据我目前的理解,我可以使用“知道如何存储ToolSetDTO 的方法ToolSetRepository.save(ToolSetDTO toolSetDto) 编写ToolSetRepository。但是几乎每个教程都没有通过*DTO,而是通过Entity

这里困扰我的是,如果你从上面拿我的ToolSet 示例,我必须执行以下步骤:

    toolSetDto 并检查是否不是null 对于toolSetDto 拥有的每个tool*Dto a) 如果具有有效的ID,则从DTO 转换为Entity,否则创建一个新的数据库条目 b) toolDescriptionDto 并转换/将其保存到数据库或创建一个新条目 在检查了上述情况后,实例化 ToolSet(实体)并将其设置为持久保存在数据库中

所有这些都太复杂了,不能简单地让服务功能(客户端接口)来处理。

我在想的是创建例如ToolSetRepository 但这里的问题是

是使用ToolSet 实体对象还是使用DTO 对象? 无论如何:*Repository 是否允许使用 其他存储库对象?就像我想保存ToolSet 但我必须先存储ToolToolDescription - 我会在ToolSetRepository 中使用ToolRepositoryToolDescriptionRepository 吗? 如果是这样:为什么它不破坏存储库模式?如果此模式基本上是服务和我的 ORM 框架之间的一层,那么由于依赖原因,它只是不“感觉正确”向其他 *Repository 类添加依赖项。

我不知道为什么我无法理解这一点。这听起来并不复杂,但仍然有像Spring Data 这样的帮助。另一件事困扰着我,因为我真的不明白这如何让任何事情变得更容易。特别是因为我已经在使用 Hibernate - 我没有看到好处(但也许这是另一个问题)。

所以.. 我知道这是一个很长的问题,但我已经对它进行了几天的研究。我现在正在处理的现有代码开始变得一团糟,因为我无法看穿这种模式。

我希望有人能给我一个比大多数文章和教程更大的图景,这些文章和教程并没有超出实现一个非常非常简单的存储库模式示例。

【问题讨论】:

在我看来,ToolSetRepository 应该只知道 ToolSet 实体......并且在 ToolSet 上,您还可以拥有 JaxB 注释,以将实体用作 DTO。在客户端,您只有使用 jaxws clientgen 生成的 jaxb 类,这些类来自从 web 服务 url 接收的 wsdl 加上“?wsdl”......在服务器端,您会收到“非托管”实体。那么您必须使用 entitymanager.merge 将其置于托管状态。就这样。在我看来,只有在无法使用命名查询的复杂条件下才需要特定的存储库。例如标准 api 查询。 @StefanHeimberg 但是ToolSetRepository 将如何处理ToolToolDescription 的持久性?那些应该已经被坚持了?如果此时应该已经坚持这些,那么我会在哪里做呢?在我的服务方法中这样做感觉不对,因为像ToolSet 这样的复杂实体会使服务方法代码膨胀。恕我直言,服务方法应该只做一些初始化和基本检查,然后将工作委托给下一层。 如果您在服务层(事务边界)中收到“非托管”实体,然后在实体管理器上使用 merge(),则该实体已被实体管理器知道。服务方法完成后,事务提交,实体管理器中的更改被持久化到数据库中...... AFAIK Hibernate(和 JPA)是一个完整的 DAO 层,因为它的工作是连接到数据源(在本例中为数据库),尽管有底层细节(MySQL、Oracle、SQL Server 等)和您可以通过多种方式查询数据源。如果您想要/需要对您的实体使用特定查询,它允许您使用在 Repository 中指定使用的条件,因此最终 Hibernate 既是 Dao 又是 Repository。你将在它之上做的是创建你自己的层来抽象这个 dao(或存储库)或任何你用来实现它并继续编程的东西。 merge() 已经检查是否是新的。然后创建插入或更新查询。在我看来,这是底层 ORM 的责任。例如JPA。 【参考方案1】:

您可以阅读我的“傻瓜仓库”post 了解仓库的简单原理。我认为您的问题是您正在使用 DTO,在这种情况下,您并没有真正使用存储库模式,而是使用的是 DAO。

存储库和 dao 之间的主要区别在于存储库仅返回调用层可以理解的对象。大多数情况下,存储库由业务层使用,因此它返回业务对象。 dao 返回的数据可能是也可能不是整个业务对象,即数据不是有效的业务概念。

如果您的业务对象只是数据结构,则可能暗示您有建模问题,即糟糕的设计。对于“丰富”或至少正确封装的对象,存储库更有意义。如果您只是加载/保存数据结构,可能不需要存储库,orm 就足够了。

如果您正在处理由其他对象(聚合)组成的业务对象,并且该对象需要其所有部分才能一致(聚合根),那么存储库模式是最好的解决方案,因为它将抽象所有持久性细节。您的应用将只请求一个“产品”,并且存储库会将其作为一个整体返回,而不管需要多少表或查询来恢复对象。

根据您的代码示例,您没有“真正的”业务对象。你有 Hibernate 使用的数据结构。业务对象是根据业务概念和用例设计的。存储库使 BL 可以不关心该对象的持久化方式。在某种程度上,存储库充当对象和将被持久化的模型之间的“转换器/映射器”。基本上,repo 将对象“减少”为持久性数据所需的对象。

业务对象不是 ORM 实体。可能从技术角度来看,但从设计角度来看,一个模型业务填充另一个模型持久性东西。在许多情况下,它们并不直接兼容。

最大的错误是根据存储需求和思维方式设计业务对象。与许多开发人员所相信的相反,ORM 的目的不是持久化业务对象。它的目的是在 rdbms 之上模拟一个“oop”数据库​​。 ORM 映射是在您的数据库对象和表之间,而不是在应用程序对象(在处理业务对象时更是如此)和表之间。

【讨论】:

嗨!现在我更清楚什么是存储库,但如果可以的话,我想询问我的具体代码示例:问题是我的客户端没有太多事情发生,所以永远不会有更多比基本上存储例如ToolSet 在数据库中。但这仍然不意味着我可以写ToolSetRepository,它基本上构建了我的实体类然后持久化它,对吗?我在问题中遇到的最后一个大问题是“无论如何:*Repository 是否允许使用其他存储库对象(在其中)?”。 我看到的所有教程都没有显示更复杂的示例,其中存储库实际上必须执行人们声称它的用途。我只看到非常简单的例子,让我想知道我需要这个做什么? ^^ 除了允许在其他 Repository 对象中使用其他 Repository 对象以完成工作。 :) @StefanFalk 存储库模式实际上是界面设计。一旦你有了接口,你就可以疯狂地使用实际的实现。关于使用其他存储库的存储库,我认为不是,这是错误的设计,这意味着您有 2 个存储库处理相同的对象。但是一个存储库可以使用很多 DAO。在我的代码库中,存储库彼此不了解。问题是,您需要有一个适当的业务模型(具有明确的一致性边界)才能正确使用存储库,否则它只是一个复杂的问题。 顺便说一句,存储库的目的是存储/检索东西。东西越复杂越好。但请记住重要的一点:该对象必须代表一个业务概念,要么是简单的(一个结构),要么是复杂的(许多规则或许多子对象)。 不!存储库只是一个持久性问题。 永远不要在其中放入 http 之类的东西。您违反了关注点分离原则。你真的想要一个可以做所有事情的对象吗? repo 实现始终是持久性的一部分(作为一个概念),您的应用程序应该只使用通过注入的抽象,即 di 容器应该创建应该只从 db 保存/加载的 repo,而不是检查安全性。如果它们是查询的一部分,您可以“检查”它们,例如 'select * from tools where id=@0 and userId=@1' 。

以上是关于存储库模式 - 如何理解它以及它如何与“复杂”实体一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

数据库和聚合根的存储库模式

如何在 ASP MVC 中实现工作单元、存储库和业务逻辑?

使用实体框架、代码优先和 CRUD 操作的存储库模式

存储库模式和聚合根模式和实体框架

如何在 Spring Boot 中实现通用 JPA 存储库 - 它可以自动装配到任何实体/类类型的 Spring 服务中

DAL 中的实体框架存储库模式,如何实现更新功能?