2009-10-23 136 views
14

我在寻找适用于我的应用程序模型层的编程/设计模式,并且想知道哪种模型最适合您正在执行涉及跨多个表的连接的检索的情况。用于数据库表连接的最佳设计模式

例如,假设你有如下表/关系:客户 - > 1..1账户 - > 0..n的特点

其中一个功能可能是一个支票本,或者一些高端产品比如免费旅行保险。

然后我想要getCustomersForFeature()检索所有拥有免费旅行保险账户的客户。

使用ActiveRecord或数据访问对象似乎不合适,因为它们通常集中在每个表的一个类上;同样适用于Data Mapper。我意识到我可以将它分解成每个表的操作,例如getAccountsForFeature()和getCustomersForAccount(),但我想在一次打击中进行检索。

如果我们要“弯曲”每表一个类的模式并使用数据访问对象模式,比如说,getCustomersForFeature()方法应该在CustomerDAO还是FeatureDAO上?但是这对我来说并不合适,因为你会用其他表格的知识来污染你的DAO。

建议请。

+0

我已经为此问题添加了社区Wiki答案,该答案基于这里的答案和其他阅读材料总结了我认为的可用方法。这并不影响其他答案,但是这是我所期待的面向模式的方法,包含了总体思想。我犹豫选择它作为“接受的答案”(我可以这样做我自己的答案?),直到它受到更多的审查。 – 2009-11-04 18:14:06

回答

1

在C#/ Linq to SQL中,它将如下所示。 (我假设实际上有一个要素类型的查找表,以便您有一个标准的要素类型列表,然后他们与一个账户的关系是分开的,所以FeatureTypeId将是您的FK值,也许从下拉列表中选择名单什么的。)

// or whatever data type your keys are in 
public IEnumerable<Customer> getCustomersForFeature(int featureTypeId) 
{ 
    return from feature in dbContext.Features 
      where feature.FeatureTypeId == featureTypeId 
      select getCustomer(feature.Account.Customer.Id); 
} 

在一定程度上你的DAO /包必须要知道你的对象之间的关系,所以实际上这是一个祖父母关系不应该是太吓人了。

至于它在哪里它属于你的BAO结构,可能无论哪种方式都可能有争论。我可能会把它放在客户身上,因为那最终是我试图去的地方。

编辑:托比指出,关系是双向的;再次Linq中,你可以走另外一条路,以及:

// or whatever data type your keys are in 
public IEnumerable<Customer> getCustomersForFeature(int featureTypeId) 
{ 
    return from customer in dbContext.Customers 
      from account in customer.Accounts 
      from feature in account.Features 
      where feature.FeatureTypeId == featureTypeId 
      select getCustomer(customer.Id); 
} 

任何其他的ORM应该有非常相似的行为,即使语法和结构将发生变化。

2

Rails模式中的Active Record模式是通过允许Customer对象拥有have_many帐户 - 这基本上转化为Account对象的集合,而Account对象的集合又具有一组Features。这些关系可以是双向的,因此每个AR模型可以根据您的需求“了解”它们之间的关系。

我认为对象具有其他表的知识是很好的,因为这样的关系对于OO和RDBMS/SQL都很重要。

+0

另外,您可以使用has_many:users,:through =>帐户定义功能。 – 2009-10-23 12:22:48

+0

您能否详细说明如何在封面下完成映射,例如检索客户是否会自动检索关联的帐户和功能(即跨3个表的连接)还是这些惰性加载?如果我想检索Customer */Account/Feature的一些*属性(即子集),但不检索整个对象图,我该怎么做? – 2009-10-26 14:45:06

+0

Rails中的Active Record非常复杂。默认行为是集合将被延迟加载,这可能意味着额外的查询。但是,通过在初始查找中使用“包含”机制可以加载数据(因此,当您加载客户时,您告诉它加载账户,AR会为您创建加入)。特定的字段也可以加载 - 您只需告诉它要加载哪些字段。 – 2009-10-27 00:15:41

0

通常,根据您正在使用的SQL数据库的功能对数据进行建模。忽略ORM,映射器等

一旦你有一个好的数据模型,如果ActiveRecord或DAO不能以你想要的方式访问它,那么通过编写SQL就可以绕过它们。

对象关系映射应该可以使数据库编程变得更容易,但是因为或者模型之间的差异,有时候简单的方法是直接使用SQL。

+0

我很担心忽略ORM等,因为我想在尽可能少的地方(最好是一个)封装如何映射到/来自数据库的知识。噩梦场景正在改变数据库结构,然后不得不查找所有受影响的查询。你对ActiveRecord/DAO施加什么限制(如果有的话),例如每桌一个班级? – 2009-10-26 14:51:20

+1

只需使用纯SQL脚本,就可以在没有任何ORM帮助的情况下进行数据库迁移。这通常在具有训练有素的DBA的大型数据库安装中完成。如果您要使用SQL数据库,则必须中途兼顾DBA。有时候,采取他们的做法。对于应用程序特定数据库中的简单数据模型,ORM是可以的,但许多应用程序不拥有数据库,它们只是几个用户之一。 – 2009-10-26 19:26:37

0

同意GalacticCowboy ORM允许将数据库类映射细节与面向对象查询分开。只是想为Java ORM休眠提供了一个例子 - 有很多方法来定义association mappings和数量以下的面向对象的HQL查询上面所有的人都工作正常

