1.事物:事物是一系列的動做,他們綜合在一塊兒纔是一個完整的單元,這些動做必須所有完成,若是有一個失敗的話,事物就會回滾到最初始的狀態,彷彿什麼都沒有發生過。java
事物有四個特性:spring
2.spring中事物有5個隔離級別sql
隔離級別是指若干個併發事物之間的隔離水平,TransactionDefinition介紹了五種隔離級別數據庫
1)ISOLATION_DEFAULT:這是默認值,表示採用使用的數據庫的默認的隔離級別編程
2)ISOLACTION_COMMITTED(讀已提交):容許讀取其餘併發事物已經提交的更新(防止髒讀)併發
3)ISOLACTION_UNCOMMITTED(讀未已提交):容許讀取其餘併發事物還未提交的更新,會致使事物之間的3個缺陷發生,這是速度最快的一個隔離級別,但也是隔離級別最低的一個。app
4)ISOLACTION_REPEATABLE_READ(重複讀):除非事物自身修改了數據,不然規定事物屢次重複讀取數據必須相同,(防此髒讀,不可重複讀) 框架
5)ISOLACTION_SERIALIZABLE(串行化):這是最高的隔離級別,它能夠防止髒讀、不可重複讀和幻讀。性能
注意:並非全部的資源管理器都支持全部的隔離級別,可針對不一樣的資源管理使用以上的隔離級別。 測試
3.事物的只讀性
在對數據庫的操做中,查詢是使用最頻繁的操做,每次執行查詢時都要從數據庫中從新讀取數據,有時屢次讀取的數據都是相同的,這樣的數據操做不只浪費了系統資源,還影響了系統速度。對訪問量大的程序來講,節省這部分資源能夠大大提 升系統速度。
若是將事物聲明爲只讀的,那麼數據庫能夠根據事物的特性優化事物的讀取操做,事物的只讀性須要配合事物的傳播行爲共同設置
<prop key="query">PROPAGATION_REQUIRED,readOnly<prop>
4.事物的超時性
這個屬性和事務的只讀屬性同樣須要搭配事務的傳播行爲共同設置,它設置了事務的超時時間,事務自己可能會因某種緣由很長沒有迴應,在這期間事務可能鎖定了數據庫的表格,這樣會出現嚴重的性能問題。經過設置事務的超時時間,從開始執行事務起,在規定的超時時間內若是沒有事務就將它回滾。事務的超時屬性以timeout_爲前綴和一個整型數字定義,例如:
<prop key="query*">PROPAGATION_REGUIRED,timeout_5,readOnly</prop>
5.編程式事物和聲明式事物
編程式事物: Transaction tx = con.beginTransaction(); tx.commit();
聲明式事物:用框架去維護事物對象,和事物的提交和回滾
Spring特有的事物傳播行爲,spring支持7種傳播行爲,肯定客戶端和被調用端的事物邊界(說的通俗一點就是多個具備事物控制的service的相互調用時所造成的複雜的事物邊界控制)。
傳播行爲 | 含義 |
PROPAGATION_REQUIRED(XML文件中爲REQUIRED) | 表示當前方法必須在一個具備事務的上下文中運行,若有客戶端有事務在進行,那麼被調用端將在該事務中運行,不然的話從新開啓一個事務。(若是被調用端發生異常,那麼調用端和被調用端事務都將回滾) |
PROPAGATION_SUPPORTS(XML文件中爲SUPPORTS) | 表示當前方法沒必要須要具備一個事務上下文,可是若是有一個事務的話,它也能夠在這個事務中運行 |
PROPAGATION_MANDATORY(XML文件中爲MANDATORY) | 表示當前方法必須在一個事務中運行,若是沒有事務,將拋出異常 |
PROPAGATION_NESTED(XML文件中爲NESTED) | 表示若是當前方法正有一個事務在運行中,則該方法應該運行在一個嵌套事務中,被嵌套的事務能夠獨立於被封裝的事務中進行提交或者回滾。若是封裝事務存在,而且外層事務拋出異常回滾,那麼內層事務必須回滾,反之,內層事務並不影響外層事務。若是封裝事務不存在,則同PROPAGATION_REQUIRED的同樣 |
PROPAGATION_NEVER(XML文件中爲NEVER) | 表示當方法務不該該在一個事務中運行,若是存在一個事務,則拋出異常 |
PROPAGATION_REQUIRES_NEW(XML文件中爲REQUIRES_NEW) | 表示當前方法必須運行在它本身的事務中。一個新的事務將啓動,並且若是有一個現有的事務在運行的話,則這個方法將在運行期被掛起,直到新的事務提交或者回滾才恢復執行。 |
PROPAGATION_NOT_SUPPORTED(XML文件中爲NOT_SUPPORTED) | 表示該方法不該該在一個事務中運行。若是有一個事務正在運行,他將在運行期被掛起,直到這個事務提交或者回滾才恢復執行 |
6.在Spring的事物管理中主要涉及下面三個接口,platformTransactionManager,TransactionDefinition,TransactionStatus,其中platformTransactionManager抽取了事物管理過程當中的整個流程中的最頂層的操做接口。
7.Spring對事物的提交仍是回滾:異常類型是編譯時異常,默認是提交的
異常類型是運行時異常,默認是回滾的
8.事物的併發會產生什麼問題
1)第一類丟失更新:在沒有事物隔離的狀況下,兩個事物同時更新一行數據,可是第二個事物卻中途失敗退出,致使對數據的兩個修改都失效了。
例如:張三的工資爲5000元,事物A中獲取工資爲5000,事物B獲取工資爲5000,匯入100,並提交數據庫,工資變爲5100,隨後,事物A發生異常,回滾了,恢復張三的工資爲5000元,這樣就致使了事物B的跟新丟失了
2)髒讀:髒讀就是指當一個事物正在訪問數據,而且對數據進行了修改,而這種修改尚未提交到數據庫中,這時,另外一個事物也訪問這個數據,而後使用了這個數據。
例如:張三的工資爲5000,事物A中把他的工資爲改成8000,但事物A還沒有提交。於此同時,事物B正在讀取張三的工資,讀取到張三的工資爲8000。隨後,事物A發生異常,而回滾了事物。張三的工資又回滾到5000。最後,事物B讀取到的張三工資爲8000的數據即爲髒數據,事物B作了一次髒讀。
3)不可重複讀:是指在一個事物內,屢次讀同一數據。在這個事物尚未結束時,另外一個事物也訪問該同一數據,那麼,在第一個事物中的兩次讀數據之間,因爲第二個事物的修改,那麼第一個事物中兩次讀取數據之間,因爲第二個事物的修改,那麼第一個事物兩次讀到的數據多是不同的。這樣就發生了在同一個事物內兩次讀到的數據是不同的,所以稱爲是不可重複度的。
例如:在事物A中,讀取到張三的工資爲5000,操做沒有完成,事物還沒提交。於此同時,事物B把張三的工資改成8000,並提交了事物。隨後,在事物A中,在次讀取到張三的工資,此時工資變爲8000.在一個事物中先後兩次讀取到的結果並不一致,致使了不可重複度。
4)第二類丟失更新:不可重複讀的特利。有兩個併發事物同時讀取同一行數據,而後其中一個對它進行修改提交,而另外一個也進行了修改提交,這就會形成第一次讀寫操做失效。
例如:在事物A中,讀取到張三的存款爲5000,操做沒有完成,事物還沒提交。於此同時,事物B,存儲1000,把張三的存款改成6000,並提交了事物,這樣事物A的更新覆蓋了事物B的更新。
5)幻讀:是指當前事物不是獨立執行時發生的一種現象,例如第一個事物對錶中的數據進行了修改,這種修改涉及到表中的所有數據行,同時,第二個事物也修改了這個表中的數據,這種修改是向表中插入一行新的數據,那麼,之後就會發生操做第一個事物的用戶發向表中還有沒有修改的數據行,就好像發生了幻覺同樣。
例如:目前工資爲5000的員工有10人,事物A讀取全部工資爲5000的人數爲10人。此時,事物B插入一條工資也爲5000的記錄,這時,事物A再次讀取工資爲5000的員工,記錄爲11人,此時產生了幻讀。
提示:不可重複讀的重點是修改,一樣的條件,你讀取過的數據,再次讀取出來的發現值不同了。
幻讀的重點在於新增或者刪除,一樣的條件,第一次和第二次讀出來的記錄數不同
9.事物的實例
買股票的事件
01.建立實體類
package cn.happy.entity; /* * 銀行帳戶 * */ public class Account { private Integer aid; //賬戶id private String aname; //賬戶名 private double blance; //賬戶餘額 public Integer getAid() { return aid; } public void setAid(Integer aid) { this.aid = aid; } public String getAname() { return aname; } public void setAname(String aname) { this.aname = aname; } public double getBlance() { return blance; } public void setBlance(double blance) { this.blance = blance; } }
Stock類
package cn.happy.entity; /* * 股票類 * */ public class Stock { private Integer sid; //股票id private String sname; //股票名 private Integer count; //持股數 public Integer getSid() { return sid; } public void setSid(Integer sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public Integer getCount() { return count; } public void setCount(Integer count) { this.count = count; } }
異常類
StockException
/* * 編譯時異常 spring默認 自動提交事物 * 運行時異常 spring默認 自動回滾事物 * */ public class StockException extends Exception { public StockException() { super(); } public StockException(String message) { super(message); } }
02.建立dao層
public interface IAccountDao { //核心業務 public void updateAccount(int aid,double money,boolean isBuy); }
public interface IStockDao { public void updateStock(int sid,int count,boolean isBuy); }
03.建立dao的實現類
import cn.happy.dao.IAccountDao; import org.springframework.jdbc.core.support.JdbcDaoSupport; public class IAccountDaoImpl extends JdbcDaoSupport implements IAccountDao { public void updateAccount(int aid, double money, boolean isBuy) { String sql = null; if(isBuy){
//購買股票 sql = "update account set blance=blance-? where aid=?"; }else{
//拋出股票 sql = "update account set blance=blance+? where aid=?"; } this.getJdbcTemplate().update(sql,money,aid); } }
import cn.happy.dao.IStockDao; import org.springframework.jdbc.core.support.JdbcDaoSupport; public class IStockDaoImpl extends JdbcDaoSupport implements IStockDao { String sql = null; public void updateStock(int sid, int count, boolean isBuy) { if(isBuy){ //購買股票 sql="update stock set count=count+? where sid=?"; }else{ //拋出股票 sql="update stock set count=count-? where sid=?"; } //對數據進行增 刪 改 都用update this.getJdbcTemplate().update(sql, count, sid); } }
04. 建立service層
public interface IStockService { public void buyStock(int sid,int counts,int aid,double money); }
05.建立service實現類
import cn.happy.dao.IAccountDao; import cn.happy.dao.StockDao; import cn.happy.entity.StockException; public class AccoutServiceImpl implements IAccountService { //植入AccountDao private IAccountDao accountDao; //植入StockDao private StockDao stockDao; public void buyStock(int sid, int count, int aid, int money) throws StockException { boolean isBuy = true; //購買股票 accountDao.updateAccount(aid,money,isBuy); //手動製造異常 if(1==1) { throw new StockException(); } stockDao.updateStock(sid,count,isBuy); } public IAccountDao getAccountDao() { return accountDao; } public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; } public StockDao getStockDao() { return stockDao; } public void setStockDao(StockDao stockDao) { this.stockDao = stockDao; } }
06.編寫配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "> <!--1.識別jdbc.properties文件--> <context:property-placeholder location="jdbc.properties"></context:property-placeholder> <!--2.創建數據遠,${} spring內置的數據源 DriverMangerDateSource--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> </bean> <!--4.dao的配置--> <bean id="AccountDao" class="cn.happy.dao.impl.IAccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="StockDao" class="cn.happy.dao.impl.IStockDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <!--5.service的配置--> <bean id="stockService" class="cn.happy.service.StockServiceImpl"> <property name="accountDao" ref="AccountDao"></property> <property name="stockDao" ref="StockDao"></property> </bean> <!--事物管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--配置事物,攔截業務方法--> <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!--目標類型(要加強的類)--> <property name="target" ref="stockService"></property> <property name="transactionManager" ref="transactionManager"></property> <!--加強--> <property name="transactionAttributes"> <props> <prop key="buyStock">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-StockException</prop> </props> </property> </bean> </beans>
07.編寫測試類
public class TestSW { @Test public void test01(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); IStockService stockService = (IStockService)context.getBean("stockService"); stockService.buyStock(34,10,3,1000); } }
在發生異常時,餘額和股票數都不會變。