原创作者: yiding_he
阅读:2749次
评论:2条
更新时间:2011-05-26
事务的处理从来都是“三部曲”:
在 JDBC 中,调用事务处理并不复杂,因为 Connection 类提供了现成的方法,就是 setAutoCommit() 、commit() 和 rollback()。
我们希望 DAO 能够在这个基础上更加方便的处理事务:每个线程都执行各自的事务,一旦事务开始,线程中所有的 DAO 对象的操作都将处于该事务当中,随同事务一起提交和回滚,这样就能大大简化当一个事务需要连接多个数据库时的处理方式。
每个线程执行各自的事务,这同数据库本身处理事务的方式是一致的,不需要特别设计。另外每个线程还需要有各自的标识说明当前事务状态,用 ThredLocal 就可以了。
首先给 DAO 类增加事务处理的静态方法:
可以看出,这几个方法都允许连续调用多次,但实际上只有第一次起了作用。
我们争论过是否实现“事务嵌套”。但实际上根据事务的定义,事务不存在“嵌套”的说法。想一想,如果“子事务”被 rollback 掉了,那么因为它执行不成功,“父事务”也必然会被 rollback 掉,否则就不叫事务了。这跟在同一个事务里没有区别。JDBC 也没有所谓“事务嵌套”的概念。
当事务开始之后,持有 Connection 的 DbExecutor 对象必须被缓存起来以供下次调用。它们被缓存在 GlobalTransaction 中(见下图)。一旦事务结束,GlobalTransaction 便会将线程中缓存的 DbExecutor 对象全部清掉。
同时,DAO 对象在每次执行完数据库操作的时候,要判断一下当前是否处于事务当中。以 insert 方法为例:
至此便实现了 DAO 对事务的处理。
java 代码
- try {
- begin();
- // ...
- commit();
- } catch (Exception e) {
- rollback();
- }
在 JDBC 中,调用事务处理并不复杂,因为 Connection 类提供了现成的方法,就是 setAutoCommit() 、commit() 和 rollback()。
我们希望 DAO 能够在这个基础上更加方便的处理事务:每个线程都执行各自的事务,一旦事务开始,线程中所有的 DAO 对象的操作都将处于该事务当中,随同事务一起提交和回滚,这样就能大大简化当一个事务需要连接多个数据库时的处理方式。
每个线程执行各自的事务,这同数据库本身处理事务的方式是一致的,不需要特别设计。另外每个线程还需要有各自的标识说明当前事务状态,用 ThredLocal 就可以了。
首先给 DAO 类增加事务处理的静态方法:
DAO.java
- /**
- * 开始事务
- *
- * @throws DAOException 如果读取配置或执行数据库失败
- */
- public static void beginTransaction() throws DAOException {
- try {
- log.debug("事务开始。");
- GlobalTransaction.begin();
- } catch (Exception e) {
- throw new DAOException(e);
- }
- }
- /**
- * 提交事务
- *
- * @throws DAOException 如果执行数据库失败
- */
- public static void commitTransaction() throws DAOException {
- // 如果不在事务当中则不做任何操作
- if (!GlobalTransaction.isInTransaction()) {
- return;
- }
- try {
- GlobalTransaction.commit();
- } catch (Exception e) {
- throw new DAOException(e);
- }
- }
- /**
- * 回滚事务
- *
- * @throws DAOException 如果回滚失败
- */
- public static void rollbackTransaction() throws DAOException {
- // 如果不在事务当中则不做任何操作
- if (!GlobalTransaction.isInTransaction()) {
- return;
- }
- try {
- GlobalTransaction.rollbackFinish();
- } catch (Exception e) {
- throw new DAOException(e);
- }
- }
可以看出,这几个方法都允许连续调用多次,但实际上只有第一次起了作用。
我们争论过是否实现“事务嵌套”。但实际上根据事务的定义,事务不存在“嵌套”的说法。想一想,如果“子事务”被 rollback 掉了,那么因为它执行不成功,“父事务”也必然会被 rollback 掉,否则就不叫事务了。这跟在同一个事务里没有区别。JDBC 也没有所谓“事务嵌套”的概念。
当事务开始之后,持有 Connection 的 DbExecutor 对象必须被缓存起来以供下次调用。它们被缓存在 GlobalTransaction 中(见下图)。一旦事务结束,GlobalTransaction 便会将线程中缓存的 DbExecutor 对象全部清掉。
DbExecutorFactory.java
- /**
- * 创建 DbExecutor 对象
- *
- * @param dsName 数据源名称
- *
- * @return 连接到指定数据源的 DbExecutor 对象。如果当前处于事务中,则从事务缓存中获取 DbExecutor 对象,否则重新创建一个。
- *
- * @throws ConfigErrorException 如果读取配置失败
- * @throws DAOException 如果获取数据库连接失败
- */
- public DbExecutor getDbExecutor(String dsName) throws ConfigErrorException, DAOException {
- try {
- DbExecutor executor = GlobalTransaction.getExecutor(dsName);
- if (executor == null || executor.isClosed()) {
- log.debug("重新创建executor \"" + dsName + "\"");
- executor = createNewExecutor(dsName);
- if (GlobalTransaction.isInTransaction()) {
- // 将获得的 DbExecutor 对象放入事务缓存
- executor.beginTransaction();
- GlobalTransaction.saveExecutor(dsName, executor);
- }
- }
- return executor;
- } catch (SQLException e) {
- throw new DAOException(e);
- }
- }
GlobalTransaction.java
- /**
- * 获得当前线程内缓存的连接到指定数据源的 DbExecutor 对象
- *
- * @param dsName 数据原名称
- *
- * @return 如果数据源处于事务当中,则返回正在处理该事务的 DbExecutor,否则返回 null
- */
- public static DbExecutor getExecutor(String dsName) {
- HashMap map = getExecutorsMap();
- return (DbExecutor) map.get(dsName);
- }
- /**
- * 开始事务
- */
- public static void begin() {
- status.set(STARTED);
- }
- /**
- * 提交事务
- */
- public static void commit() {
- HashMap map = getExecutorsMap();
- Collection executors = map.values();
- for (Iterator iterator = executors.iterator(); iterator.hasNext();) {
- DbExecutor executor = (DbExecutor) iterator.next();
- executor.close();
- }
- map.clear();
- status.set(FINISHED);
- }
- /**
- * 回滚事务
- */
- public static void rollback() {
- if (!getStatus().equals(STARTED)) {
- return;
- }
- Collection executors = ((HashMap) transactions.get()).values();
- for (Iterator iterator = executors.iterator(); iterator.hasNext();) {
- DbExecutor executor = (DbExecutor) iterator.next();
- try {
- executor.rollback();
- } catch (SQLException e) {
- log.error("事务回滚失败", e);
- }
- }
- getExecutorsMap().clear();
- status.set(ROLLBACKED);
- }
同时,DAO 对象在每次执行完数据库操作的时候,要判断一下当前是否处于事务当中。以 insert 方法为例:
DAO.java
- /**
- * 执行数据库插入
- *
- * @param obj 要插入的记录
- * @param tablename 要插入的表名
- *
- * @throws DAOException 如果读取配置或执行数据库失败
- */
- public void insert(Object obj, String tablename) throws DAOException {
- DbExecutor executor = null;
- try {
- executor = initExecutor();
- executor.insert(obj, tablename);
- } catch (DAOException e) {
- throw e;
- } catch (Exception e) {
- throw new DAOException(e);
- } finally {
- if (!GlobalTransaction.isInTransaction() && executor != null) {
- executor.close();
- }
- }
- }
至此便实现了 DAO 对事务的处理。
2 楼 yiding_he 2010-06-22 20:49
有兴趣的话可以打开这里:http://code.google.com/p/hydrogen-dao/
源码已公开。
1 楼 figure_he_he 2010-05-10 13:42