Transaction 浅析

一。先从jdbc来说最简单的事物处理

public void test1() throws Exception{
		String url="";
		Class.forName("com.mysql.jdbc.Driver");//加在驱动
		Connection con=DriverManager.getConnection(url);
		//事物开始。
		con.setAutoCommit(false);
		
		//执行操作。
		
		//事物提交
		con.commit();   
		con.setAutoCommit(true);
		con.close();
	}
这个相信大家都同意吧,有时看到有人会这样写。

public void test() throws Exception{
		String url="";
		Class.forName("com.mysql.jdbc.Driver");//加在驱动
		Connection con=DriverManager.getConnection(url);
		con.setAutoCommit(false);
		Statement stm = con.createStatement();
		//执行数据库操作。
		stm = con.createStatement();   
        con.setAutoCommit(false);   
        //若不出现异常,则继续执行到try语句完,否则跳转到catch语句中
        stm.addBatch("insert into student values(23,'tangbao','高数',100)");   
        stm.addBatch("insert into student values(24,'王定','c#',98)");   
        stm.addBatch("insert into student values(25,'王国云','java',90)");   
        stm.addBatch("insert into student values(26,'溜出','英语',89)");   
        stm.addBatch("insert into student values(27,'wqde','java',63)");   
        stm.executeBatch();   
        System.out.println("插入成功!");   
        //commit:若成功执行完所有的插入操作,则正常结束
        con.commit();   
        System.out.println("提交成功!");   
        con.setAutoCommit(true);  
        stm.close();
		con.setAutoCommit(true);
		con.close();
	}
原来我看到statement就蒙了,实际上他就是一条执行语句而已  只要没设定con.setAutoCommit(true)都是一条执行,一个事物,看了上面的代码相信你会明白的。

     这是最基础的事物,而,spring和hibernate 以及jta的事物,都是在这个基础上进行封装的。

二,咱们先来看看datasource 接口

      

public interface DataSource
    extends CommonDataSource, Wrapper
{

    public abstract Connection getConnection()
        throws SQLException;

    public abstract Connection getConnection(String s, String s1)
        throws SQLException;
}
 这两个方法都能获得一个链接实时上他就是一种咱们得到的链接,spring有没有对着接口进行一个代理呢?,然后通过AOP将这个datasource 注入到springbeanFactory 里面的dataSource 呢,于是不管从哪里得到datasource 必然会被spring拦截。下面模拟了一下简单的实现

package com.xianfeng.common.test;
public class DatasourceHandler implements InvocationHandler {  
	
	private DataSource dataSource;  
	/** 
	 * @param dataSource 
	 */  
	public DatasourceHandler(DataSource dataSource) {  
		super();  
		this.dataSource = dataSource;  
	}  
	/* (non-Javadoc) 
	 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) 
	 */  
	@Override  
	public Object invoke(Object proxy, Method method, Object[] args)  
			throws Throwable {  
		if(method.getName().equals("getConnection")){  
			
			if(ResourceHolder.getResource(proxy)!=null){  
				Connection connection = (Connection) method.invoke(this.dataSource, args);  
				ResourceHolder.addResource(proxy, connection);  
				ResourceHolder.setKey(proxy);
			}  
			return ResourceHolder.getResource(proxy);  
		}else{  
			return method.invoke(this.dataSource, args);  
		}  
	}
} 

package com.xianfeng.common.testDynamicProxy;

import java.util.HashMap;
import java.util.Map;

public class ResourceHolder {  
	  
    private static ThreadLocal> resources= new ThreadLocal>();  
    private static ThreadLocal key= new ThreadLocal();  
    
    public static void setKey(Object keyObject){
    	if (key.get()==null) {
			key.set(keyObject);
		}
    }
    public static Object getKey(){
    	return key.get();
    }
    
    public static void addResource(Object key,Object value){  
        if(resources.get()==null){  
            resources.set(new HashMap());  
        }  
        resources.get().put(key, value);  
    }  
      
    public static Object getResource(Object key){  
          
        return resources.get().get(key);  
    }  
      
    public static void clear(){  
        resources.remove();  
    }  
}


上面用了java的动态代理生成了一个datasource的代理类,再调用datasource的getconnection 方法时,会得到一个connection 并把他与当前的线程绑定,动态生成的代理类也会与本线程绑定。

在这个线程需要connection 时,通过resourceHolder getResource 获得,而getResource 需要一个key,因为代理类也与线程绑定了挡在key里可以直接活动。

