`
fengzl
  • 浏览: 216719 次
  • 性别: Icon_minigender_1
  • 来自: 宁波
社区版块
存档分类
最新评论

TransactionManager

阅读更多
您有没有想过,为什么会有六种事务划分属性(NotSupported、Required、Supports、RequiresNew、Mandatory和Never)?这六种都是由容器托管事务 (CMT)的bean来支持的,但如果使用的是bean托管事务(BMT),EJB规范所能提供的功能难道就只有通过UserTransaction接口启动和提交/回滚事务吗?显然,CMT模型好像更强大,比如,BMT不能使当前事务挂起然后恢复,这就意味着在BMT bean中无法仿真RequiresNew和NotSupported划分,至少是在使用UserTransaction接口时。

  虽然EJB规范并没有解释为什么会存在以上所提到的不对称情况,但是在BMT模型中依然有一种用来使事务挂起然后恢复的合法方式。如果曾经研究过javax.transaction包的内容,您可能会注意到,与UserTransaction接口一起的还有一个TransactionManager接口,它看起来就像一个扩展的UserTransaction:同样的方法——begin()、commit()和rollback(),再加上suspend()和resume()。

  如果能从EJB中得到一个TransactionManager实现,我们就可以实现编程式地使事务挂起然后恢复的目标。虽然J2EE 1.3和EJB 2.0规范都未提到TransactionManager的可用性,但它们也都没有明确表示禁止使用它。此外,对于CMT事务划分,容器是从内部使用Java Transaction API (JTA),因此,我们几乎可以100%地肯定:TransactionManager是存在的,惟一的问题只是在代码中获得对它的引用。

  在这篇文章中,我们将了解如何利用几个流行的容器来获得一个TransactionManager,以及如何用它来扩展bean托管事务的功能,使它们和容器托管事务一样强大。我们也将简述一些涉及使用这些高级功能的风险,在文章的结尾,我们还将探讨如何在流行的Spring框架中使用TransactionManager。
在各种J2EE服务器中获得TransactionManager的引用

  J2EE和EJB规范没有描述任何获得TransactionManager引用的标准方法,每个J2EE容器供应商可以随意将其放置在任何地方,甚至不需提供任何机制,就可以从应用程序代码中对它进行访问。但在实践中,如今所有的容器都有获取它的机制。以下是一些如何从最流行的J2EE容器获得TransactionManager引用的例子。
抛出一个UserTransaction (WebLogic、Orion、OC4J)

  任何一个兼容J2EE的容器都必须使UserTransaction对象在JNDI中的java:comp/UserTransaction下可用。因为UserTransaction接口是TransactionManager的子集,所以一些J2EE容器供应商选择为它们提供一种通用的实现。WebLogic 8、Orion 2和Oracle的OC4J EJB3预览版都是这种方法的例子。在这些容器中,只要从JNDI中获得一个UserTransaction对象,再把它转到TransactionManager,就可以获得对TransactionManager的引用。这可能是最简单的一种情况。

private TransactionManager getFromUserTransaction() 
        throws Exception {
    InitialContext ctx = new InitialContext();
    UserTransaction ut = (UserTransaction)
        ctx.lookup("java:comp/UserTransaction");
    if (ut instanceof TransactionManager) {
        log("UserTransaction also TransactionManager");
        return (TransactionManager)ut;
    }
    return null;
}


直接从JNDI中获取TransactionManager (JBoss、WebLogic)

  在JBoss 3和WebLogic 8中,可从JNDI获取TransactionManager(虽然名称不一样),因此可以通过简单的查找而获得:

private TransactionManager getFromJNDI() 
        throws Exception {
    InitialContext ctx = new InitialContext();
    try {
        // WebLogic
        return (TransactionManager)
            ctx.lookup("javax.transaction.TransactionManager");
     }
     catch (Exception e) {  }

    try {
        // JBoss
        return (TransactionManager)
            ctx.lookup("java:/TransactionManager");
    }
    catch (Exception e) { }
    return null;
}


从一个定制的工厂获取TransactionManager (Websphere)

  在WebSphere 4/5/6中,TransactionManager的引用要从工厂类中获取。但是,麻烦的是,工厂类的名称随WebSphere版本的不同而有所改变。

public TransactionManager getFromWebsphereFactory() 
        throws Exception {
     try {
        // WebSphere 5.1 or 6.0
        return 
            com.ibm.ws.Transaction.TransactionManagerFactory
                .getTransactionManager();
    }
    catch (ClassNotFoundException ex) {}
        
    try {
        // WebSphere 5.0
        return 
            com.ibm.ejs.jts.jta.TransactionManagerFactory
                .getTransactionManager();
    }
    catch (ClassNotFoundException ex) {}
        
    try {
        // WebSphere 4.0
        com.ibm.ejs.jts.jta.JTSXA..getTransactionManager();
    }
    catch (ClassNotFoundException ex) { }

    return null;
}


  在WebLogic 7/8/9中,对TransactionManager的引用可以通过在Weblogic 7的TxHelper中定义的静态方法getTransactionManager()而获得。该类在WebLogic 8中被否决了,而用TransactionHelper取而代之。

public TransactionManager getFromWebLogicFactory() 
    throws Exception {
     try {
        // WebLogic 8/9
        return 
            weblogic.transaction.TransactionHelper
                .getTransactionManager();
    }
    catch (ClassNotFoundException ex) {}
        
    try {
        // WebLogic 7
        return 
            weblogic.transaction.TxHelper
                .getTransactionManager();
    }
    catch (ClassNotFoundException ex) {}
        
    return null;
}


使用TransactionManager

  一旦成功地获得TransactionManager引用,就可以用它来挂起和恢复事务,正如以下的示例代码所示。

...
// obtain UserTransaction object and start transaction
InitialContext ctx = new InitialContext();
UserTransaction userTransaction = (UserTransaction)
    ctx.lookup("java:comp/UserTransaction");

// start first transaction
userTransaction.begin();
            
// obtain TransactionManager 
// using one of the methods described above
TransactionManager tm = getTransactionManager();

// suspend transaction
// suspend() returns reference to suspended
// Transaction object which later should be passed
// to resume()
Transaction transaction = tm.suspend();
                
// here you can do something outside of transaction
// or start new transaction, 
// do something and then commit or rollback
userTransaction.begin();
    
// commit subtransaction
userTransaction.commit();

// resume suspended transaction
tm.resume(transaction);

// commit first transaction
userTransaction.commit();
...


正如您所看到的,在TransactionManager接口的帮助下,可以对UserTransaction所提供的标准功能进行扩展,在BMT代码中实现与CMT bean相同的灵活性水平。

  需要知道的是,当事务被挂起时,并不意味着事务的计时器停止了。换言之,如果把事务的超时设定为30秒,而事务已挂起了20秒并恢复了,那么该事务只剩10秒就要到达超时了。事务的挂起会解除事务与正在运行的线程之间的关联,然后resume()调用会再次将其关连,而不影响事务超时计时器。
已知问题

  因为J2EE规范并不要求TransactionManager在J2EE容器中的可用性以及功能(虽然从底层的JTA基础架构中我们知道它应该是存在的),所以有些应用服务器中存在一些问题。例如,在WebLogic 7、8以及9(beta版)中有种特别奇怪的现象:假如一个事务被标记为回滚(通过调用UserTransaction.setRollbackOnly ()),然后被挂起,当被挂起的事务尝试恢复时,它将会出现如下的提示:

  javax.transaction.InvalidTransactionException: Attempt to resume an inactive transaction

  以下代码说明了该行为:

...
// obtain UserTransaction object and start transaction
InitialContext ctx = new InitialContext();
UserTransaction userTransaction = (UserTransaction)
    ctx.lookup("java:comp/UserTransaction");
userTransaction.begin();

// mark for rollback
userTransaction.setRollbackOnly();
            
TransactionManager tm = getTransactionManager();

// suspend transaction
Transaction transaction = tm.suspend();
                
// resume suspended transaction
// this call will fail with InvalidTransactionException
//  in WebLogic
tm.resume(transaction);

...

  幸运的是,对于该问题,有一个应急方案。WebLogic的TransactionManager实现连同标准的resume (Transaction transaction)方法,有一个可用于替代的forceResume方法。以下的代码展示了一种在WebLogic中运行代码时需要用到的模式。注意,在这种情况下,应该把对TransactionManager的引用转换到WebLogic的定制实现接口(WebLogic 7中的weblogic.transaction.TransactionManager或WebLogic 8以及更高版本中的weblogic.transaction.ClientTransactionManager)。

...
// obtain UserTransaction object and start transaction
InitialContext ctx = new InitialContext();
UserTransaction userTransaction = (UserTransaction)
    ctx.lookup("java:comp/UserTransaction");
userTransaction.begin();

// mark for rollback
userTransaction.setRollbackOnly();
            
TransactionManager tm = getTransactionManager();

// suspend transaction
Transaction transaction = tm.suspend();
                
// resume suspended transaction
try {
    // first try standard JTA call
    tm.resume(transaction);
}
catch (InvalidTransactionException e) {
    // standard method failed, try forceResume()
    if (tm instanceof 
        weblogic.transaction.ClientTransactionManager) {
        // WebLogic 8 and above
        ((weblogic.transaction.ClientTransactionManager)tm)
            .forceResume(transaction);
    }    
    else if (tm instanceof 
        weblogic.transaction.TransactionManager) {
        // WebLogic 7
        ((weblogic.transaction.TransactionManager)tm)
            .forceResume(transaction)
    }
    else {
        // cannot resume
        throw e;
    }
}

...

TransactionManager在Spring Framework中的用法

  在流行的Spring framework中,以上所描述的技术都被广泛地用于事务代理。应该注意的是,每次使用PROPAGATION_REQUIRES_NEW或PROPAGATION_NOT_SUPPORTED、事务属性以及JtaTransactionManager来配置Spring的TransactionProxyFactoryBean时, Spring就会用JTA的TransactionManager来挂起和恢复事务。Spring太智能了,但有时太智能了反而使我无法接受——甚至没有指定,它就会发现容器中的TransactionManager。例如,当在一个Spring应用程序上下文中定义 JtaTransactionManager时,就可以为UserTransaction提供一个JNDI名,而如果UserTransaction也实现了它,它就将“自动检测”TransactionManager。如上所示,这对WebLogic、Orion和Oracle OC4J都适用。

  有时,这可能并不是我们所想要的,尤其是当您想要严格遵循J2EE/EJB规范,并确保跨所有J2EE容器的完全可移植性的时候。正如我们所看到的,有时候编程式的事务挂起和恢复可能存在一些问题。虽然Spring知道如何绕过这些问题,至少是对于上面所描述的问题,即,在Welogic中,在挂起之前将事务标记为回滚。在这种情况下,可以在配置JtaTransactionManager时把 autodetectTransactionManager属性设定为false。如果这么做了,那么任何使用 PROPAGATION_REQUIRES_NEW或PROPAGATION_NOT_SUPPORTED事务属性的尝试都会抛出 TransactionSuspensionNotSupportedException而失败。但PROPAGATIO-REQUIRED、 PROPAGATION-SUPPORTS、PROPAGATION-MANDATORY以及PROPAGATION-NEVER应该会正常运行。这对应于JTA Usertransaction所提供的功能,而且在任一个兼容J2EE的容器中都起作用。

  以下是Spring应用程序上下文中的事务管理器定义,其中禁用了TransactionManager自动检测:
<bean id="transactionManager" class=
"org.springframework.transaction.jta.JtaTransactionManager">
  <property name="userTransactionName">
    <value>javax.transaction.UserTransaction</value>
  </property>
  <property name="autodetectTransactionManager">
    <value>false</value>
  </property>
</bean>


结束语

  J2EE规范不要求对JIA TransactionManager接口的支持。但由于J2EE使用JTA作为它的底层事务基础架构,所以几乎所有的J2EE服务器都把它公开为 J2EE的扩展。存在一些已知的兼容性问题,而在某些情况下,可以用特定于容器的代码来绕过这些问题。但是,如果需要实现编程式事务挂起,则J2EE中的 TransactionManager是一个强大的特性。只需首先在所选择的J2EE服务器中检测一下它是否可用就可以了。
分享到:
评论
3 楼 waitgod 2014-11-27  
Good!@!!!
2 楼 duan15131926982 2014-10-22  
对事物的概念理解的还不是太透彻,不过感觉资料还是不错的,
1 楼 java2mocca 2010-07-22  
真是不错,帮我解除了心中的迷惑

相关推荐

    2771885 - TransactionManager is not ready

    标题:"2771885 - TransactionManager is not ready" 指的可能是SAP系统中的一则官方技术支持文档编号(SAP Note),它描述了与SAP系统中 TransactionManager 服务有关的技术问题。在SAP系统中,TransactionManager...

    开源项目-Code-Hex-sqlx-transactionmanager.zip

    总结起来,Code-Hex-sqlx-transactionmanager 是一个增强型的 Go 语言数据库事务管理工具,它简化了基于 sqlx 的应用程序中事务的处理流程,提高了代码的可读性和可靠性。通过使用这个库,开发者可以更高效地管理...

    springboot整合mybatis配置多数据源

    Spring Boot默认使用JpaTransactionManager,但这里我们需要MyBatis的SqlSessionTemplate,所以使用`@EnableTransactionManagement`和`@Bean`创建MyBatis的TransactionManager。 6. **Mapper配置** 需要为每个数据...

    Mybatis中文版教程非常详细.适合初学者

    &lt;bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"&gt; ``` ##### 4.2 容器管理事务 除了手动管理事务外,还可以利用Spring AOP(面向切面编程)特性实现...

    ssh(structs,spring,hibernate)框架中的上传下载

    Struts+Spring+Hibernate实现上传下载    本文将围绕SSH文件上传下载的主题,向您详细讲述如何开发基于SSH的Web程序。SSH各框架的均为当前最新版本:  •Struts 1.2  •Spring 1.2.5  •Hibernate 3.0 ...

    mysql多数据源连接代码

    JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(entityManagerFactory1); return transactionManager; } @Bean(name = ...

    Spring事务配置的五种方式.doc

    Spring框架中事务配置是非常重要的一部分,通常由三个组成部分组成,即DataSource、TransactionManager和代理机制。无论采取何种配置方式,代理机制部分总是变化的,而DataSource和TransactionManager部分则根据数据...

    浅谈SpringBoot之事务处理机制

    JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setDataSource(dataSource()); return transactionManager; } 二、声明式事务 Spring支持声明式事务,即使用...

    Spring+SpringMVC+Mybatis框架整合例子(SSM) 下载

    2. 配置Spring:创建Spring的配置文件,如applicationContext.xml,配置Bean、DataSource、TransactionManager等。 3. 配置SpringMVC:创建SpringMVC的配置文件,如servlet-context.xml,配置DispatcherServlet、...

    09-4编程式事务管理.rar

    &lt;property name="transactionManager" ref="transactionManager" /&gt; ``` 这样,MyBatis的SqlSession操作就会被Spring的事务管理所包围,实现事务的自动提交或回滚。 总之,编程式事务管理是Spring提供的一个强大...

    spring配置多数据源jdbc

    JpaTransactionManager transactionManager = new JpaTransactionManager(); transactionManager.setEntityManagerFactory(primaryEntityManagerFactory().getObject()); return transactionManager; } @Bean...

    springmybatis

    &lt;transactionManager type="JDBC"/&gt; 3. 建立与数据库对应的 java class,以及映射文件. 在src_user下建立package:com.yihaomen.mybatis.model ,并在这个 package 下建立 User 类: 程序...

    详解Spring配置事务的五种方式

    5. **Spring Boot的自动配置**:在Spring Boot项目中,通过@EnableTransactionManagement和适当的依赖,Spring Boot能自动配置DataSource和TransactionManager,并且可以通过@Transactional注解进行事务控制。...

    Spring的事务配置的五种方式

    在Spring中,事务管理通常分为三部分:DataSource、TransactionManager和代理机制。DataSource是数据源,TransactionManager是事务管理器,而代理机制则是实现事务控制的关键。 1. **基于XML的AOP代理配置** 这是...

    spring 事务配置

    &lt;property name="transactionManager" ref="transactionManager"/&gt; *"&gt;PROPAGATION_REQUIRED ``` 这里定义了一个名为`transactionManager`的事务管理器,并将其与Hibernate会话工厂关联起来。接下来...

    Spring事务配置的五种方式

    &lt;property name="transactionManager" ref="transactionManager"/&gt; *"&gt;PROPAGATION_REQUIRED ``` 2. **使用AOP代理(基于XML)** 这种方式利用Spring的AOP功能,通过切面定义事务策略。事务切面...

    spring事务配置的五种方式

    &lt;property name="transactionManager" ref="transactionManager"/&gt; &lt;!--配置事务属性--&gt; *"&gt;PROPAGATION_REQUIRED ``` 在这个例子中,`userDao` Bean被配置为一个事务代理,所有的方法调用都会被...

    spring事务配置的5中方式

    在Spring中,事务配置主要涉及到三个核心组件:DataSource、TransactionManager和代理机制。下面将详细介绍Spring的五种事务配置方式。 1. **基于XML的事务配置** - **每个Bean都有一个代理**: 在这种配置方式中...

    spring事务与配置

    &lt;property name="transactionManager" ref="transactionManager"/&gt; &lt;!--配置事务属性--&gt; *"&gt;PROPAGATION_REQUIRED,-Exception ``` 这里的配置使用了`TransactionProxyFactoryBean`来创建事务代理。...

Global site tag (gtag.js) - Google Analytics