Transaction 浅析

来源:互联网 发布:nginx反向代理配置优化 编辑:程序博客网 时间:2024/05/22 05:58

一。先从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<Map<object object="">> resources= new ThreadLocal<Map</object><object object="">>();      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</object><object object="">());          }          resources.get().put(key, value);      }            public static Object getResource(Object key){                    return resources.get().get(key);      }            public static void clear(){          resources.remove();      }  }</object>

上面用了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)    {<span style="white-space:pre"></span>//挂起就是把当前事物的sessionHolder 设为空。<span style="white-space:pre"></span>//并将他们与资源对象解绑,也就是说把sessionHolder不与当前对象绑定,<span style="white-space:pre"></span>//把这么解绑的资源放在与线程绑定的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里面的各个方法。
     以上是个人整理了几天的浅见,如果有不对的地方,希望同道中人,能帮忙点解下。



      

     









       

1 0
原创粉丝点击