【问题标题】:Is a DAO Only Meant to Access Databases?DAO 是否仅用于访问数据库?
【发布时间】:2011-12-25 00:49:52
【问题描述】:

我一直在复习我的设计模式,并想到我无法在任何地方找到一个好的答案。所以也许有更多经验的人可以帮助我。

DAO 模式是否仅用于访问数据库中的数据?

我找到的大多数答案都是肯定的;事实上,大多数关于 DAO 模式的讨论或写作都倾向于自动假设您正在使用某种数据库。

我不同意。我可以有如下的 DAO:

public interface CountryData {
    public List<Country> getByCriteria(Criteria criteria);
}

public final class SQLCountryData implements CountryData {
    public List<Country> getByCriteria(Criteria criteria) {
        // Get From SQL Database.
    }
}

public final class GraphCountryData implements CountryData {
    public List<Country> getByCriteria(Criteria criteria) {
        // Get From an Injected In-Memory Graph Data Structure.
    }
}

在这里,我有一个 DAO 接口和 2 个实现,一个与 SQL 数据库一起使用,一个与内存中的图形数据结构一起使用。它是否正确?或者图形实现是要在其他类型的层中创建的?

如果它是正确的,那么抽象每个 DAO 实现所需的具体实现细节的最佳方法是什么?

例如,以上面引用的 Criteria Class I 为例。假设是这样的:

public final class Criteria {
    private String countryName;

    public String getCountryName() {
        return this.countryName;
    }

    public void setCountryName(String countryName) {
        this.countryName = countryName;
    }
}

对于 SQLCountryData,它需要以某种方式将 countryName 属性映射到 SQL 标识符,以便生成正确的 SQL。对于 GraphCountryData,可能需要创建某种针对 countryName 属性的谓词对象,以从图中过滤掉失败的顶点。

在不将针对抽象 CountryData 的客户端代码与此类实现特定细节耦合的情况下,抽象此类细节的最佳方法是什么?

有什么想法吗?

编辑:

我包含的 Criteria 类的示例很简单,但考虑一下我是否要允许客户端构造复杂的条件,它们不仅应该指定要过滤的属性,还应该指定相等运算符、逻辑运算符复合标准和价值。

【问题讨论】:

  • 一方面,我不要将 DAO 仅用于数据库访问。在我的设计中,我经常有一个从某个来源读取数据的 DAO,就像名字所说的那样。有时,它是一个数据库,有时它可能是一个文件、一个特定的流、一个加密设备、硬件随机数生成器,等等。只要它为我提供数据,而且我不在乎数据来自哪里,我很乐意遵循 DAO 模式。这也使得以后重构代码变得容易,原来的 SQL 数据库可能会变成其他一些存储机制,除了 DAO 实现之外,对我的应用程序没有任何影响。

标签: java design-patterns dao


【解决方案1】:

DAO 是 DAL(数据访问层)的一部分,您可以拥有由任何类型的实现(XML、RDBMS 等)支持的数据。您只需要确保在运行时注入/使用项目实例。在这种情况下,像 Spring/Guice 这样的 DI 框架大放异彩。此外,您的Criteria 接口/实现应该足够通用,以便仅捕获业务详细信息(即国家名称标准),并且实际映射再次由实现类处理。

对于 SQL,在您的情况下,您可以手动生成 SQL,使用像 Spring 这样的帮助库生成它,或者使用像 MyBatis 这样的完整框架。在我们的项目中,使用 Spring XML 配置文件来解耦客户端和实现;您的情况可能会有所不同。

编辑:我看到您在上一个问题中提出了类似的问题。答案还是一样。您可以在界面中添加尽可能多的灵活性;您只需要确保实现足够智能,可以理解它接收到的所有参数并将它们适当地映射到底层源。在我们的例子中,我们从业务层获取值对象,并将其转换为 SQL 实现层中的一个映射,供 MyBatis 使用。同样,这个过程非常透明,服务层与 DAO 通信的唯一方法是通过接口定义的值对象。

