J2EE 开发人员使用数据访问对象(Data Access Object DAO)设计模式,以便将低级别的数据访问逻辑与高级别的业务逻辑分离。实现 DAO 模式涉及比编写数据访问代码更多的内容。在本文中,Java 开发人员 Sean C. Sullivan 讨论了 DAO 编程中三个常常被忽略的方面:事务界定、异常处理和日志记录。
图 1 显示了应用程序和数据源之间的关系:
图 1. 应用程序和数据源
在整个应用程序中使用数据访问对象(DAO)使我们可以将底层数据访问逻辑与业务逻辑分离开来。我们构建了为每一个数据源提供 CRUD (创建、读取、更新、删除)操作的 DAO 类。
在本文中,我将为您介绍构建更好的 DAO 类的 DAO 实现策略和技术。更确切地说,我将讨论日志、异常处理和事务界定。您将学到如何将这三者结合到自己的 DAO 类中。本文假定您熟悉 JDBC API、SQL 和关系数据库编程。
我们将以对 DAO 设计模式和数据访问对象的概述开始。
DAO基础
DAO 模式是标准 J2EE 设计模式之一。开发人员用这种模式将底层数据访问操作与高层业务逻辑分离开。一个典型的 DAO 实现有以下组件:
一个 DAO 工厂类
一个 DAO 接口
一个实现了 DAO 接口的具体类
数据传输对象(有时称为值对象)
具体的 DAO 类包含访问特定数据源的数据的逻辑。在下面一节中您将学习设计和实现数据访问对象的技术。
事务界定
关于 DAO 要记住的重要一点是它们是事务性对象。由 DAO 所执行的每一个操作 -- 如创建、更新或者删除数据 -- 都与一个事务相关联。因此, 事务界定的概念就变得特别重要了。
事务界定是定义事务边界的方式。J2EE 规范描述了两种事务界定的模型:编程式(programmatic)和声明式(declarative)。表 1 分析了这两种模型:
表 1. 两种事务界定的模型
声明式事务界定
编程式事务界定
程序员用 EJB 部署描述符声明事务属性。
程序员负责编写事务逻辑。
运行时环境(EJB 容器)用这些属性自动管理事务。
应用程序通过一个 API 控制事务。
我们将侧重于编程式事务界定。
设计考虑
如前所述,DAO 是事务性对象。一个典型的 DAO 执行像创建、更新和删除这样的事务性操作。在设计 DAO 时,首先要问自己以下问题:
事务要如何开始?
事务应如何结束?
哪一个对象将负责开始一个事务?
哪一个对象将负责结束一个事务?
DAO 是否要负责事务的开始和结束?
应用程序是否需要通过多个 DAO 访问数据?
事务涉及到一个 DAO 还是多个 DAO?
一个 DAO 是否调用另一个 DAO 的方法?
了解上述问题的答案将有助于您选择最适合的 DAO 的事务界定策略。在 DAO 中有两种主要的界定事务的策略。一种方式是让 DAO 负责界定事务,另一种将事务界定交给调用这个 DAO 方法的对象处理。如果选择了前一种方式,那么就将事务代码嵌入到 DAO 中。如果选择后一种方式,那么事务界定代码就是在 DAO 类外面。我们将使用简单的代码示例帮助您更好理解每一种方式是如何工作的。
清单 1 显示了一个有两种数据操作的 DAO:创建和更新:
清单 1. DAO 方法
public void createWarehouseProfile(WHProfile profile);
public void updateWarehouseStatus(WHIdentifier id, StatusInfo status);
清单 2 显示了一个简单的事务。事务界定在 DAO 类外面。注意在这个例子中调用者是如何在一个事务中结合多个 DAO 操作的。
清单 2. 调用者管理的事务