由于CMT依靠容器开始、提交和回滚事务,所以会限制事务的边界位置,并且CMT不支持嵌套事务,Bean管理事务支持嵌套事务,所以需要嵌套事务时可以采用Bean管理事务。同时,BMT允许通过编程的方式来指定事务的开始、提交和回滚的位置。主要使用的是javax.transaction.UserTransaction接口。
BMT事务
如下面代码:
Java代码
@Stateless)
@TransactionManagement(TransactionManagementType.BEAN)
public class OrderManagerBean {
@Resource
private UserTransaction userTransaction;
public void placeSnagItOrder(Item item, Customer customer){
try {
userTransaction.begin();
if (!bidsExisting(item)){
validateCredit(customer);
chargeCustomer(customer, item);
removeItemFromBidding(item);
}
userTransaction.commit();
} catch (CreditValidationException cve) {
userTransaction.rollback();
} catch (CreditProcessingException cpe){
userTransaction.rollback();
} catch (DatabaseException de) {
userTransaction.rollback();
} catch (Exception e) {
e.printStackTrace();
}
}
}
说明:@TransactionManagement(TransactionManagementType.BEAN) 指定了事务的类型为BMT,上面没有用到@TransactionAttribute,因为它只适用于CMT,在上面代码中,可以显示的指定事务的开始与提交,因此更加的灵活。上面最关键的地方是注入了UserTransaction资源。其中获得UserTransaction资源的方式有三种,除了用EJB资源的方式注入以外,还有以下两种方式:
(1) JNDI查找
Java代码
Context context = new InitialContext();
UserTransaction userTransaction =
(UserTransaction) context.lookup(“java:comp/UserTransaction”);
userTransaction.begin();
// Perform transacted tasks.
userTransaction.commit();
如果在EJB之外,则可使用此方法,如在不支持依赖注入的JBoss4.2.2的Web工程或helper class即帮助器类中都可以。
(2)EJBContext
Java代码
@Resource
private SessionContext context;
...
UserTransaction userTransaction = context.getUserTransaction(); userTransaction.begin();
// Perform transacted tasks.
userTransaction.commit();
注意:getUserTransaction方法只能在BMT中使用。如果在CMT中使用,则会抛出IllegalStateException异常。且在BMT中不能使用EJBContext的getRollbackOnly和setRollbackOnly方法,如果这样使用,也会抛出IllegalStateException异常。
跨多个数据库的事务控制(JTA事务)
如果想要在一个事务操作中控制多个数据库的操作,需要如下两步操作:
1、需要设置persistence.xml里面的datasouce支持jta事务,另外设置transaction-type为jta,如下所示
<persistence-unit name="public_master" transaction-type="JTA">
<jta-data-source>java:/public_master_db</jta-data-source>
<properties>
<property name="hibernate.dialect" value="com.jiwu.core.utils.BlobMySQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="false" />
</properties>
</persistence-unit>
<persistence-unit name="build_master" transaction-type="JTA">
<jta-data-source>java:/build_master_db</jta-data-source>
<property name="hibernate.dialect" value="com.jiwu.core.utils.BlobMySQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update"/>
<property name="hibernate.show_sql" value="false" />
</properties>
</persistence-unit>
2、修改jboss安装目录下的\server\default\conf\jbossjta-properties.xml
在这个节点下,添加一个子节点:
<property name="com.arjuna.ats.jta.allowMultipleLastResources" value="true"/>
java代码:
//状态定义实列Bean 提供远程JNDI
@Stateless(name = "newhouseManager")
@Remote(INewhouseManager.class)//定义远程接口
@Local(INewhouseManager.class)//定义本地接口
//设置为BMT事务
@TransactionManagement(TransactionManagementType.BEAN)
public class NewhouseManagerImpl implements INewhouseManager{
@Resource
private UserTransaction ut; //注入UserTransaction
@EJB(beanName = "newhouseDAO") //注入dao
private IGenericDAO<Newhouse, Integer> newhouseDAO;
@EJB(beanName = "buildDAO") //注入houseDAO测试
private IGenericDAO<Build, Integer> buildDAO;
@TransactionAttribute(TransactionAttributeType.REQUIRED) //设置事务的传播特性为required
public Newhouse save(Newhouse entity) {
LogUtil.log("saving Newhouse instance", Level.INFO, null);
try {
ut.begin();
LogUtil.log("save successful", Level.INFO, null);
entity.setBname("测试1:" + new Date());
newhouseDAO.create(entity);
Newhouse entity2 = new Newhouse();
entity2.setBname("测试2");
entity2.setPath(null);
//这里不设置为null的话,不出现异常,entity1和entity2会插入到public库的newhouse表中,另外build库的build表中id为2的被删除
newhouseDAO.create(entity2);
buildDAO.delete(2);
ut.commit();
} catch(RuntimeException e) {
ut.rollBack(); //出现异常,entity1和entity2不会插入到public库的newhouse表中,另外build库的build表中id为2的记录也不会删除
}
}
“`
总结:
如果使用有状态的Session Bean且需要跨越方法调用维护事务,那么BMT是你唯一的选择,当然BMT这种技术复杂,容易出错,且不能连接已有的事务,当调用BMT方法时,总会暂停已有事务,极大的限制了组件的重用。故优先考虑CMT事务管理。