本文配套示例代碼下載地址(完整可運行,含sql文件,下載後請修改數據庫配置):http://download.csdn.net/detail/daijin888888/9567096html
1、事務的做用
將若干的數據庫操做做爲一個總體控制,一塊兒成功或一塊兒失敗。
原子性:指事務是一個不可分割的工做單位,事務中的操做要麼都發生,要麼都不發生。
一致性:指事務先後數據的完整性必須保持一致。
隔離性:指多個用戶併發訪問數據庫時,一個用戶的事務不能被其餘用戶的事務所幹擾,多個併發事務之間數據要相互隔離。
持久性:指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,即時數據庫發生故障也不該該對其有任何影響。java
2、Spring事務管理高層抽象主要包括3個接口
--Platform TransactionManager 事務管理器(提交、回滾事務)
Spring爲不一樣的持久化框架提供了不一樣的Platform TransactionManager接口實現。如:
使用Spring JDBC或iBatis進行持久化數據時使用DataSourceTransactionManager
使用Hibernate3.0版本進行持久化數據時使用HibernateTransactionManager
--TransactionDefinition 事務定義信息(隔離、傳播、超時、只讀)
髒讀:一個事務讀取了另外一個事務改寫但還未提交的數據,若是這些數據被回滾,則讀到的數據是無效的。
不可重複讀:在同一事務中,屢次讀取同一數據返回的結果有所不一樣。
幻讀:一個事務讀取了幾行記錄後,另外一個事務插入一些記錄,幻讀就發生了。再後來的查詢中,第一個事務就會發現有些原來沒有的記錄。
事務隔離級別:(五種)spring
- DEFAULT--使用後端數據庫默認的隔離級別(Spring中的選擇項)
- READ_UNCOMMITED--容許你讀取還未提交的改變了的數據。可能致使髒、幻、不可重複讀
- READ_COMMITTED--容許在併發事務已經提交後讀取。可防止髒讀,但幻讀和不可重複讀仍可發生
- REPEATABLE_READ--對相同字段的屢次讀取是一致的,除非數據被事務自己改變。可防止髒、不可重複讀,但幻讀仍可能發生
- SERIALIZABLE--徹底服從ACID的隔離級別,確保不發生髒、幻、不可重複讀。這在全部的隔離級別中是最慢的,它是典型的經過徹底鎖定在事務中涉及的數據表來完成的
其中,MySQL默認採用REPEATABLE_READ隔離級別;Oracle默認採用READ_COMMITTED隔離級別
事務傳播行爲:(七種)sql
- REQUIRED--支持當前事務,若是當前沒有事務,就新建一個事務。這是最多見的選擇。
- SUPPORTS--支持當前事務,若是當前沒有事務,就以非事務方式執行。
- MANDATORY--支持當前事務,若是當前沒有事務,就拋出異常。
- REQUIRES_NEW--新建事務,若是當前存在事務,把當前事務掛起。
- NOT_SUPPORTED--以非事務方式執行操做,若是當前存在事務,就把當前事務掛起。
- NEVER--以非事務方式執行,若是當前存在事務,則拋出異常。
- NESTED--若是當前存在事務,則在嵌套事務內執行。若是當前沒有事務,則進行與REQUIRED相似的操做。擁有多個能夠回滾的保存點,內部回滾不會對外部事務產生影響。只對DataSourceTransactionManager有效
--TransactionStatus 事務具體運行狀態數據庫
3、Spring提供瞭如下方法控制事務
a.編程式事務管理(基於Java編程控制,不多使用)--見demo1包
利用TransactionTemplate將多個DAO操做封裝起來
*b.聲明式事務管理(基於Spring的AOP配置控制)
-基於TransactionProxyFactoryBean的方式.(不多使用)--見demo2包
須要爲每一個進行事務管理的類,配置一個TransactionProxyFactoryBean進行加強.
-基於XML配置(常用)--見demo3包
一旦配置好以後,類上不須要添加任何東西。
若是Action做爲目標對象切入事務,須要在<aop:config>元素裏添加proxy-target-class="true"屬性。緣由是通知Spring框架採用CGLIB技術生成具備事務管理功能的Action類。
-基於註解(配置簡單,常用)--見demo4包
在applicationContext.xml中開啓事務註解配置。(applicationContext.xml中只需定義Bean並追加如下元素)
<bean id="txManager" class="...">
<property name="sessionFactory">
</property>
<tx:annotation-driven transaction-manager="txManager"/>express
在目標組件類中使用@Transactional,該標記可定義在類前或方法前。編程
4、示例(銀行轉帳)
--編程式後端
[java] view plain copysession
- /**
- * @Description:轉帳案例的DAO層接口
- *
- */
- public interface AccountDao {
- /**
- * @param out
- * :轉出帳號
- * @param money
- * :轉帳金額
- */
- public void outMoney(String out, Double money);
-
- /**
- *
- * @param in
- * :轉入帳號
- * @param money
- * :轉帳金額
- */
- public void inMoney(String in, Double money);
- }
[java] view plain copy併發
- /**
- * @Description:轉帳案例的DAO層實現類
- */
- public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
- /**
- * @param out
- * :轉出帳號
- * @param money
- * :轉帳金額
- */
- @Override
- public void outMoney(String out, Double money) {
- String sql = "update account set money = money-? where name = ?";
- this.getJdbcTemplate().update(sql, money, out);
- }
- /**
- * @param in
- * :轉入帳號
- * @param money
- * :轉帳金額
- */
- @Override
- public void inMoney(String in, Double money) {
- String sql = "update account set money = money+? where name = ?";
- this.getJdbcTemplate().update(sql, money, in);
- }
- }
[java] view plain copy
- /**
- * @Description:轉帳案例的業務接口
- *
- */
- public interface AccountService {
- /**
- * @param out :轉出帳號
- * @param in :轉入帳號
- * @param money :轉帳金額
- */
- public void transfer(String out,String in,Double money);
- }
[java] view plain copy
- /**
- * @Description:轉帳案例的業務層實現類
- */
- public class AccountServiceImpl implements AccountService {
- // 注入轉帳的DAO
- private AccountDao accountDao;
-
- // 注入事務管理的模板
- private TransactionTemplate transactionTemplate;
-
- /**
- * @param out
- * :轉出帳號
- * @param in
- * :轉入帳號
- * @param money
- * :轉帳金額
- */
- @Override
- public void transfer(final String out, final String in, final Double money) {
-
- // 未經事務控制的業務處理操做,若是過程當中出異常,則致使前面的操做能完成,後面的不能,即轉帳成功但未收到轉帳款
- // accountDao.outMoney(out, money);
- // int i = 1/0;
- // accountDao.inMoney(in, money);
-
- transactionTemplate.execute(new TransactionCallbackWithoutResult() {
-
- @Override
- protected void doInTransactionWithoutResult(
- TransactionStatus transactionStatus) {
- accountDao.outMoney(out, money);
- // int i = 1 / 0;//事務控制,即出現異常,該段內代碼都執行失效
- accountDao.inMoney(in, money);
- }
- });
- }
-
- public void setAccountDao(AccountDao accountDao) {
- this.accountDao = accountDao;
- }
-
- public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
- this.transactionTemplate = transactionTemplate;
- }
- }
applicationContext1.xml
[html] view plain copy
- <!-- 引入外部的屬性文件 -->
- <context:property-placeholder location="classpath:jdbc.properties"/>
-
- <!-- 配置c3p0鏈接池 -->
- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
- <property name="driverClass" value="${jdbc.driverClass}" />
- <property name="jdbcUrl" value="${jdbc.url}" />
- <property name="user" value="${jdbc.username}" />
- <property name="password" value="${jdbc.password}" />
- </bean>
-
- <!-- 配置業務層類 -->
- <bean id="accountService" class="com.zs.spring.demo1.AccountServiceImpl">
- <property name="accountDao" ref="accountDao" />
- <!-- 注入事務管理的模板 -->
- <property name="transactionTemplate" ref="transactionTemplate" />
- </bean>
-
- <!-- 配置DAO類(簡化,會自動配置JdbcTemplate) -->
- <bean id="accountDao" class="com.zs.spring.demo1.AccountDaoImpl">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <!-- 配置DAO類(未簡化) -->
- <!-- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
- <property name="dataSource" ref="dataSource" />
- </bean>
- <bean id="accountDao" class="com.zs.spring.demo1.AccountDaoImpl">
- <property name="jdbcTemplate" ref="jdbcTemplate" />
- </bean> -->
-
- <!-- ==================================1.編程式的事務管理=============================================== -->
- <!-- 配置事務管理器 -->
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <!-- 配置事務管理的模板:Spring爲了簡化事務管理的代碼而提供的類 -->
- <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
- <property name="transactionManager" ref="transactionManager"/>
- </bean>
測試:
[java] view plain copy
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration("classpath:applicationContext1.xml")
- public class TransactionTest {
- @Resource(name = "accountService")
- private AccountService accountService;
-
- @Test
- public void demo1() {
- accountService.transfer("aaa", "bbb", 200d);
- }
- }
--基於TransactionProxyFactoryBean的方式
[java] view plain copy
- public class AccountServiceImpl implements AccountService {
- // 注入轉帳的DAO
- private AccountDao accountDao;
-
- /**
- * @param out
- * :轉出帳號
- * @param in
- * :轉入帳號
- * @param money
- * :轉帳金額
- */
- @Override
- public void transfer(String out, String in, Double money) {
- accountDao.outMoney(out, money);
- // int i = 1/0;
- accountDao.inMoney(in, money);
- }
-
- public void setAccountDao(AccountDao accountDao) {
- this.accountDao = accountDao;
- }
- }
applicationContext2.xml
[html] view plain copy
- <!-- 引入外部的屬性文件 -->
- <context:property-placeholder location="classpath:jdbc.properties"/>
-
- <!-- 配置c3p0鏈接池 -->
- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
- <property name="driverClass" value="${jdbc.driverClass}" />
- <property name="jdbcUrl" value="${jdbc.url}" />
- <property name="user" value="${jdbc.username}" />
- <property name="password" value="${jdbc.password}" />
- </bean>
-
- <!-- 配置業務層類 -->
- <bean id="accountService" class="com.zs.spring.demo2.AccountServiceImpl">
- <property name="accountDao" ref="accountDao" />
- </bean>
-
- <!-- 配置DAO類(簡化,會自動配置JdbcTemplate) -->
- <bean id="accountDao" class="com.zs.spring.demo2.AccountDaoImpl">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <!-- ==================================2.使用XML配置聲明式的事務管理(原始方式)=============================================== -->
-
- <!-- 配置事務管理器 -->
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <!-- 配置業務層的代理 -->
- <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
- <!-- 配置目標對象 -->
- <property name="target" ref="accountService" />
- <!-- 注入事務管理器 -->
- <property name="transactionManager" ref="transactionManager"></property>
- <!-- 注入事務的屬性 -->
- <property name="transactionAttributes">
- <props>
- <!--
- prop的格式:
- * PROPAGATION :事務的傳播行爲
- * ISOTATION :事務的隔離級別
- * readOnly :只讀
- * -EXCEPTION :發生哪些異常回滾事務
- * +EXCEPTION :發生哪些異常不回滾事務
- -->
- <prop key="transfer">PROPAGATION_REQUIRED</prop>
- <!-- <prop key="transfer">PROPAGATION_REQUIRED,readOnly</prop> -->
- <!-- <prop key="transfer">PROPAGATION_REQUIRED,+java.lang.ArithmeticException</prop> -->
- </props>
- </property>
- </bean>
測試:
[java] view plain copy
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration("classpath:applicationContext2.xml")
- public class TransactionTest {
- /**
- * 必定要注入代理類:由於代理類進行加強的操做
- */
- // @Resource(name="accountService")
- @Resource(name = "accountServiceProxy")
- private AccountService accountService;
-
- @Test
- public void demo1() {
- accountService.transfer("aaa", "bbb", 200d);
- }
- }
--基於XML配置
[java] view plain copy
- public class AccountServiceImpl implements AccountService {
- // 注入轉帳的DAO
- private AccountDao accountDao;
-
- /**
- * @param out
- * :轉出帳號
- * @param in
- * :轉入帳號
- * @param money
- * :轉帳金額
- */
- @Override
- public void transfer(String out, String in, Double money) {
- accountDao.outMoney(out, money);
- // int i = 1/0;
- accountDao.inMoney(in, money);
-
- }
-
- public void setAccountDao(AccountDao accountDao) {
- this.accountDao = accountDao;
- }
- }
applicationContext3.xml
[html] view plain copy
- <!-- 引入外部的屬性文件 -->
- <context:property-placeholder location="classpath:jdbc.properties"/>
-
- <!-- 配置c3p0鏈接池 -->
- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
- <property name="driverClass" value="${jdbc.driverClass}" />
- <property name="jdbcUrl" value="${jdbc.url}" />
- <property name="user" value="${jdbc.username}" />
- <property name="password" value="${jdbc.password}" />
- </bean>
-
- <!-- 配置業務層類 -->
- <bean id="accountService" class="com.zs.spring.demo3.AccountServiceImpl">
- <property name="accountDao" ref="accountDao" />
- </bean>
-
- <!-- 配置DAO類(簡化,會自動配置JdbcTemplate) -->
- <bean id="accountDao" class="com.zs.spring.demo3.AccountDaoImpl">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <!-- ==================================3.使用XML配置聲明式的事務管理,基於tx/aop=============================================== -->
-
- <!-- 配置事務管理器 -->
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <!-- 配置事務的通知 -->
- <tx:advice id="txAdvice" transaction-manager="transactionManager">
- <tx:attributes>
- <!--
- propagation :事務傳播行爲
- isolation :事務的隔離級別
- read-only :只讀
- rollback-for:發生哪些異常回滾
- no-rollback-for :發生哪些異常不回滾
- timeout :過時信息
- -->
- <tx:method name="transfer" propagation="REQUIRED"/>
- </tx:attributes>
- </tx:advice>
-
- <!-- 配置切面 -->
- <aop:config>
- <!-- 配置切入點 -->
- <aop:pointcut expression="execution(* com.zs.spring.demo3.AccountService+.*(..))" id="pointcut1"/>
- <!-- 配置切面 -->
- <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
- </aop:config>
測試:
[java] view plain copy
- /**
- * @Description:Spring的聲明式事務管理的方式二:基於AspectJ的XML方式的配置
- */
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration("classpath:applicationContext3.xml")
- public class TransactionTest {
- /**
- * 必定要注入代理類:由於代理類進行加強的操做
- */
- @Resource(name = "accountService")
- private AccountService accountService;
-
- @Test
- public void demo1() {
- accountService.transfer("aaa", "bbb", 200d);
- }
- }
--基於註解
[java] view plain copy
- /**
- * @Transactional中的的屬性 propagation :事務的傳播行爲 isolation :事務的隔離級別 readOnly :只讀
- * rollbackFor :發生哪些異常回滾 noRollbackFor :發生哪些異常不回滾
- * rollbackForClassName 根據異常類名回滾
- */
- @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
- public class AccountServiceImpl implements AccountService {
- // 注入轉帳的DAO
- private AccountDao accountDao;
-
- /**
- * @param out
- * :轉出帳號
- * @param in
- * :轉入帳號
- * @param money
- * :轉帳金額
- */
- @Override
- public void transfer(String out, String in, Double money) {
- accountDao.outMoney(out, money);
- // int i = 1/0;
- accountDao.inMoney(in, money);
- }
-
- public void setAccountDao(AccountDao accountDao) {
- this.accountDao = accountDao;
- }
- }
applicationContext4.xml
[html] view plain copy
- <!-- 引入外部的屬性文件 -->
- <context:property-placeholder location="classpath:jdbc.properties"/>
-
- <!-- 配置c3p0鏈接池 -->
- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
- <property name="driverClass" value="${jdbc.driverClass}" />
- <property name="jdbcUrl" value="${jdbc.url}" />
- <property name="user" value="${jdbc.username}" />
- <property name="password" value="${jdbc.password}" />
- </bean>
-
- <!-- 配置業務層類 -->
- <bean id="accountService" class="com.zs.spring.demo4.AccountServiceImpl">
- <property name="accountDao" ref="accountDao" />
- </bean>
-
- <!-- 配置DAO類(簡化,會自動配置JdbcTemplate) -->
- <bean id="accountDao" class="com.zs.spring.demo4.AccountDaoImpl">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <!-- ==================================4.使用註解配置聲明式事務============================================ -->
-
- <!-- 配置事務管理器 -->
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource" />
- </bean>
-
- <!-- 開啓註解事務 -->
- <tx:annotation-driven transaction-manager="transactionManager"/>
-
測試:
[java] view plain copy
- @RunWith(SpringJUnit4ClassRunner.class)
- @ContextConfiguration("classpath:applicationContext4.xml")
- public class TransactionTest {
-
- /**
- * 必定要注入代理類:由於代理類進行加強的操做
- */
- @Resource(name = "accountService")
- private AccountService accountService;
-
- @Test
- public void demo1() {
- accountService.transfer("aaa", "bbb", 200d);
- }
- }