【问题标题】:DAO vs ORM(hibernate) pattern [closed]DAO vs ORM(休眠)模式[关闭]
【发布时间】:2011-05-01 12:53:08
【问题描述】:

我在一些文章中读到 DAO 对 hibernate 不是强制性的,它的实现是“取决于”的,换句话说,我们可以在 ORM 和 DAO 模式之间进行选择。

好的,假设我不想使用 DAO 模式,所以我只使用 hibernate(我的 ORM)提供的会话 CRUD 和查询操作。

特别是对于“搜索”和“查找”查询,总是重写它们是不正确的,因此将它们放入一个类中是合理的。

但是这个类是一个简单的DAO,没有DAO模式和DAOFactory的所有实现,只是一个DAO的轻量级实现。 所以,关键是我们总是需要一个 DAO,而选择是重型 DAO 实现还是轻量级 DAO 实现?

我说错了吗?

编辑 我遇到的另一个问题是在哪里放置 dao 交互,例如我必须登录一个用户并编写登录日志(我知道的无用示例......)

所以在 DAO 模式中,我拥有所有通用的 dao 实现、DAOFactory,最后是 UserHibernateDAO 和 LogHibernateDAO。 登录操作是一个业务方法

private void login(String username, String password){
    daoFactory.beginTransaction();
    UserDAO userDao=daoFactory.HIBERNATE.getUserDao();
    LogDAO logDao=daoFactory.HIBERNATE.getLogDao();
    if(userDao.checkAccount(username, password){
        User user=userDao.findByAccount(username, password);
        logDao.save(new Log("log-in", user);
    }
    daoFactory.commit();
}

这合理吗?我可以这样使用 dao 吗? 如果我想处理异常,最好的地方是在业务逻辑中?

EDIT2 让我们假设使用 DAO 模式,这样做的主要原因是能够在技术(ORM->JDBC 等)之间切换,一切都很好,但是我在哪里可以处理休眠会话和事务? 我不能把它放到 DAO 中,它是反模式,我不能把它放到服务层中,因为在一个 hipohtetycal 开关中我必须删除所有这些事务(因为其他技术可能不会使用它们)。

【问题讨论】:

标签: java hibernate design-patterns dao


【解决方案1】:

ORM 和 DAO 是正交概念。一个与对象如何映射到数据库表有关,另一个是用于编写访问数据的对象的设计模式。您不会在它们之间进行选择。你可以让 ORM 和 DAO 是同一个应用程序,就像你不需要 ORM 来使用 DAO 模式一样。

也就是说,虽然你从来没有真正需要任何东西,但你应该使用 DAO。该模式适用于模块化代码。您将所有持久性逻辑保存在一个地方(关注点分离,对抗泄漏的抽象)。您允许自己与应用程序的其余部分分开测试数据访问。并且您允许自己测试与数据访问隔离的应用程序的其余部分(即您可以模拟您的 DAO)。

另外,遵循 DAO 模式很容易,即使实现数据访问可能很困难。因此,您只需花费很少(或不花费任何费用),而您会收获很多。

编辑 -- 关于您的示例,您的登录方法应该在某种 AuthenticationService 中。您可以在那里处理异常(在登录方法中)。如果您使用 Spring,它可以为您管理很多事情:(1) 事务,(2) 依赖注入。您不需要编写自己的事务或 dao 工厂,您只需围绕服务方法定义事务边界,并将您的 DAO 实现定义为 bean,然后将它们连接到您的服务中。

EDIT2

使用该模式的主要原因是分离关注点。这意味着您所有的持久性代码都在一个地方。这样做的一个副作用是,可测试性和可维护性,而且这使得以后切换实现变得更容易。如果您正在构建基于 Hibernate 的 DAO,您绝对可以在 DAO 中操作会话,这是您应该做的。反模式是当与持久性相关的代码发生在持久层之外时(泄漏抽象法则)。

交易有点棘手。乍一看,事务似乎是持久性的问题,而且确实如此。但它们不仅仅是对持久性的关注。事务也是您的服务的一个问题,因为您的服务方法应该定义一个“工作单元”,这意味着,服务方法中发生的一切都应该是原子的。如果您使用休眠事务,那么您将不得不在您的 DAO 之外编写休眠事务代码,以围绕使用许多 DAO 方法的服务定义事务边界。

但请注意,事务可以独立于您的实现——无论您是否使用休眠,您都需要事务。另请注意,您不需要使用休眠事务机制——您可以使用基于容器的事务、JTA 事务等。

毫无疑问,如果您不使用 Spring 或类似的东西,事务将会很痛苦。我强烈建议使用 Spring 来管理您的事务,或者我相信您可以使用注释围绕服务定义事务的 EJB 规范。

查看以下链接,了解基于容器的交易。

Container-Managed Transactions

Sessions And Transactions

我从中了解到的是,您可以轻松地在服务级别定义 DAO 之外的事务,而无需编写任何事务代码。

另一个(不太优雅的)替代方案是将所有原子工作单元放在 DAO 中。您可以使用 CRUD DAO 来执行简单的操作,然后使用更复杂的 DAO 来执行多个 CRUD 操作。这样,您的程序化事务将保留在 DAO 中,您的服务将调用更复杂的 DAO,而不必担心事务。

以下链接是 DAO 模式如何帮助您简化代码的一个很好的示例

AO vs ORM(hibernate) pattern

(感谢@daff

注意接口的定义如何使您的业务逻辑只关心 UserDao 的行为。它不关心实现。您可以使用休眠或仅使用 JDBC 编写 DAO。因此,您可以更改数据访问实现而不影响程序的其余部分。

【讨论】:

  • 好的,但如果我选择不使用 DAO 模式,我该如何继续?换句话说,使用休眠和没有 DAO,我将我的实体 CRUD 逻辑和 HQL 查询或条件查询等放在哪里...?
  • @blow,这就是重点。您必须将持久性代码放在某处。您应该将其与应用程序的业务逻辑分开。那么为什么不把它放在 DAO 中呢?换句话说,你要写一个 DAO,问题是,你要遵循这个模式吗?
  • 谢谢你的解释,目前我不想使用spring来管理DAO,首先我想知道DAO模式是如何工作的,如果我真的需要它。 PS。我还有一个小疑问,我已经编辑了第一个问题。
  • @blow -- 我为您的第二次编辑更新了我的答案。其他人,这在我的驾驶室之外,所以绝对欢迎 cmets 如何处理交易....
【解决方案2】:

让我提供一个源代码示例来hvgotcodes很好的答案:

public class Application
{
    private UserDao userDao;

    public Application(UserDao dao)
    {
        // Get the actual implementation
        // e.g. through dependency injection
        this.userDao = dao;
    }

    public void login()
    {
        // No matter from where
        User = userDao.findByUsername("Dummy");
    }
}


public interface UserDao
{
    User findByUsername(String name);
}

public class HibernateUserDao implements UserDao
{
    public User findByUsername(String name)
    {
        // Do some Hibernate specific stuff
        this.session.createQuery...
    }
}

public class SqlUserDao implements UserDao
{
    public User findByUsername(String name)
    {
        String query = "SELECT * FROM users WHERE name = '" + name + "'";
        // Execute SQL query and do mapping to the object
    }
}

public class LdapUserDao implements UserDao
{
    public User findByUsername(String name)
    {
        // Get this from LDAP directory
    }
}

public class NoSqlUserDao implements UserDao
{
    public User findByUsername(String name)
    {
        // Do something with e.g. couchdb
        ViewResults resultAdHoc = db.adhoc("function (doc) { if (doc.name=='" + name + "') { return doc; }}");
        // Map the result document to user
    }
}

所以,如前所述,DAO 是一种将耦合最小化的设计模式 在您的应用程序和您的后端之间,而 ORM 处理如何 将对象映射到对象关系数据库(这减少了 数据库和你的应用程序,但最后,没有使用 DAO 您的应用程序将取决于使用的 ORM 或更高级别 像 JPA 这样的标准)。

因此,如果没有 DAO,很难更改您的应用程序(例如,迁移到 NoSQL 数据库而不是兼容 JPA 的 ORM)。

【讨论】:

  • 谢谢达夫,你的例子很清楚。您说“不使用 DAO,您的应用程序将依赖于使用的 ORM 或更高级别的标准,如 JPA”,好的,但我需要编写一个简单的 DAO,我可以使用其他名称(如 UserService 或 UserManager 或UserSessionfacade,但它是一个简单的 DAO……因此,真正的选择是在具有一些好处的 DAO 的真正实现或封装我所有 CRUD 操作或对某个实体的查询的简单休眠 DAO 之间进行选择。
  • 谢谢。 @blow:如果您使用基于服务的应用程序方法,您可能会将您的服务视为 DAO,如果您不需要另一个抽象级别,那么恕我直言,您不需要介于两者之间的 DAO。在更大的应用程序中,我通常让服务处理会话和事务管理,并将实际工作重定向到 DAO。但这取决于。
  • 谢谢。我目前的困惑是如何在我的 JAX-RS 应用程序中手动控制 DAO 的注入。 Jersey 为我实例化了 @PersistenceContext EntityManager...
【解决方案3】:

不,我不认为这是正确的。 ORM 是实现 DAO 的一种方式;你可以选择在没有 ORM 的情况下做 DAO。

你把它弄反了:我认为 ORM 比 DAO 更重,因为依赖关系更大。我可以在没有 ORM 的情况下直接用 JDBC 编写 DAO。更轻,IMO。

我们是否同意取决于我们如何定义“轻”和“重”。我将按依赖项进行 - 超出 JDK 本身所需的额外 JAR 数量。

【讨论】:

  • 模式?这和它有什么关系?
猜你喜欢
  • 2023-03-25
  • 2011-11-11
  • 2012-10-05
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 2014-09-19
  • 2012-04-01
  • 1970-01-01
相关资源
最近更新 更多