什麼是事務:java
事務指的是邏輯上的一組操做,這組操做要麼所有成功,要麼所有失敗。mysql
事務的特性:git
原子性,隔離性,一致性,持久性。spring
原子性:指的是事務是一個不可分割的工做單位,事務的發生要麼所有發生,要麼所有都不發生。sql
隔離性:指的是當多個用戶併發訪問數據庫時,一個用戶的事務不能被其餘用戶的事務所幹擾,多個併發事務之間數據要相互隔離。數據庫
一致性:指事務發生後,先後的數據完整性必須保持一致。express
持久性:當事務必定提交,他對數據庫中的數據的改變是永久的,即便數據庫發生故障也不該該對其有任何影響。編程
Spring 事務管理高層抽象主要包括了3個接口:後端
platFormTransactionManager 事務管理器併發
TransactionDefinition 事務定義信息(隔離,傳播,超時,只讀)
TransactionStatus 事務具體運行狀態
(1)platFormTranscationManager 是一個接口 有不少針對不一樣持久層的框架去選擇不一樣的事務管理器
事務 | 說明 |
org.sprinframework.jdbc.datasource.DataSourceTransactionManager |
使用Spring JDBC或iBatis進行持久化數據時使用 |
org.springframework.orm.hibernate3.HibernateTranscationManager | 使用Hiber3.0版本進行持久化數據時使用 |
org.springframework.orm.jpa.JpaTranscationManager | 使用JPA進行持久化時使用 |
org.springframework.jdo.JdoTranscationManager |
當持久化機制是jdo時使用 |
org.springframework.transaction.jta.JtaTranscationManager | 使用一個JTA實現來管理事務,在一個事務跨越多個資源時必須使用 |
(2)TranscationDefinition
TransactionDefinition接口的常量中ISOLATION_開頭的表示事物的隔離級別(4種)
沒有的話隔離級別,會出現的問題:
髒讀:一個事務讀取到另一個事務改寫但還未提交的數據。若是這些數據被回滾,則讀到的數據是無效的。
不可重複讀:在同一個事務中,讀到了另外一個事務已經提交更新的數據返回結果不同,致使這個事務中先後的查詢結果不一致。
虛讀或幻讀:一個事務讀取一些數據以後,另外一個事務插入了一些記錄,而致使這個事務中查詢的數據不一致。(第二次讀取,會讀取到插入後一些數據)
看了上面的解釋,感受不可重複讀和幻讀很相似。
特地查一下資料:
不可重複讀的重點在修改,你先後讀取到的數據是不同。
例子(我隨便想的,可能不對):事務1,a查詢卡里有1000元,事務2,有人轉500到你的卡里,事務1尚未結束,它再次查詢卡里的金額,顯示爲1500元。同一個事務中先後讀取的信息不一致。
幻讀的重點在新增或刪除,一樣的條件, 第1次和第2次讀出來的記錄數不同。
例子(網上找的):目前工資爲1000的員工有10人。 事務1,讀取全部工資爲1000的員工。 這時另外一個事務向employee表插入了一條員工記錄,工資也爲1000 ,事務1再次讀取全部工資爲1000的員工 共讀取到了11條記錄,這就產生了幻像讀。
隔離級別 | 含義 |
DEFAULT | 使用後端數據庫默認的隔離級別(spring中的選擇項) |
READ_UNCOMMITED | 容許你讀取還未提交的改變了得數據,可能致使髒讀,幻讀,不可重複讀 |
READ_COMMITED | 容許在併發事務已經提交讀取,可防止髒讀,但幻讀和不可重複讀仍可發生 |
REPEATABLE_READ | 對相同字段的屢次讀取是一致的,除非數據被事務自己改變。可防止髒,不可重複讀,但幻讀仍可發生 |
SERIALIZABLE | 徹底服從ACID(數據庫事務的四要素)的隔離級別,確保不發生髒,幻,不可重複讀。這在全部的隔離級別中是最慢的。他是典型的經過徹底鎖定在事務中涉及的數據表完成的。 |
mysql默認採用REPEATABLE_READ隔離級別
oracle默認採用READ_COMMITED隔離級別
TransactionDefinition接口的常量中PROPAGATION開頭的表示事物的傳播行爲(7種)
當出現複雜狀況時,好比某一個業務調用的業務層中的另外二個業務,每一個業務都裏面都有事務。
事務的傳播行爲:解決業務層方法之間的相互調用的問
事務傳播行爲類型 | 說明 |
PROPAGATION_REQUIRED | 支持當前事務,若是不存在,就新建一個 |
PROPAGATION_SUPPORTS | 支持當前事務,若是不存在,就不使用事務 |
PROPAGATION_MANDATORY | 支持當前事務,若是不存在,拋出異常 |
PROPAGATION_REQUIRES_NEW | 若是有事務存在,掛起當前事務,建立一個新的事務 |
PROPAGATION_NOT_SUPPORTED | 以非事務運行,若是有事務存在,掛起當前事務 |
PROPAGATION_NEVER | 以非事務運行,若是有事務存在,拋出異常 |
PROPAGATION_NESTED | 若是當前事務存在,則嵌套事務存在 |
Spring事務管理
Spring支持兩種方式事務管理
--編程式的事務管理
在實際應用中不多使用
經過TranscationTemplate手動管理事務
--使用xml配置聲明式事務
開發中推薦使用(代碼侵入性最小)
Spring的聲明式事務時經過aop實現的
用轉帳系統的事務管理。
編程式的事務管理,在xml上配置:
<!-- 引入外部的屬性文件 --> <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.spring.demo1.AccountServiceImpl"> <property name="accountDao" ref="accountDao" /> <!-- 注入事務管理的模板 --> <property name="transactionTemplate" ref="transactionTemplate" /> </bean> <!-- 配置DAO類(簡化,會自動配置JdbcTemplate) --> <bean id="accountDao" class="com.spring.demo1.AccountDaoImpl"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 配置事務管理器 --> <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>
在轉帳業務上 注入事務管理的模板
@Override public void transfer(final String out, final String in, final Double money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { accountDao.outMoney(out, money); //int i = 1/0; accountDao.inMoney(in, money); } }); }
基於代理的聲明式的事務管理
<!-- 配置事務管理器 --> <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 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>
在轉帳業務上
@Override public void transfer(String out, String in, Double money) { accountDao.outMoney(out, money); //int i = 1/0; accountDao.inMoney(in, money); }
使用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> <tx:method name="transfer" propagation="REQUIRED"/> <!-- propagation : 事務的傳播行爲 isolation : 事務隔離級別 read-only : 只讀 rollback-for :發生那些異常回來 no-rollback-for : 發生哪些一場不回滾 timeout :過時信息 --> </tx:attributes> </tx:advice> <!-- 配置切面 --> <aop:config> <!-- 配置切入點 --> <aop:pointcut expression="execution(* com.spring.demo3.AccountService+.*(..))" id="pointcut1"/> <!-- 配置切面 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/> </aop:config>
業務代碼
@Override public void transfer(String out, String in, Double money) { accountDao.outMoney(out, money); int i = 1/0; accountDao.inMoney(in, money); }
基於註解的事務管理的方式
<!-- ==================================4.基於註解的的事務管理 =============================================== --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 開啓註解事務 --> <tx:annotation-driven transaction-manager="transactionManager"/>
業務代碼
/** * @Transactional 註解中的屬性: * propagation :事務的傳播行爲 * isolation :事務的隔離級別 * readOnly :只讀 * rollbackFor :發生哪些異常回滾 * noRollbackFor :發生那些異常不回滾 * @author mufeng * */ @Override public void transfer(String out, String in, Double money) { accountDao.outMoney(out, money); int i = 1/0; accountDao.inMoney(in, money); }