24

所以我已经完成了这个通用的 DAO 事情,从表面上看,它似乎没问题。它基本上是仿照 Hibernate 家伙的 CaveatEmptor 示例应用程序。

最重要的是,我有一个业务层……应用程序的核心。它完全不知道任何特定的 DAO 实现。

到目前为止,一切似乎都很好,直到我开始考虑交易。如果将事务留给客户端来实现,那么我究竟如何保持我在各层之间进行的良好分离?也就是说,我目前正在使用 Hibernate,我不太想将特定于 Hibernate 的事务添加到我的业务层代码中。

我可以使用开始、提交和回滚方法创建一个简单的事务接口,并将实现传递给我的业务层......但是......我不确定......

所以这是一个挑战:你能为我推荐一种不使用 Spring(或 EJB,或任何其他附加框架)这个词的方法吗?

4

5 回答 5

13

我记得Martin Fowler建议在业务层保持对事务的控制,因为事务是一个业务问题。(如果您设计一个 BankAccount 类,则事务是域语言的一部分)。

您可以尝试在 .NET 中实现 TransactionScope,它的工作原理类似于

using (TransactionScope ts = new TransactionScope())
{
  ...
}

它与(不完全是,但如果你是 Java 人,它对你来说更明确)是一样的

TransactionScope scope = new TransactionScope();
try
{
 ...
 scope.Commit();
}
catch(Exception ex)
{
  scope.Rollback();
  throw;
}

要将您的业务层与任何 DAO 技术分离,您可以在您的领域语言中添加一个 TransactionFactory,它返回您使用 Commit 和 Rollback 方法定义的 ITransactionScope(一个接口)。这样,您的域层就不会绑定到您的 DAO 层,只有 TransactionFactory 的具体实现。

ITransactionScope scope = transactionFactory.CreateTransaction();
try
{
 ...
 scope.Commit();
}
catch(Exception ex)
{
  scope.Rollback();
  throw;
}
于 2009-04-21T17:25:37.230 回答
7

在 Web 应用程序中,我划分事务的方法是利用 HTTP 请求/响应周期,其中每个原子业务操作在这些周期之一的范围内执行,在单个专用线程中。

无论使用什么 Web 框架(Struts、JSF、GWT 等),通常都存在一个可以执行事务划分的“接缝”。在 Struts 中,它可以是一个基本的 Action 类。在 GWT 中,它可以是一个基础 RemoteServiceImpl 类。

因此,使用该中心访问点打开事务(在允许执行特定于应用程序的代码之前),并在没有异常冒泡或回滚时通过提交终止它(在执行特定于应用程序的代码之后) .

我在一个大型复杂的商业 Web 应用程序中广泛应用了这种策略,并且证明效果很好。

于 2009-09-21T12:06:11.077 回答
3

也许现在回答有点晚了,但是如何为特定事务创建另一个类,它位于业务层和 dao 层之间?例如,如果来自 DAO 的方法 a() 和 b() 要在某个特定 foo() 业务方法的事务中运行,则创建类似 fooInTransaction() 的东西,它启动一个事务并调用 a() 和 b()它。业务方法 foo() 委托给它。

这将保持业务代码干净,并且可以通过重构来避免重复。

于 2011-03-10T04:42:58.070 回答
2

过去,我将事务逻辑放在根 DAO 中,用于与模型中的对象层次结构匹配的 DAO 层次结构,这些对象层次结构代表系统中的单个实体实体。

即,如果你有一个有很多 Y 的 X,并且你想同时存储和检索 X 和它们的 Y 作为单个复合对象,那么你的 X 的 DAO 也应该调用 Y 的 DAO。然后你可以把围绕 X 的 DAO 中的 add() 和 update() 方法中的所有内容进行事务 - 甚至将 Y DAO 包设为私有以将其隐藏在您的主要业务逻辑中。即,而不是业务逻辑:

XDAO xDAO = new XDAO(conn);
xDAO.startTransaction();
boolean success = xDAO.add(x);
if (success)
    for (Y y : x.getYs()) {
        success = YDAO.add(y);
        if (!success) break;
    }
if (success)
    xDAO.commit();
else
    xDAO.rollback();

你只会有:

XDAO xDAO = new XDAO(conn);
xDAO.add(x);

(具有该 DAO 内部的成功/提交/回滚逻辑)

但是,这并不能涵盖所有情况,您的情况可能会有所不同(例如,我的工作与 JDBC 一起使用,我不知道 Hibernate 是如何工作的,或者是否有可能)。

于 2009-04-21T16:31:10.483 回答
1

您有权认为该应用程序是协调事务的好地方,因为这允许组合由各种服务/管理器/或您想要调用它们的任何东西实现的更复杂的操作。

一个简单的解决方案是定义一个 ITransaction 接口,并使用某种类型的工厂或 DI 对您的应用程序隐藏实际的 ITransaction 实现者。我使用 nHibernate 在 .net 中像这样滚动我自己的,基本上我有一个基类,我的所有经理(在这种情况下,经理包含一组逻辑实体的业务逻辑,例如可能使用一个或多个存储库的成员资格、订单)。我的基类有一个 ITransaction BeginTransaction(),它根据配置文件动态创建一个类型。

然后这个类使用 nHibernate 的 Session 来开始和提交事务。

于 2009-04-21T16:23:36.693 回答