一、EJB的事務管理分如下兩個方面:
1、CMT(容器管理事務)
2、BMT(Bean管理事務)
二、CMT介紹:
容器管理事務主要是通過容器自動控制事務的開啟,提交和回滾,開發人員不需要手工編寫代碼,由容器來控制事務的邊界,一般來說是業務方法的開始
是事務的開啟,業務方法的結束是事務的提交部分,當程序遇到運行時異常,事務會自動回滾。如果遇到非運行時異常想要回滾事務的話可以用SessionCon
Text的setRollBackOnly()方法來達到目的。下面的例子是一個使用CMT的例子:(事務的控制我們一般放在Service層,而不是Dao層)
示例程序1:(參考publicEJB組件的NewhouseManagerImpl類)
@Stateless(name = "newhouseManager") //狀態定義實列Bean 提供遠程JNDI
@Remote(INewhouseManager.class)//定義遠程接口
@Local(INewhouseManager.class)//定義本地接口
@TransactionManagement(TransactionManagementType.CONTAINER) // 這里來定義是CMT還是BMT
public class NewhouseManagerImpl implements INewhouseManager{
@EJB(beanName = "newhouseDAO") //注入dao
private IGenericDAO<Newhouse, Integer> newhouseDAO;
@TransactionAttribute(TransactionAttributeType.REQUIRED) //這里來定義事務的傳播特性,如果調用該組件的客戶方已經開啟了事務則加入原事務,否則開啟一個新事務
public Newhouse save(Newhouse entity) {
LogUtil.log("saving Newhouse instance", Level.INFO, null);
try {
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); // 這里設置path為null的話,會出現運行時異常,事務會回滾,entity1和entity2將不會插入到public庫的newhouse表中
newhouseDAO.create(entity2);
} catch (RuntimeException re) {
LogUtil.log("save failed", Level.SEVERE, re);
re.printStackTrace();
return null;
}
PS:這里要特別注意NewhouseDAO這個類,需要添加@TransactionAttribute(TransactionAttributeType.REQUIRED)這個注解,我們項目里面原來的事務傳播屬性
設置的是REQUIRES_NEW,這樣會將原來的事務掛起,然后開啟一個新的事務。
三、BMT介紹
BMT主要是通過手工編程來實現事務的開啟、提交和回滾,相對於CMT來說雖然增加了工作量,但是控制力度更細,而且更加靈活,我們可以再出現異常的時候回滾事務
,也可以通過JMS返回或者遠程調用返回值來控制事務的回滾或提交;使用BMT需要用到UserTransaction這個類的實例來實現事務的begin、commit和rollback,可以通過
Ejb注解的方式獲得這個類實例,也可以用EJBContext.getUserTransaction來獲得。下面是一個使用BMT的例子:
示例程序2:
@Stateless(name = "newhouseManager") //狀態定義實列Bean 提供遠程JNDI
@Remote(INewhouseManager.class)//定義遠程接口
@Local(INewhouseManager.class)//定義本地接口
@TransactionManagement(TransactionManagementType.BEAN) //設置為BMT事務
public class NewhouseManagerImpl implements INewhouseManager{
@Resource
private UserTransaction ut; //注入UserTransaction
@EJB(beanName = "newhouseDAO") //注入dao
private IGenericDAO<Newhouse, Integer> newhouseDAO;
@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);
newhouseDAO.create(entity2);
ut.commit();
} catch(RuntimeException e) {
ut.rollBack();
}.........................
..........................
........................
}
測試效果和示例程序1一樣
四、跨多個數據庫的事務控制(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
在<properties depends="arjuna" name="jta">這個節點下,添加一個子節點<property name="com.arjuna.ats.jta.allowMultipleLastResources" value="true"/>
實例程序3:
@Stateless(name = "newhouseManager") //狀態定義實列Bean 提供遠程JNDI
@Remote(INewhouseManager.class)//定義遠程接口
@Local(INewhouseManager.class)//定義本地接口
@TransactionManagement(TransactionManagementType.BEAN) //設置為BMT事務
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的記錄也不會刪除
}.........................
..........................
........................
}