好处:1.实现了线程安全。2整个线程中得到的connection 都将是同一个对象,因此用 上面一提到的 知识就很容易实现 spring的事物安全了。

疑点:为啥要同时吧代理类和connection都放在线程中呢,我猜想是防止connection 为空的情况,要是空的就可以直接用代理生成,后再把新的connection 和代理类绑定,再与线程绑定。

三.hibernate 则是session控制的事物。

public void test(){
		Configuration configuration =new Configuration().configure();
		SessionFactory sf=configuration.buildSessionFactory();
		Session ss =sf.openSession();
		ss.beginTransaction();
		//执行增删改差。
		ss.flush();
		ss.getTransaction().commit();
		ss.close();
	}
这跟一种的事物好像真的不一样啊,好的我们在往下看,看看他的API里他调用的是

public Transaction beginTransaction()
        throws HibernateException
    {
        errorIfClosed();
        if(rootSession != null)
            log.warn("Transaction started on non-root session");
        Transaction result = getTransaction();
        result.begin();
        return result;
    }
public Transaction getTransaction()
        throws HibernateException
    {
        errorIfClosed();
        return jdbcContext.getTransaction();
    }
去jdbc事物里看看他的begin();
toggleAutoCommit = jdbcContext.connection().getAutoCommit();
            if(log.isDebugEnabled())
                log.debug((new StringBuilder()).append("current autocommit status: ").append(toggleAutoCommit).toString());
            if(toggleAutoCommit)
            {
                log.debug("disabling autocommit");
                jdbcContext.connection().setAutoCommit(false);
            }
        }
看到这几行时,你瞬间懂了吧,他一样事调用的  setAutoCommit(false);那么他是怎么实现线程安全的呢 hibernate官方文档手册的实例中提供了一个好方法。

