Spring第四天講義spring
今日內容數據庫
在操做數據庫時(增刪改),若是同時操做屢次數據,咱們從業務但願,要不所有成功,要不所有失敗。這種狀況稱爲事務處理。express
A轉帳給B。編程
第一步,扣除A君帳號要轉的金額併發
第二步,增長B君帳號的金額app
事務:指單個邏輯操做單元的集合框架
1.JavaEE體系進行分層開發,事務處理位於業務層,因此,通常狀況下咱們使用事務代理,通常放在分層設計業務層。函數
2.spring框架爲咱們提供了一組事務控制的應用程序接口(API)。性能
3.spring的事務控制都是基於AOP的,它既可使用編程的方式實現,也可使用配置的方式實現。咱們學習的重點是使用配置的方式實現。學習
需求:從ID爲10086帳戶給ID爲10010帳戶轉帳1000元錢。
數據準備:account表(帳戶): ------------------------- ID BALANCE ------------------------- 10010 0 10086 1000 |
設計一個 Account表 |
CREATE TABLE `t_account` ( `id` int(11) NOT NULL AUTO_INCREMENT, `balance` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=10087 DEFAULT CHARSET=utf8; |
編寫轉帳案例 |
結論: 根據上述案例分析, 事務管理應該是在Service層處理
什麼是數據庫併發問題?
併發: 多個客戶端同時同時訪問數據庫中某一條數據(秒殺)
數據庫能夠擁有多個訪問客戶端,若多個客戶端併發地訪問數據庫中相同的資源,若是沒有采起必要的隔離措施,則會致使各類併發問題,破壞數據的完整性。
這些問題歸結爲5類
包括3類數據讀問題(髒讀,不可重複讀,幻讀)
和2類數據更新問題(第一類丟失更新,第二類丟失更新)。 看圖
兩個事務更新相同數據,若是一個事務提交,另外一個事務回滾,第一個事務的更新會被回滾
|
多個事務同時讀取相同數據,並完成各自的事務提交,致使最後一個事務提交會覆蓋前面全部事務對數據的改變
|
第二個事務查詢到第一個事務未提交的更新數據,第二個事務根據該數據執行,但第一個事務回滾,第二個事務操做髒數據
|
一個事務查詢到了另外一個事務已經提交的新數據,致使屢次查詢數據不一致
|
一個事務查詢到另外一個事務已經修改的數據,致使屢次查詢數據不一致
|
問題:上述問題理論上若是出現了應該如何解決?
答:通常狀況,數據庫都會處理一些事務併發的問題,數據庫提供了不一樣的事務隔離級別來處理不一樣的事務併發問題,事務隔離級別定義以下:
隔離級別 |
說明 |
READ_UNCOMMITED |
容許你讀取還未提交的改變了的數據。可能致使髒、幻、不可重複讀(至關於沒有作任何事務隔離) |
READ_COMMITTED |
容許在併發事務已經提交後讀取。可防止髒讀,但幻讀和 不可重複讀仍可發生(ORACLE默認級別) |
REPEATABLE_READ |
對相同字段的屢次讀取是一致的,除非數據被事務自己改變。可防止髒、不可重複讀,但幻讀仍可能發生。(MYSQL默認級別) |
SERIALIZABLE |
徹底服從ACID的隔離級別,確保不發生髒、幻、不可重複讀。這在全部的隔離級別中是最慢的,它是典型的經過徹底鎖定在事務中涉及的數據表來完成的。(ORACLE支持)
|
針對不一樣隔離級別能夠解決的的以下五類問題
|
數據庫表數據加鎖
1,悲觀鎖
(1) 在操做當前數據的事務開啓事務就使用for update 鎖住當前數據
(2) Hibernate和MyBatis都有悲觀鎖對應的解決方案
|
2,樂觀鎖
(1) 爲表添加一個version字段。當前事務操做的時候都會比對當前事務狀況的屢次操做的版本號是否一致,若是不一致認爲數據已經被更新
(2) Hibernate和MyBatis都有樂觀鎖對應的解決方案
答:使用Spring是事務代理,能夠配置一次,不用重複編寫事務處理代碼!!
Spring框架針對事務處理提供專門的解決方案
Spring的事務管理主要包括3個接口
Spring事務處理接口 |
描述 |
TransactionDefinition |
封裝事務的隔離級別超時時間,是否爲只讀事務和事務的隔離級別和傳播規則等事務屬性,可經過XML配置具體信息 |
PlatformTransactionManager |
根據TransactionDefinition提供的事務屬性配置信息,建立事務。事物管理器 |
TransactionStatus |
封裝了事務的具體運行狀態。好比,是不是新開啓事務,是否已經提交事務,設置當前事務爲rollback-only等 |
該接口主要定義了:事務的傳播行爲(規則),事務的隔離級別,得到事務信息的方法。因此在配置事務的傳播行爲,事務的隔離級別已經須要得到事務信息時,能夠經過查閱該類的代碼得到相關信息。
TransactionDefinition源碼
public interface TransactionDefinition {
//事務的傳播行爲 int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4; int PROPAGATION_NEVER = 5; int PROPAGATION_NESTED = 6; //事務的隔離級別 int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE; //事務超時管理 int TIMEOUT_DEFAULT = -1;
//得到事務信息 int getPropagationBehavior();
int getIsolationLevel();
int getTimeout();
boolean isReadOnly();
String getName();
}
|
Spring在TransactionDefinition接口中定義了七種事務傳播規則,規定了事務方法和事務方法發生嵌套調用時事務該如何進行傳播
事務傳播規則類型 |
描述 |
PROPAGATION_REQUIRED |
若是當前存在事務,則加入該事務;若是當前沒有事務,則建立一個新的事務(最經常使用操做)-Spring默認使用的就是此規則 |
PROPAGATION_REQUIRES_NEW |
建立一個新的事務,若是當前存在事務,則把當前事務掛起 |
PROPAGATION_SUPPORTS |
若是當前存在事務,則加入該事務;若是當前沒有事務,則以非事務的方式繼續運行 |
PROPAGATION_NOT_SUPPORTED |
以非事務方式運行,若是當前存在事務,則把當前事務掛起 |
PROPAGATION_NEVER |
以非事務方式運行,若是當前存在事務,則拋出異常 |
PROPAGATION_MANDATORY |
若是當前存在事務,則加入該事務;若是當前沒有事務,則拋出異常 |
PROPAGATION_NESTED |
若是當前存在事務,則建立一個事務做爲當前事務的嵌套事務來運行;若是當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED |
Spring的事務管理:
1,PlatformTransactionManager:接口統一抽象處理事務操做相關的方法;
1):TransactionStatus getTransaction(TransactionDefinition definition):
根據事務定義信息從事務環境中返回一個已存在的事務,或者建立一個新的事務,並用TransactionStatus描述該事務的狀態。
ü 2):void commit(TransactionStatus status):
根據事務的狀態提交事務,若是事務狀態已經標識爲rollback-only,該方法執行回滾事務的操做。
ü 3):void rollback(TransactionStatus status):
將事務回滾,當commit方法拋出異常時,rollback會被隱式調用
2,在使用spring管理事務的時候,首先得告訴spring使用哪個事務管理器,使用不一樣的框架(JdbcTemplate,MyBatis,Hibernate/JPA )使用事務管理器都不一樣
|
|
3,經常使用的事務管理器:
DataSourceTransactionManager:使用JDBC,MyBatis的事務管理器;
Spring支持編程式事務管理和聲明式事務管理。
在配置文件中引入新的命名空間 tx
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd "> |
Spring對象事務的支持,必須先配置事務管理器,事務管理器已經封裝了事務的具體的處理
DataSourceTransactionManager
使用JDBC,MyBatis的事務管理器;(當前案例使用的Spring的JDBC操做,因此配置這個)
<!-- 1,配置事務管理器(應根據狀況使用合適的事務管理器) --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入dataSource --> <property name="dataSource" ref="dataSource"/> </bean> <!-- 一、切入點(Pointcut):在哪些類,哪些方法上切入(where); 二、加強(Advice):早期翻譯爲通知,在方法執行的什麼時機(when:方法前/方法後/方法先後)作什麼(what:加強的功能); 三、切面(Aspect):切面=切入點+通知,通俗點就是:什麼時機,什麼地點,作什麼! 四、織入(Weaving):把切面加入到對象,並建立出代理對象的過程。(該過程由Spring來完成)。 WWW 原則 : where : 在什麼地方切事務 what : 幹什麼(切事務) when : 時機(在方法前或者後) -->
<!-- 事務相關的配置 : 使用AOP面向切面編程(切事務) : 使用 tx: 標籤 --> <!-- tx:advice : 配置事物的標籤 id : 惟一標識 transaction-manager : 須要用到的事物管理器 --> <!-- 2,配置管理事務的「加強」 :環繞通知--> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <!-- 配置要切的方法 : trans (默認使用的是環繞通知) --> <tx:method name="trans"/> </tx:attributes> </tx:advice>
<!-- 3,面向切面 : --> <aop:config > <!-- 切入點 : where --> <aop:pointcut expression="execution ( * cn.zj.spring.service..*.*(..))" id="pt"/> <!-- 配置切面 切面 = 切入點 + 通知 advice-ref 通知的引用 pointcut-ref 切入點的引入 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pt" /> <!-- 織入(Weaving):把切面加入到對象,並建立出代理對象的過程。(該過程由Spring來完成) --> </aop:config> |
事務方法屬性:
事務方法屬性 |
描述 |
name |
匹配到的方法模式 |
read-only |
若是爲true,開啓一個只讀事務,只讀事務的性能較高,可是不能再只讀事務中,使用DML |
isolation |
表明數據庫事務隔離級別(就使用默認) DEFAULT:讓spring使用數據庫默認的事務隔離級別; |
no-rollback-for |
若是遇到的異常是匹配的異常類型,就不回滾事務 |
rollback-for |
若是遇到的異常是指定匹配的異常類型,纔回滾事務;spring默認狀況下,spring只會去回滾RuntimeException及其子類,不會回滾Exception和Thowable |
propagation |
事務的傳播方式(當一個方法已經在一個開啓的事務當中了,應該怎麼處理自身的事務) 1,REQUIRED(默認的傳播屬性):若是當前方法運行在一個沒有事務環境的狀況下,則開啓一個新的事務,若是當前方法運行在一個已經開啓了的事務裏面,把本身加入到開啓的那個事務中 2,REQUIRES_NEW:無論當前方法是否運行在一個事務空間以內,都要開啓本身的事務
|
timeout |
事務處理的超時時間,默認事物管理自動處理 ,能夠手動配置
|
<tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes>
<!-- <tx:method> name : 須要切面的方法 isolation : 事務的隔離級別 DEFAULT 使用當前數據庫默認的隔離級別,不一樣數據庫隔離級別不一樣(能夠不配置) propagation : 事物的傳播規則 ,默認使用 REQUIRED read-only : 是不是隻讀事務, DQL配置便可 --> <!-- 通常DML操做才須要事務,DQL查詢操做是不須要事物 * 通配符 --> <!-- 全部之前綴開都的方法都認爲是 查詢方法,就不切入事物 --> <tx:method name="get*" read-only="true"/> <tx:method name="select*" read-only="true" /> <tx:method name="find*" read-only="true"/> <tx:method name="query*" read-only="true"/> <tx:method name="list*" read-only="true"/>
<!-- 非查詢(DQL)方法: DML操做,須要事務管理 --> <tx:method name="*" /> </tx:attributes> </tx:advice> |
Spring的聲明式事務也支持 註解
<!-- 1,配置事務管理器(應根據狀況使用合適的事務管理器) --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入dataSource --> <property name="dataSource" ref="dataSource"/> </bean>
<!-- 開啓註解驅動配置事務 編寫此標籤:Spring底層就支持註解配置事物,而且已經配置好事物管理器 --> <tx:annotation-driven transaction-manager="txManager"/> |
@Service /* @Transactional * 貼上此當前類已經被Spring事務管理 * 注意: @Transactional 只能對當前貼的Service類有效 * 經常使用屬性 : * isolation=Isolation.REPEATABLE_READ, 隔離級別 propagation=Propagation.REQUIRED,傳播規則 readOnly=true 是否只讀事務 * */@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED) public class AccountServiceImpl implements AccountService{ @Autowired private AccountDao dao;
public void trans(Integer transOutId, Integer transInId, Integer money) { dao.tranOut(transOutId, money); System.out.println(1 / 0);// 模擬斷電 dao.tranIn(transInId, money); } //單獨爲某一個方法配置具體的事物細節:如查詢方法,事物是隻讀的 @Transactional(readOnly=true) public Object getUser() { //查詢操做 return null; } @Transactional(readOnly=true) public List<Object> getUsers() { //查詢操做 return null; } } |
Xml配置 : 代碼清晰,配置量少,容易維護
註解配置 : 侵入代碼,每一個Service層類都得配置,針對不一樣的方法還得配置事務細節:如查詢方法配置只讀操做,不容易維護
建議選擇xml配置
Spring框架出現緣由?
早期企業級開發組件(框架) EJB (重量級)很差用
音樂博士,計算機大牛
以爲ejb 很差,寫了一本書各類吐槽抨擊 ejb
順便寫了一個輕量級 一站式 企業框架 spring
解耦 (下降程序代碼與代碼的直接依賴關係)
Spring容器-下面全部操做都在Spring 容器中完成,Spring就是項目對象的管家
(1) Xml 配置
① <bean id/name=’xxx’ class = 「類的全限定名」 scope=’做用範圍’ init-mehtod=’初始化方法’destory-mehtod=’銷燬方法’>
② 從Spring容器中讀取bean對象
(2) 註解配置 -貼在類上便可
① @Component 通用組件掃描建立
② @Controller 表現層(SpringMVC 專用)
③ @Service 業務層專用
④ @Repository 持久層/DAO/Mapper 專用
⑤ @Scope 做用範圍
⑥ @PostConstrcut 初始化方法
⑦ @PreDestory 銷燬方法
(1) XML配置
① <property name=’’ value/ref=’’> 屬性注入
② <constructor-arg name=’參數名’ value/ref=’’ >
③ P 命名空間
1) <bean id =’xx’ class =」Xxxx」 p:屬性名=’值’ p:屬性名-ref=’引用類型’>
(2) 註解配置
① Spring框架制定
1) @Autowired 默認按照類型注入
2) @Qualifier 從多個相同類型中經過id指定惟一的那個bean
② JavaEE 官方指定
1) @Resource(name=’bean的id’)
(1) AOP 底層原理-Java動態代理
① JDK動態代理 : 只能有接口的類型進行代理
② CGLIB代理 :便可代理有接口類,有能夠代理沒有接口的
(2) 專業術語
① JoinPoint 鏈接點(方法)
② PointCut 切入點 (某一類方法,規則)
③ Advice 通知/加強
④ Aspect 切面 = 切入點+通知
⑤ Weaving 織入(Spring自動完成)
(1) 配置事務管理器
① JDBC/MyBatis ---->DataSourceTransactionManager
(2) 事務的隔離級別 4個
(3) 事務的傳播規則(行爲) 7個
(4) 是不是隻讀事務
(5) 使用AOP 將事務切入到 業務層
(1) jdbcTemplate 模板類
① Update 更新方法(update,insert,delete)
② queryForObject : 單行查詢
③ Query : 多行查詢