【讨论】:

  • 小心思,例如,如果您希望客户指定要订购的属性怎么办。 Criteria 对象将如何适应这种情况,以便您可以将其翻译为要传递给 MyBatis 的地图?抱歉,如果这似乎是一个类似的问题,那么我的最后一个问题;我承认他们有点相似
  • 我会传递标准(例如国家/地区名称)和排序值(具有字段名称和排序类型的对象 ASCDESC)。 MyBatis 是 capable of creating dynamic queries 基于传入的“值对象”或“映射”,它应该负责创建的查询。
  • 我假设您的意思是您将传递数据库中的字段名称?或者你的意思是你想在你的 SQL 映射中排序的值模型上的“属性”名称,你会将它转换为适当的数据库字段名称?如果是后者,如果模型中有很多“属性”,会不会有大量的条件逻辑?
  • 我的意思是可以在动态查询中使用的数据库列名以及方向(ASCDESC)。在您的 SQL DAO 实现中,您将读取这些值并创建一个类似的映射; map.put("sortColumn", ordering.column); map.put("sortOrder", ordering.order); map.put("countryName", myCountryName);。然后这个映射将被传递给 MyBatis,MyBatis XML 将在创建查询时使用映射“键”是属性。
  • 对,但这正是我的观点。如果客户端代码正在使用 DAO 的注入实例,则它是针对接口而不是具体实现的。所以从本质上讲,它不知道它正在反对什么样的实现。它可以是我的示例中的 SQL 或 Graph。因此,如果您在调用者调用 DAO 方法时强制调用者使用数据库字段名称设置订单条件,则存在隐式耦合,并且接口的整个想法是无用的。明白我在说什么了吗?
【解决方案2】:

不,我不相信它与数据库相关联。首字母缩略词是Data Access Object,而不是“数据库访问对象”,因此它可以用于任何类型的数据源。

它的重点是将应用程序与后备数据存储区分开,以便可以随意修改存储区,前提是它仍然遵循相同的规则。

这不仅仅意味着使用 Oracle 并引入 DB2。这也可能意味着切换到完全不基于 DBMS 的解决方案。

【讨论】:

    【解决方案3】:

    好的,这是一个有点哲学的问题,所以我会告诉你我在想什么。 DAO 通常代表数据访问对象。这里的数据源并不总是数据库,尽管在现实世界中,实现通常是这样的。 它可以是 XML、文本文件、一些远程系统,或者像你所说的内存中的对象图。

    从我在实际项目中看到的情况来看,是的,你说得对,你应该提供不同的 DAO 实现来以不同的方式访问数据。 在这种情况下,一个 dao 进入 DB,另一个 dao 实现进入对象图。

    DAO 的界面必须非常仔细地设计。您的“标准”必须足够通用,以封装您从中获取数据的方式。 如何达到这种程度的解耦?答案可能因您的系统而异,一般来说,我会说,答案将是“像往常一样,通过添加另一个间接级别”:)

    您还可以将您的条件对象视为一个数据对象,您只提供查询所需的数据。在这种情况下,您甚至不需要支持不同的 Criteria。 DAO 的每个特定实现都会获取这些数据并以自己不同的方式处理它:一个将为图构造查询,另一个将其绑定到您的 SQL。

    为了尽量减少维护麻烦,我建议您使用依赖管理框架(例如 Spring)。通常这些框架非常适合实例化您的 DAO 对象并很好地协同工作。 祝你好运!

    【讨论】:

    • 有趣。您介意对“另一个间接级别”的含义进行一个小的解释吗?我正在尝试像您说的那样设计一个 Criteria 对象的方法,其中客户端代码可以指定要过滤的属性,它的运算符(=,>等)和以通用解耦方式的值,并具有每个 DAO实现可以随意干预它。但我似乎无法弄清楚。编辑:感谢您提供非常详细的答案。
    【解决方案4】:

    不,仅用于数据库的 DAO 是一个常见的误解。

    DAO 是“数据访问对象”,而不是“数据库访问对象”。因此,您可以在任何需要 CRUD 数据传入/传出的地方(例如文件、内存、数据库等),都可以使用 DAO。

    在领域驱动设计中有一个存储库模式。虽然Repository 作为一个词远远优于三个随机字母(DAO),但概念是相同的。

    DAO/Repository 模式的目的是抽象一个支持数据存储,它可以是任何可以保存状态的东西。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-07
      • 2017-03-21
      • 1970-01-01
      • 2012-11-12
      • 2014-11-12
      相关资源
      最近更新 更多