参考spring transaction management 简单的改造公司的persistence框架

公司的persistence是一个很久之前写的的框架了。期间经过了各种修修补补。最近要做的一个东西,需要支持数据库事务。
看了下,居然不支持事务,这个猛。。。

下面是一个数据库操作的调用链
这里写图片描述
框架采用了命令模式,最后会调用到以下的代码里

UpdateDatabaseExecutor{
 public void invokeExecutor(...) throws Exception {
   ...
   java.sql.Connection con = null;
   con = ConnectionManager.getConnection();     
   //取到connection 后,并没有begin,rollback之类的事务支持
    stmt.beforeExecution();
    ret = stmt.executeUpdate(con);
    stmt.afterExecution(ret);
   ...
   PersistenceUtils.closeStuff(con, stmt, null);
 }
}

由于知道所有的Connection都使用ConnectionManager.getConnection(),最后会调用到一个统一的代码里,如下

javax.sql.DataSource ds = (javax.sql.DataSource) result;
final String username =getProperty("javax.sql.username", ctx_);
final String password =getProperty("javax.sql.password", ctx_);
if ((username != null) || (password != null)) {
 result = ds.getConnection(username, password);
} else {
 result = ds.getConnection();
}
return result;

根据spring template transaction 里的源码的原理,begin的方法 取connection的并将connection放到threadlocal里。后面取得时候先判断threadlocal里是否已经存在Connection,如果存在的话,就从threadlocal里去取了。则可以改造代码加入如下

//加入的支持transaction处理开始
transactionManager.setDataSource(ds);
Connection connection = transactionManager.getConnection();
if(connection!=null){
  return connection;
}
//加入的支持transaction处理结束
javax.sql.DataSource ds = (javax.sql.DataSource) result;
    final String username =getProperty("javax.sql.username", ctx_);
    final String password =getProperty("javax.sql.password", ctx_);
    if ((username != null) || (password != null)) {
        result = ds.getConnection(username, password);
    } else {
        result = ds.getConnection();
    }
return result;

那么关键就在于 transactionManager.getConnection();
transactionManager也是我新加的一个类

public class TransactionManager {
    //在线程上下文里存入一个ConnectionHolder的类
    private ThreadLocal<ConnectionHolder> ConnectionCache =new  
       ThreadLocal<ConnectionHolder>();
    private TransactionManager(){};
    //spring里dataSource可以有多个,而我的项目里只有一个
    private DataSource dataSource;
    private static TransactionManager instance = new TransactionManager(); 
    //单例
    public static TransactionManager getInstance(){
        return instance;
    }
    public DataSource getDataSource() {
        return dataSource;
    }
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    private ConnectionHolder get(){
        return ConnectionCache.get();
    }
    //getConnection的时候从上下文里先判断是否存在,如果有的就直接获取,没有就返回空
    public Connection getConnection() throws SQLException{
        Connection connection = null;
        ConnectionHolder holder =get();
        if(holder==null)
            return null;
        if(holder.getOwnerThread() == Thread.currentThread()){
            connection = holder.getConn();
        }
        if(connection!=null && !connection.isClosed() && !connection.getAutoCommit()){
            return connection;
        }

        return null;
    }

    public void setConnection(Connection conn) {
        ConnectionCache.set(new ConnectionHolder(conn,Thread.currentThread()));
    }

    public static class ConnectionHolder{
        private Connection conn;
        private Thread ownerThread;
        public ConnectionHolder(Connection conn, Thread ownerThread) {
            this.conn = conn;
            this.ownerThread = ownerThread;
        }
        public Connection getConn() {
            return conn;
        }
        public Thread getOwnerThread() {
            return ownerThread;
        }
    }

    public boolean hasConnectionInThread() throws SQLException{
        return getConnection()!=null;
    }

    public boolean hasConnectionInThread(Connection conn) throws SQLException{
        Connection connInThreadContext = getConnection();
        if(connInThreadContext==null)
            return false;

        if(connInThreadContext==conn)
            return true;

        return false;       
    }
       //调用doBegin,会将一个Connection放入到threadlocal里
    public Connection doBegin() throws ConnectionException, SQLException {
        Connection conn = null;
        conn = ConnectionManager.getConnection();
        conn.setAutoCommit(false);
        setConnection(conn);
        return conn;
    }

    public void commit() throws SQLException {
        getConnection().commit();
    }

    public void rollback() throws SQLException {
        getConnection().rollback();
    }

    public void close() throws SQLException {
        getConnection().close();
        ConnectionCache.remove();
    }          
}

在调用的地方

public void processs(PendingUserValue pendingUserValue) {
         try {
            。。。
            TransactionManager.getInstance().doBegin();
            process(pendingUserValue);
            TransactionManager.getInstance().commit();
                   。。。
        }catch(Exception e){
           try {
                TransactionManager.getInstance().rollback();                
        }finally{
            try {
                TransactionManager.getInstance().close();
            } catch (SQLException e) {
                。。。
            }
        }
}

以为万事大吉了,在测试中发现,我们的数据库里的表的主键不是通过自增的方式来做的,而是通过一张主键表来维护的。
也就是说process(pendingUserValue)里如果要插入一条记录,例如User, 就要先到主键表里获取到User的key,然后更新主键表里的User主键最大值,这样就需要执行commit操作。
一旦commit的话,connection遇到错误就没法rollback了。这实际上是事务的传播机制里的 RequiresNew
当Transaction2执行的时候,transaction1需要挂起

当Transaction2执行的时候,transaction1需要挂起。

用我的项目里逻辑就是外面 process执行到需要获取主键时,获取的主键是一个单独的事务,这个单独事务不影响process的处理。因此看了下spring 对这部分的处理源码,然后在TransactionManager 里加入了如下代码

public class TransactionManager {
    //挂起
     public ConnectionHolder suspend() {
        ConnectionHolder holder= get();
        ConnectionCache.remove();
        return holder;
    }
    //恢复
    public void resume(ConnectionHolder holder) {
        ConnectionCache.set(holder);
    }
}

获取主键的调用的时候,先挂起,再在finally里恢复,如下

TransactionManager.ConnectionHolder holder = null;
    try {
        if(TransactionManager.getInstance().hasConnectionInThread()){
            holder = TransactionManager.getInstance().suspend();
        }
        connection = ConnectionManager.getConnection();
        connection.commit();                
    } finally {
        PersistenceUtils.closeStuff(connection, statement, resultSet);
        if(holder!=null)
            TransactionManager.getInstance().resume(holder);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值