public class HibernateUtil {  
    public static final SessionFactory sessionFactory;  
    static {  
	       try {  
	              sessionFactory = new Configuration().configure()  
	                             .buildSessionFactory();  
	            } catch (Throwable ex) {  
	                  throw new ExceptionInInitializerError(ex);  
	            }  
    		}  
   public static final ThreadLocal session = new ThreadLocal();  
   public static Session currentSession() throws HibernateException {  
        Session s = (Session) session.get();  
        if(s == null) {  
              s = sessionFactory.openSession();  
                session.set(s);  
          }  
           return s;  
   }  
  public static void closeSession() throws HibernateException {  
         Session s = (Session) session.get();  
           if(s != null) {  
                 s.close();  
             }  
           session.set(null);  
   }  
和二种的处理方法,大同小异了。
四、事物间的嵌套是怎么处理的? spring的事物就支持嵌套, 让我们去它的原码中找找 org.springframework.orm.hibernate3.HibernateTransactionManager

事物准备阶段。

protected Object doGetTransaction()
    {
        HibernateTransactionObject txObject = new HibernateTransactionObject(null);
        //设置事物是否允许嵌套
        txObject.setSavepointAllowed(isNestedTransactionAllowed());
        //这里就像用一个对象来获得当前线程的session.第一个事物来时肯定以前没有绑定过资源。
        SessionHolder sessionHolder = (SessionHolder)TransactionSynchronizationManager.getResource(getSessionFactory());
        if(sessionHolder != null)
        {
            if(logger.isDebugEnabled())
                logger.debug((new StringBuilder("Found thread-bound Session [")).append(SessionFactoryUtils.toString(sessionHolder.getSession())).append("] for Hibernate transaction").toString());
            txObject.setSessionHolder(sessionHolder);
        } else
        if(hibernateManagedSession)
            try
            {
            	//先看当前线程有无session 没有及open一个绑定到线程中,有就把原来的拿过来。
                Session session = getSessionFactory().getCurrentSession();
                if(logger.isDebugEnabled())
                    logger.debug((new StringBuilder("Found Hibernate-managed Session [")).append(SessionFactoryUtils.toString(session)).append("] for Spring-managed transaction").toString());
                txObject.setExistingSession(session);
            }
            catch(HibernateException ex)
            {
                throw new DataAccessResourceFailureException("Could not obtain Hibernate-managed Session for Spring-managed transaction", ex);
            }
        if(getDataSource() != null)
        {
        	//第一个事物来的时候都是没有的,
            ConnectionHolder conHolder = (ConnectionHolder)TransactionSynchronizationManager.getResource(getDataSource());
            txObject.setConnectionHolder(conHolder);
        }
        return txObject;
    }

protected void doBegin(Object transaction, TransactionDefinition definition)
    {
        HibernateTransactionObject txObject;
        Session session;
        txObject = (HibernateTransactionObject)transaction;
        //第一次来的时候是没有绑定的,现在事物还没有正式开始。
        if(txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction())
            throw new IllegalTransactionStateException("Pre-bound JDBC Connection found! HibernateTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single HibernateTransactionManager for all transactions on a single DataSource, no matter whether Hibernate or JDBC access.");
        session = null;
        //第一次来的时候是没有绑定的,这个事物是没有绑定到线程中的。
        if(txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction())
        {
            Interceptor entityInterceptor = getEntityInterceptor();
            Session newSession = entityInterceptor == null ? ((Session) (getSessionFactory().openSession())) : ((Session) (getSessionFactory().openSession(entityInterceptor)));
            if(logger.isDebugEnabled())
                logger.debug((new StringBuilder("Opened new Session [")).append(SessionFactoryUtils.toString(newSession)).append("] for Hibernate transaction").toString());
            txObject.setSession(newSession);
        }
        session = txObject.getSessionHolder().getSession();
        Transaction hibTx;
        if(timeout != -1)
        {
        	//这里是重点开始事物
            hibTx = session.getTransaction();
            hibTx.setTimeout(timeout);
            hibTx.begin();
        } else
        {
            hibTx = session.beginTransaction();
        }
        txObject.getSessionHolder().setTransaction(hibTx);
        if(getDataSource() != null)
        {
        	//这里才是通过当前线程得到connection,
            java.sql.Connection con = session.connection();
            ConnectionHolder conHolder = new ConnectionHolder(con);
            if(timeout != -1)
                conHolder.setTimeoutInSeconds(timeout);
            if(logger.isDebugEnabled())
                logger.debug((new StringBuilder("Exposing Hibernate transaction as JDBC transaction [")).append(con).append("]").toString());
            //把connection绑定到当前线程
            TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
            //设置事物的connection
            txObject.setConnectionHolder(conHolder);
        }
        if(txObject.isNewSessionHolder())
        	//第一个事物肯定是新建的holder,这个holder与资源绑定再一起绑定到再次绑定到线程中来。
            TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
        //将sessionHolder设置成状态为已经事物开启了
        txObject.getSessionHolder().setSynchronizedWithTransaction(true);
    }
此时事物已经开始启动了,以后在DAO,或Facade中调用方法,要获得session和connection都将是一个对象,而这个connection也是绑定再session的,这用前面的观点就能证明是在一个事物中,

假如,在执行过程中又开启了一个事物,怎么办呢,继续往下看

protected Object doSuspend(Object transaction)
    {
		//挂起就是把当前事物的sessionHolder 设为空。
		//并将他们与资源对象解绑,也就是说把sessionHolder不与当前对象绑定,
		//把这么解绑的资源放在与线程绑定的SuspendedResourcesHolder这个对象上,此时,当前线程的session就为空了
        HibernateTransactionObject txObject = (HibernateTransactionObject)transaction;
        txObject.setSessionHolder(null);
        SessionHolder sessionHolder = (SessionHolder)TransactionSynchronizationManager.unbindResource(getSessionFactory());
        txObject.setConnectionHolder(null);
        ConnectionHolder connectionHolder = null;
        if(getDataSource() != null)
            connectionHolder = (ConnectionHolder)TransactionSynchronizationManager.unbindResource(getDataSource());
        return new SuspendedResourcesHolder(sessionHolder, connectionHolder, null);
    }
这个事物挂起,新开启的事物会继续执行上面的doGetTransaction,doBegin,当新的事物执行完成了后,个人估计是要先判断下是否有挂起的线程(原码太多了不好找),有就执行

protected void doResume(Object transaction, Object suspendedResources)
    {
        SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder)suspendedResources;
        if(TransactionSynchronizationManager.hasResource(getSessionFactory()))
            TransactionSynchronizationManager.unbindResource(getSessionFactory());
        TransactionSynchronizationManager.bindResource(getSessionFactory(), resourcesHolder.getSessionHolder());
        if(getDataSource() != null)
            TransactionSynchronizationManager.bindResource(getDataSource(), resourcesHolder.getConnectionHolder());
    }
然后执行dao,Facade里面的各个方法。
     以上是个人整理了几天的浅见,如果有不对的地方,希望同道中人,能帮忙点解下。



      

     









       

你可能感兴趣的:(java,web)