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


77

我很难理解存储库模式。

关于该主题有很多意见,例如在Repository模式中做得正确,但其他信息,例如Repository是新的Singleton或再次出现,例如在不要使用DAO中使用Repository或只是以某种方式使用Spring JPA Data + Hibernate + MySQL + MAVEN存储库似乎与DAO对象相同。

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

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

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

Service Layer需要*DTO的对象,并将这些的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从客户端获得对象的部分。

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

让我困扰的是,如果您ToolSet从上面举我的例子,我将必须执行以下步骤:

  1. 采取toolSetDto并检查是否null
  2. 对于每个tool*Dto拥有者toolSetDto
    a)如果具有有效的id,则从转换为DTOEntity否则创建一个新的数据库条目
    b)toolDescriptionDto并将其转换/保存到数据库或创建一个新条目
  3. 在检查ToolSet完上述实例(实体)并将其设置为持久保存在数据库中之后

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

我当时在想创建一个,ToolSetRepository但是这里的问题是

  • 它采用ToolSet实体对象还是使用DTO对象?
  • 无论如何:*Repository允许使用其他存储库对象吗?就像当我要救ToolSet,但我必须存储ToolToolDescription第一-我会用ToolRepositoryToolDescriptionRepository里面ToolSetRepository
    如果是这样:为什么它不破坏存储库模式?如果此模式基本上是服务和我的ORM框架之间的层,则*Repository由于依赖关系的原因,它只是“感觉不到”将依赖关系添加到其他类。

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

所以..我知道这是一个很长的问题,但是我已经花了几天的研究时间。我现在正在使用的现有代码已经变得一团糟,因为我看不到这种模式。

我希望有人能给我比大多数文章和教程更大的印象,而这些文章和教程只能实现一个非常非常简单的存储库模式示例。


在我看来,ToolSetRepository应该只知道ToolSet实体...,并且在ToolSet上还可以具有JaxB批注,以将该实体用作DTO。在客户端,您只有从Web服务URL接收到的wsdl中的jaxws clientgen生成的jaxb类加上服务器端的“?wsdl” ....然后,您会收到“ unmanaged”实体。那么您必须使用entitymanager.merge使其进入托管状态。就这样。在我看来,只有在无法使用命名查询的复杂条件下才需要特定的存储库。例如条件api查询。
StefanHeimberg

@StefanHeimberg但是ToolSetRepository,例如,如何处理Tooland的持久性ToolDescription呢?这些应该已经坚持了吗?如果这些应该已经坚持到现在,那我该怎么办?在我的服务方法中执行此操作感觉不正确,因为诸如此类的复杂实体ToolSet会使服务方法代码膨胀。恕我直言,服务方法只应执行一些初始化和基本检查工作,然后将工作委托给下一层。
Stefan Falk 2015年

如果您在服务层(事务边界)中接收到“非托管”实体,然后在实体管理器上使用merge(),则该实体已成为实体管理人员。服务方法完成后,将提交事务,并将实体管理器中的更改持久保存到数据库中……
StefanHeimberg,2015年

1
AFAIK Hibernate(和JPA)是一个完整的DAO层,因为其工作是连接到数据源(在这种情况下为数据库),尽管有基础细节(MySQL,Oracle,SQL Server等),并且您可以通过多种方式查询数据源。如果您希望/需要对实体使用特定的查询,它允许您使用在存储库中指定要使用的条件,因此最终,Hibernate既是Dao又是存储库。在此基础上,您将要做的是创建自己的层来抽象此dao(或存储库)或用于实现此目标并继续编程的任何内容。
Luiggi Mendoza

2
merge()已经检查是否新。然后创建一个插入或更新查询。我认为这是基础ORM的责任。例如JPA。
StefanHeimberg,2015年

Answers:


113

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

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

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

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

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

一个业务对象不是一个ORM实体,它可能是从技术角度来看的,但是从设计角度来看,一个模型可以对业务对象进行建模,而其他模型可以对持久性模型进行建模。在许多情况下,这些不是直接兼容的。

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


1
嗨!现在,对我来说,什么是存储库更清楚了,但是如果可以的话,我想问一下我的特定代码示例:问题是客户端上没有太多事情要做,所以再也不会有太多事情要做了。比基本上将例如存储ToolSet在数据库中做。但这仍然并不意味着我可以编写ToolSetRepository基本上建立我的实体类然后将其持久化的代码,对吗?我在问题中遇到的最后一个大问题是“无论如何:是否*Repository允许使用其他存储库对象(在其中)? ”。
Stefan Falk

1
我所见的任何教程都没有显示一个更复杂的示例,其中存储库实际上必须执行人们声称的功能。我只看到残酷的简单示例,这使我想知道我将需要什么?^^除非允许使用其他存储库对象中的其他存储库对象以完成工作。:)
Stefan Falk 2015年

4
@StefanFalk存储库模式实际上是接口设计。一旦有了接口,就可以疯狂地进行实际的实现。关于使用其他存储库的存储库,我认为不是,这是错误的设计,这意味着您有2个处理相同对象的存储库。但是存储库可以使用很多DAO。在我的代码库中,存储库彼此不认识。事实是,您需要具有正确的业务模型(具有清晰的一致性边界)才能正确使用存储库,否则就很麻烦了。
MikeSW,2015年

3
顺便说一句,存储库的目的是存储/检索东西。东西越复杂,就越好。但是请记住重要的一点:该对象必须代表简单(一个结构)或复杂(很多规则或许多子项)的业务概念
MikeSW,2015年

1
没有!存储库只是一个持久性问题。切勿在其中放入诸如http之类的内容。您违反了关注分离原则。您真的想拥有一个可以完成所有工作的对象吗?回购实现始终是持久性的一部分(作为一个概念),您的应用程序应仅通过注入使用抽象,即di容器应创建回购,仅应从db保存/加载,而不检查安全性。如果它们是查询的一部分,则可以“检查”事物,例如“从id = @ 0和userId = @ 1的工具中选择*”。
MikeSW
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.