由於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事務管理。