IBATIS事務處理
iBATIS事務處理是和Dao緊密相聯的。
在使用Dao時,如以下程式碼,先插入新記錄,再進行更新:
UserDao.insertUser (user); // Starts transaction
user.setName("wh");
UserDao.updateUser (user); // Starts a new transaction
因為沒有顯式地啟動事務,iBatis會認為這是兩次事務,分別從連線池中取兩次Connection。我們所寫的Dao子類(繼承自com.ibatis.dao.client.template.SqlMapDaoTemplate)的每一個Dao方法已經預設為一個事務(通過動態代理)。
iBatis是通過DaoManager類來統管Dao子類的事務,
眾Dao子類由DaoManager產生,如下:
Reader reader =Resources.getResourceAsReader("dao.xml");
DaoManager daoManager =DaoManagerBuilder.buildDaoManager(reader);
UserDao userDao = (UserDao) daoManager.getDao(UserDao.class);
UserDao是使用者自己定義的介面,獲得的其實是在dao.xml中指定的相對應的 SqlMapDao實現類,從而實現了鬆藕合。在良好的分層設計中,
但是一般的事務需要放到業務層,因為一個業務需要具有原子性,事務放到Dao層是不能達到業務一致的效果的,那麼如果想要把事務放到業務層,就需要在業務層使用顯示事務進行宣告處理.
顯式地宣告事務處理語句,如下:
try {
daoManager.startTransaction();
UserDao.insertUser (user);
user.setName("wh");
UserDao.updateUser(user);
otherDao.doSomething(other);
...
daoManager.commitTransaction();
} finally {
daoManager.endTransaction();
}
這樣就保持了原子性,整體為一個事務,要麼全部執行成功,否則回滾。
現在唯一的問題就是,dao層的事務是否已經放棄,否則產生事務巢狀問題對效能會有影響.
當然,iBatis 完全可以這麼做:建一個宣告式介面:IService,再使用動態代理,將使用者自己的Serivce子類通過動態代理自動包上事務處理的程式碼,預設每一個業務方法為一個事務。
大師的心如果能輕易揣測,就是不大師了:),估計大師認為這樣屬於過度設計,他認為把這種靈活性交給使用者是合適的,相當多的service 方法只調用一個Dao方法,例如CRUD操作。
再補充一下,iBatis中對事務的處理是可配置的,最常用的Type是"JDBC",也可以宣告為"JTA"或"EXTERNAL".
專案裡常用Spring與Ibatis配合使用,這樣可以在Spring裡配置事務管理,可以省去業務層的顯示事務程式碼.
spring的配置檔案的基本寫法為:
<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource" >
<property name="driverClassName"value="com.mysql.jdbc.Driver" />
<property name="url"value="jdbc:mysql://10.11.0.145:3306/carrefour?characterEncoding=gb2312"/>
<property name="username" value="dev01" />
<property name="password" value="123456"/>
</bean>
<bean id="sqlClient"class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="dataSource">
<ref local="dataSource" />
</property>
<property name="configLocation">
<value>classpath:sqlmaps.xml</value>
</property>
</bean>
<!--配置事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<propertyname="dataSource" ref="dataSource"></property>
</bean>
<!--配置哪些方法,什麼情況下需要回滾-->
<tx:advice id="serviceAdvice"transaction-manager="transactionManager">
<tx:attributes>
<!--當代理的service層中的方法丟擲異常的時候才回滾,必須加rollback-for引數-->
<tx:methodname="insert*" propagation="REQUIRED"rollback-for="Throwable"/>
<tx:methodname="del*" propagation="REQUIRED"rollback-for="Throwable"/>
<tx:methodname="update*" propagation="REQUIRED"rollback-for="Throwable"/>
<!--除了上面標識的方法,其他方法全是隻讀方法-->
<tx:methodname="*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置哪些類的方法需要進行事務管理 -->
<aop:config proxy-target-class="true">
<aop:pointcut id="servicePointcut"expression="execution(* com.wh.service.*.*(..))"/>
<aop:advisor pointcut-ref="servicePointcut"advice-ref="serviceAdvice"/>
</aop:config>
這是在spring+Ibatis的情況下, ,通過aop控制需要事務的包和具體方法,將事務控制在service層,來達到事務在業務層提交和回滾.保持業務的原子性.