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
- Transaction 浅析
- Transaction
- Transaction
- Transaction
- transaction
- Transaction
- Transaction
- Transaction
- Transaction
- @Transaction
- transaction
- Transaction
- 1008 transaction transaction transaction
- HDU6210 transaction transaction transaction
- hdu6201 transaction transaction transaction
- transaction transaction transaction HDU
- hdu6201 transaction transaction transaction
- HDU6201 transaction transaction transaction
- LatinIME默认打开各国语言
- AS3中如何修改注册点
- MemoryStream类
- struts(17)注释
- C# asp.net与jquery做ajax简单进度条
- Transaction 浅析
- soapui + groovy 接口自动化测试 第一章
- android httpClient 支持HTTPS的2种处理方式
- Vim命令合集
- 收集的48个Shell脚本小技巧
- Crontab的格式
- Jedis使用总结【pipeline】【分布式的id生成器】【分布式锁【watch】【multi】】【redis分布式】
- 链表的倒数第k个节点
- Struts(16)异常处理