posts - 193,  comments - 520,  trackbacks - 0
问题背景:我们是一家工作流公司,客户采购我们的产品后,将其嵌入其项目中。我们的工作流采用的是   spring+hibernate的方式,客户项目则是jdbc直接进行数据库操作。
问题:客户在其数据库操作过程中需要调用我们的工作流接口,这样就需要将我们的工作流操作与他们的业  务操作置于同一个事务中。我们的服务采用的都是spring的声明式事务,而客户采用的是对         connection进行事务处理。如何保证事务的一致性?
想到的解决方案一:使用jta事务,用tomcat+jotm提供事务管理器。为什么一开始就想到要使用jta事务??实际上我们和客户都是使用的同一个数据库,为了方便,各自使用了不同的数据库连接方式,使用jta的话确实有bt的意思在里面。但是事实上是我们的第一反应都是jta。最后没有采用该方法的原因也很简单:我没有将jotm配置成功!汗一个。
想到的解决方案二:将客户的这些特定代码用spring管理起来。因为要修改客户部分代码,这个方案遭到了客户的强烈反对。于是放弃。
想到的解决方案三:客户数据库操作与我们的服务使用同一个数据库连接。然后编程处理事务。存在两种方式:一种是把客户的连接传给我们,另一种则是把我们的连接传给客户。第一种方式对我们的影响太大,所以最后决定采用后一种方式:从hibernate session中获取connection然后传递给客户。接下来查看一下HibernateTemplate的execute()方法,思路就很简单了:获取定义的sessionFactory-->创建一个新的session并打开-->将session与当前线程绑定-->给客户代码返回connection-->打开事务-->客户使用我们传递的connection进行数据库操作-->我们不带声明事务的服务操作-->提交事务-->解除绑定。
实际要注意的地方是:1、将session与当前线程绑定使用的TransactionSynchronizationManager.bindResource()方法,这样在HibernateTemplate里才能找到session;
                    2、我们的服务一定要把声明式事务彻底干掉,否则会有commit;
                    3、我们服务调用完毕后一定要flush session,否则客户代码不会感知数据库里的数据变化。
最终解决:使用了spring里常用的模板和回调。代码如下:
public class TransactionTemplate {

    
protected final Log logger = LogFactory.getLog(TransactionTemplate.class);

    
private FlushMode flushMode = FlushMode.ALWAYS;

    
public Object execute(TransactionCallback callback) {
        
//首先获取sessionFactory
        SessionFactory sessionFactory = (SessionFactory) Framework.getEngine()
                .getContainer().getComponent(
"sessionFactory");
        
//创建一个新的session并打开
        logger.debug("Opening single Hibernate Session in TransactionTemplate");
        Session session 
= getSession(sessionFactory);
        
//将session与当前线程绑定
        TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
        
//获取数据库连接
        Connection conn = session.connection();
        Object result 
= null;
        Transaction transaction 
= null;
        
try {
            
//开始处理事务
            transaction = session.beginTransaction();
            
try {
                result 
= callback.doInTransaction(conn);
            }
            
catch (RuntimeException ex) {
                doRollback(session, transaction);
                
throw ex;
            }
            
catch (Error err) {
                doRollback(session, transaction);
                
throw err;
            }
            
//如果数据库操作过程中没有发生异常则提交事务
            transaction.commit();
        } 
catch (WorkflowException e) {
            logger.error(
"数据库操作失败,事务回滚也失败!");
            
throw e;
        } 
catch (RuntimeException ex) {
            logger.error(
"数据库操作失败,事务被回滚!");
            
throw ex;
        } 
catch (Error err) {
            logger.error(
"数据库操作失败,事务被回滚!");
            
throw err;
        } 
finally {
            
// 将session与当前线程解除绑定
            TransactionSynchronizationManager.unbindResource(sessionFactory);
            doClose(session);
        }
        
return result;
    }

    
protected Session getSession(SessionFactory sessionFactory) {
        Session session 
= SessionFactoryUtils.getSession(sessionFactory, true);
        FlushMode flushMode 
= getFlushMode();
        
if (flushMode != null) {
            session.setFlushMode(flushMode);
        }
        
return session;
    }

    
private void doRollback(Session session, Transaction transaction) {
        logger.debug(
"数据库操作异常,开始回滚事务");
        
try {
            transaction.rollback();
            logger.debug(
"回滚事务成功!");
        }
        
catch (Exception e) {
            logger.error(
"回滚事务失败!");
            
throw new WorkflowException("回滚事务失败!");
        } 
finally {
            session.clear();
        }
    }

    
private void doClose(Session session) {
        logger.debug(
"开始关闭连接");
        
try {
            session.close();
        }
        
catch (Exception e) {
            logger.error(
"关闭连接失败!");
            
throw new WorkflowException("关闭连接失败!");
        }
    }

    
public FlushMode getFlushMode() {
        
return flushMode;
    }

    
public void setFlushMode(FlushMode flushMode) {
        
this.flushMode = flushMode;
    }
}

public interface TransactionCallback {

    Object doInTransaction(Connection conn);
}
调用伪代码:
    public void methodA(){
        TransactionTemplate transactionTemplate
=new TransactionTemplate();
        transactionTemplate.execute(
new TransactionCallback(){
            
public Object doInTransaction(Connection conn) {
                
//客户代码
                client.method1("1");
                
//我们代码 直接使用
                our.method2();
                
//客户代码
                client.method3("l");
                
return null;  
            }
        });
    }




http://www.blogjava.net/ronghao 荣浩原创,转载请注明出处:)
posted on 2007-10-09 15:11 ronghao 阅读(7724) 评论(5)  编辑  收藏 所属分类: 工作日志

FeedBack:
# re: 结合spring+hibernate与jdbc的事务
2007-10-09 18:40 | BeanSoft
感谢分享!  回复  更多评论
  
# re: 结合spring+hibernate与jdbc的事务
2007-10-09 18:57 | 快乐的猪猪
不错  回复  更多评论
  
# re: 结合spring+hibernate与jdbc的事务
2007-10-11 08:07 | ce
不错。。。  回复  更多评论
  
# re: 结合spring+hibernate与jdbc的事务[未登录]
2007-10-12 07:49 | leo
看的有点不懂。。。  回复  更多评论
  
# re: 结合spring+hibernate与jdbc的事务
2008-08-01 14:10 | asd
不错。  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航:
 
<2007年10月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

关注工作流和企业业务流程改进。现就职于ThoughtWorks。新浪微博:http://weibo.com/ronghao100

常用链接

留言簿(38)

随笔分类

随笔档案

文章分类

文章档案

常去的网站

搜索

  •  

最新评论

阅读排行榜

评论排行榜