从客户C左边选择C加入c.accounts左连接a.features f其中f.name = '酷'

注意'客户'是一个类的名字在这里和'c.accounts' 'a.features'“F。名称'是类级别的属性名称,即没有提到数据库特定的细节。

8

Model-Driven Design中,您的逻辑实体实体位于您的应用程序中,并且这些实体可以映射到物理数据库中的多个表。当然,您需要运行更复杂的SQL来获取完整的实体,而Model类则是实现这些关系的地方。

我认为ActiveRecord和他们的行为可以用于对单个表进行简单查询,但试图强制使用这些模式来处理复杂查询太困难。幸运的是,我们已经有了一个简洁的,特定于域的语言,您可以使用它来指定复杂的查询:SQL。

因此,在您的Model类中,您将拥有执行逻辑应用程序级任务的方法,例如getCustomersForFeature()。在该方法的代码中,您应该编写一个特定的查询,或者使用ActiveRecord方法,或者根据需要直接使用SQL。因此,您的数据库的具体设计被封装在Model类的一个地方。

这意味着我们需要打破模型和表格之间的耦合。 Model和ActiveRecord类之间的OO关系不是IS-A - 它是HAS-A(或has-many)。


重新评论:那么我们的模型是什么?如果您的应用程序主要需要作为实体与客户合作,并将功能视为或多或少的客户属性,那么您的模型就是客户,它会隐藏功能存储在数据库的单独表中的事实。客户模型将内部使用ActiveRecord或普通SQL来收集所需的数据,以提供客户的复杂对象及其关联的多值属性的完整视图。

但是,如果您的应用程序也需要直接使用功能呢?例如,管理员的屏幕,您可以根据功能获取报告或创建新功能。那么通过Customer模型访问功能将会很笨拙。所以你毕竟需要一个Features模型。只有它会有不同的方法来实现您需要使用功能的操作。

每个模型类都应该公开一个你需要做的事情的API 与那个模型。甚至不需要对称。仅仅因为您的客户模型可以获取具有给定功能的所有客户,并不一定意味着您的功能模型需要获取给定客户的所有功能。遵循YAGNI规则。

但是,在创建了您的Customer模型和您的Features模型之后,这是否会导致知道表之间关系的逻辑的重复?是的,它可以。这是object-relational impedance mismatch范围内的许多问题之一。

+0

有道理。所以在我给出的例子中,你会建议只创建一个CustomerModel吗? – 2009-10-30 13:37:07

0

我认为这是一个经典的问题(至少对我而言),您想在应用程序中表示您的数据模型。根据我自己的经验,总会有一些像你所描述的那样的交易。 我的解决方案与上面的一些答案非常相似。做一个简单的类与你的表一对一,如果该表与另一个表有关系,然后作为你的类中的属性。

关于你说的'我想在一击中做检索。',我认为你应该编写自定义SQL查询或者使用LINQ。

0

尽管对于实际的东西非常适合CRUD类型的表维护,但当涉及到复杂的查询表时,我发现没有什么能够击败实际的SQL。

ORM模型仅仅是一个抽象而已。

+0

是的,没有。是的,因为在某些情况下,无法使用ORM API来表示查询,而且没有,因为如果您拥有强大的ORM(Hibernate/NHibernate)并且知道其API足够好,这些情况很少。如果仅将它用于最简单的CRUD操作,我认为您会忽略ORM提供的大部分优点。 – Max 2009-10-29 08:05:08

0

要求oten为您决定。无论是根据您的截止日期,您的工作文化或您的项目要求。您的截止日期可能表明,“代码到工具”并削减功能。您的员工可能会要求“没有新库或失败模式”。在我自己的环境中,你会听到我讲述这个故事:

“...它不会引入新的ORM库,所以那些都没有了。截止日期将表明我不应该开始自学我的工作文化告诉我快速完成它我的架构呻吟序列化整个表,我的性能要求表明我不应该缓存任何这些数据...“

SQL视图可能会提供一种从对象中抽象连接的好方法,并且可以让您更独立于应用程序代码来更改模式。更新视图非常棘手,这取决于您的数据库。如果DB便携性很重要,那可能会给您的设计带来很大的影响。

你可能会发现和谐的混合方法。如果您使用表格网关,则没有理由将其严格聚合到表格的每个应用程序级别视图中。为什么不使用表格网关或活动记录来更新表范围中的项目,以及处理视图导向查询的自定义类?

3

我花了一些时间来阅读这个主题(包括在Bill的答案中引用的Domain Driven Design迷你书)。这本书中有一个总结概念,它最接近地代表了我想要实现的目标;客户封装并控制对账户和功能的访问。如果我们坚持域驱动设计方法,我们可以使用存储库来控制客户的检索,并且在我看来,这是数据库结构的知识将被封装的地方。

我还看过我的企业应用程序体系结构模式副本,虽然看起来ActiveRecord模式旨在将类直接映射到数据库表,但如果您选择不遵循DDD的存储库方法那么数据映射器将适合于复合映射。

谢谢大家的贡献。我已经投票给出了这个结论的答案。由于这可能不是本次讨论的最后一点,我已将此答案标记为社区Wiki。