目錄
2.3、PlatformTransactionManager 事務管理器
1、什麼是事務
- 事務:一組業務操作,要麼全部成功,要麼全部失敗
- 四大特性:ACID
- 原子性:一個事務是一個不可分割單位,要麼全發生,要麼全不發生
- 一致性:事務必須是使數據庫從一個一致性狀態變到另一個一致性狀態
- 隔離性:一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的數據對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。
- 持久性:持久性也稱永久性(permanence),指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。
- 隔離問題:
- 髒讀:一個事務讀到另一個事務沒有提交的數據
- 不可重複讀:一個事務讀到另一個事務已提交的數據(update)
- 幻讀:一個事務讀到另一個事務已經提交的數據(insert)
- 隔離級別:
- read uncommitted:讀未提交,存在3個問題
- read committed:讀已提交,存在2個問題
- repeatable read:可重複讀,存在一個問題
- serializable:串行化,可以解決三個問題
- 事務的操作
ABCD一個事務 Connection conn = null; try{ //1、獲得連接 conn = //2、開啓事務 conn.setAutoCommit(false); A B C D //3、提交事務 conn.commit(); }catch{ //回滾事務 conn.rollback(); }
- mysql事務操作--Savepoint
- Savepoint:保存點,記錄操作的當前位置,之後可以回滾到該位置
需求:AB(必須),CD(可選) Connection conn = null; Savepoint savepoint = null;//保存點:記錄操作的當前位置,之後可以回滾到指定的位置(可以回滾一部分) try{ //1、獲得連接 conn = //2、開啓事務 conn.setAutoCommit(false); A B savepoint = conn.setSavepoint(); C D //3、提交事務 conn.commit(); }catch{ if(savepoint!=null){//AB沒有出錯,那麼savepoint是有值的 //CD異常 //回滾到CD之前 conn.rollback(savepoint); //提交AB conn.commit(); }else{//savepoint爲空,說明AB異常 //回滾AB conn.rollback(); } }
2、事務管理介紹
2.1、導入jar包
2.2、三個頂級接口
- PlatformTransactionManager:平臺事務管理器,spring要管理事務,必須使用事務管理器。(進行事務配置時,必須配置事務管理器)
- TransactionDefinition:事務定義(事務詳情、事務的屬性),spring用於確定事務的具體詳情的,例如:隔離級別、是否只讀、超時時間等等。(之後進行事務配置時,必須配置詳情,spring將配置封裝到該對象實例)
- TransactionStatus:事務狀態,Spring用於記錄當前事務的運行狀態。例如:是否有保存點,事務是否已經完成等。(spring底層根據狀態進行相應的操作。)
2.3、PlatformTransactionManager 事務管理器
- 導入jar包:需要的是平臺事務管理器的實現類
- 常見的事務管理器
1、DataSourceTransactionManager:jdbc開發時事務管理器,採用JDBCTemplate
2、 HibernateTransactionManager:hibernate開發時事務管理器,整合hibernate
- 接口:PlatformTransactionManager源碼詳解
package org.springframework.transaction; public interface PlatformTransactionManager { //1、事務管理器,通過”事務詳情“,獲得”事務狀態“,從而管理事務 TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException; //2、根據狀態提交 void commit(TransactionStatus var1) throws TransactionException; //3、根據狀態回滾 void rollback(TransactionStatus var1) throws TransactionException; }
2.4、TrancationStatus 事務狀態
- 接口:TransactionStatus源碼詳解
package org.springframework.transaction; import java.io.Flushable; public interface TransactionStatus extends SavepointManager, Flushable { //1、是否是新的事務 boolean isNewTransaction(); //2、是否有保存點 boolean hasSavepoint(); //3、設置回滾 void setRollbackOnly(); //4、是否回滾 boolean isRollbackOnly(); //5、刷新 void flush(); //6、是否完成 boolean isCompleted(); }
2.5、TransactionDefinition事務詳情
- 接口:TransactionDefinition詳解
package org.springframework.transaction; public interface TransactionDefinition { //傳播行爲 int PROPAGATION_REQUIRED = 0;//必須:支持當前事務,A如果有事務,B將使用該事務;A若沒有事務,B將創建一個新的事務 int PROPAGATION_SUPPORTS = 1;//支持:支持當前事務,A如果有事務,B將使用該事務;A若沒有事務,B將以非事務執行 int PROPAGATION_MANDATORY = 2;//強制:支持當前事務,A如果有事務,B將使用該事務;A若沒有事務,B將拋異常 int PROPAGATION_REQUIRES_NEW = 3;//必須新的:必創建一個新的事務,A如果有事務,將A的事務掛起,B創建一個新的事務;A如果沒有事務,B直接創建一個新的事務 int PROPAGATION_NOT_SUPPORTED = 4;//不支持:不支持當前事務,總是以非事務執行。如果A有事務,將A的事務掛起,B將以非事務執行;如果A沒有事務,B直接以非事務執行 int PROPAGATION_NEVER = 5;//從不:不支持當前事務,如果有的話會拋出異常。如果A有事務,那麼B將拋出異常,如果A沒有事務,B將以非事務執行 int PROPAGATION_NESTED = 6;//嵌套:A和B底層採用保存點的機制,形成嵌套事務 //隔離級別 int ISOLATION_DEFAULT = -1;默認的,oracle默認爲2,mysql默認爲4 int ISOLATION_READ_UNCOMMITTED = 1; int ISOLATION_READ_COMMITTED = 2;// int ISOLATION_REPEATABLE_READ = 4;// int ISOLATION_SERIALIZABLE = 8; int TIMEOUT_DEFAULT = -1;//默認的超時時間,使用的是數據庫底層的超時時間 //1、傳播行爲:******* int getPropagationBehavior(); //2、隔離級別 int getIsolationLevel(); //3、獲得超時時間 int getTimeout(); //4、是否只讀(一般情況下增刪改:讀寫,查詢:只讀) boolean isReadOnly(); //5、配置事務詳情的名稱,一般是方法名稱。例如:save String getName(); }
傳播行爲:在兩個業務之間,如何來共享事務:
- PROPAGATION_REQUIRED :必須,支持當前事務,A如果有事務,B將使用該事務;A若沒有事務,B將創建一個新的事務【默認值】【掌握】
- PROPAGATION_SUPPORTS:支持,支持當前事務,A如果有事務,B將使用該事務;A若沒有事務,B將以非事務執行
- PROPAGATION_MANDATORY :強制,支持當前事務,A如果有事務,B將使用該事務;A若沒有事務,B將拋異常
- PROPAGATION_REQUIRES_NEW:必須新的,必創建一個新的事務,A如果有事務,將A的事務掛起,B創建一個新的事務;A如果沒有事務,B直接創建一個新的事務【掌握】
- PROPAGATION_NOT_SUPPORTED :不支持,不支持當前事務,總是以非事務執行。如果A有事務,將A的事務掛起,B將以非事務執行;如果A沒有事務,B直接以非事務執行
- PROPAGATION_NEVER = 5:從不,不支持當前事務,如果有的話會拋出異常。如果A有事務,那麼B將拋出異常,如果A沒有事務,B將以非事務執行
- PROPAGATION_NESTED :嵌套,A和B底層採用保存點的機制,形成嵌套事務【掌握】
3、案例:轉賬
3.1、搭建環境
3.1.1創建表
create table account( id int primary key auto_increment, username varchar(50), money int ); insert into account(username,money) values ('jack',1000),('rose',1000);
3.1.2、導入jar包
- 核心:4+1(core、context、expression、bean +loggin)
- aop:4(aop聯盟、Spring aop、aspectJ規範、spring aspect)
- 數據庫:2(jdbc + tx)
- 驅動:mysql驅動
- 連接池:c3p0
3.1.3、Dao層(接口+實現類)
接口
package tx_dao; public interface AccountDao { public void out(String outer,Integer money);//轉賬 public void in(String inner,Integer money);//收款 }
實現類:
package tx_dao_impl; import org.springframework.jdbc.core.support.JdbcDaoSupport; import tx_dao.AccountDao; public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { @Override public void out(String outer, Integer money) { super.getJdbcTemplate().update("update account set money = money - ? where username = ?",money,outer); } @Override public void in(String inner, Integer money) { super.getJdbcTemplate().update("update account set money = money + ? where username = ?",money,inner); } }
3.1.4、Service層(接口+實現類)
接口:
package tx_service; public interface AccountService { //轉賬 public void transfer(String outer,String inner,Integer money); }
實現類:
package tx_service_impl; import tx_dao.AccountDao; import tx_service.AccountService; public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transfer(String outer, String inner, Integer money) { accountDao.out(outer,money); accountDao.in(inner,money); } }
3.1.5、spring配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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"> <!--配置datasource--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value = "com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value = "jdbc:mysql://localhost:3306/spring03"></property> <property name="user" value = "root"></property> <property name="password" value = "hmx123456"></property> </bean> <!--配置dao public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { @Override public void out(String outer, Integer money) { super.getJdbcTemplate().update("update account set money = money - ? where username = ?",money,outer); } @Override public void in(String inner, Integer money) { super.getJdbcTemplate().update("update account set money = money + ? where username = ?",money,inner); } } --> <bean id="accountDaoId" class="tx_dao_impl.AccountDaoImpl"> <property name="dataSource" ref = "dataSource"></property> </bean> <!--配置service public class AccountServiceImpl implements AccountService{ private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override public void transfer(String outer, String inner, Integer money) { accountDao.out(outer,money); accountDao.in(inner,money); } } --> <bean id="accountServiceId" class="tx_service_impl.AccountServiceImpl"> <property name="accountDao" ref = "accountDaoId"></property> </bean> </beans>
3.1.6、測試類
package tx_test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import tx_service.AccountService; public class TestApp { @Test public void demo01(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); AccountService accountService = applicationContext.getBean("accountServiceId",AccountService.class); accountService.transfer("jack","rose",20); } }
3.2、手動管理事務
- spring底層使用TransactionTemplate事務模板進行操作
- 操作
- service需要獲得TransactionTemplate
- spring配置模版,並注入給service
- 模版需要注入事務管理器
- 配置事務管理器
3.2.1、修改service
package tx2_service_impl; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import tx2_dao.AccountDao; import tx2_service.AccountService; public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } //需要spring注入模版 private TransactionTemplate transactionTemplate;//事務模版 public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } @Override public void transfer(String outer, String inner, Integer money) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { accountDao.outer(outer,money); //模擬一個異常 int i = 1/0; accountDao.inner(inner,money); } }); } }
3.2.2、修改xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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、配置datasource--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value = "com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value = "jdbc:mysql://localhost:3306/spring03"></property> <property name="user" value = "root"></property> <property name="password" value = "hmx123456"></property> </bean> <!--2、dao--> <bean id="accountDao" class="tx2_dao_impl.AccountDaoImpl"> <property name="dataSource" ref = "dataSource"></property> </bean> <!--3、service--> <bean id="accountService" class="tx2_service_impl.AccountServiceImpl"> <property name="accountDao" ref = "accountDao"></property> <property name="transactionTemplate" ref = "transactionTemplate"></property> </bean> <!--4、創建模版--> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref = "txManager"></property> </bean> <!--5、配置事務管理器:管理器需要事務,事務從Connection獲得,連接從連接池DataSource獲得--> <bean id="txManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref = "dataSource"></property> </bean> </beans>
3.3、工廠bean生成代理(半自動)
- spring提供 管理事務的代理工廠 bean TransactionProxyFactoryBean
1、getBean()獲得代理對象
2、在spring中配置一個代理
3.3.1、Spring的配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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、配置datasource--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value = "com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value = "jdbc:mysql://localhost:3306/spring03"></property> <property name="user" value = "root"></property> <property name="password" value = "hmx123456"></property> </bean> <!--2、配置dao--> <bean id = "accountDaoId" class="tx3_dao_impl.AccountDaoImpl"> <property name="dataSource" ref = "dataSource"></property> </bean> <!--3、配置service--> <bean id = "accountServiceId" class="tx3_service_impl.AccountServiceImpl"> <property name="accountDao" ref = "accountDaoId"></property> </bean> <!--4、生成代理 1:接口:proxyInterfaces-目標類接口 2:目標類:target-目標類的引用 3:事務管理器:transactionManager 4:事務屬性:transactionAttributes prop.key:確定哪些方法使用當前的事務配置 prop.text:用於配置事務詳情 格式:PROPAGATION(傳播行爲)、ISOLATTON(隔離級別)、readOnly(是否只讀)、-Excepion(異常回滾:發生異常回滾)、+Exception(異常提交:發生異常仍然要提交) 例如:<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop> 默認的傳播行爲和默認的隔離級別 <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly</prop>只讀 --> <bean id = "proxyAccountServiceId" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="proxyInterfaces" value = "tx3_service.AccountService"></property> <property name="target" ref = "accountServiceId"></property> <property name="transactionManager" ref = "txManager"></property> <property name="transactionAttributes"> <props> <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly</prop> </props> </property> </bean> <!--5、配置事務管理器--> <bean id = "txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> </beans>
3.3.2、測試類(使用代理對象)
package tx3_test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import tx3_service.AccountService; public class TestApp { @Test public void demo01(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); AccountService accountService = applicationContext.getBean("proxyAccountServiceId",AccountService.class); accountService.transfer("rose","jack",20); } }
3.4、AOP配置基於XML【掌握】
- 在spring xml 配置 aop自動生成代理,進行事務管理
1、配置管理器
2、配置事務詳情
3、配置aop
3.4.1、配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--1、配置datasource--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value = "com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value = "jdbc:mysql://localhost:3306/spring03"></property> <property name="user" value = "root"></property> <property name="password" value = "hmx123456"></property> </bean> <!--2、配置dao--> <bean id = "accountDaoId" class="tx4_dao_impl.AccountDaoImpl"> <property name="dataSource" ref = "dataSource"></property> </bean> <!--3、配置service--> <bean id = "accountServiceId" class="tx4_service_impl.AccountServiceImpl"> <property name="accountDao" ref = "accountDaoId"></property> </bean> <!--4、事務管理--> <!--4.1 事務管理器 --> <bean id = "txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--4.2 事務詳情:(事務通知):在aop篩選的基礎上,對ABC三個確定使用什麼樣的事務。例如:A和C是讀寫的,B是隻讀的 <tx:attributes>:用於配置事務的詳情(屬性) <tx:method name=""/>:詳情的具體配置 propagation 傳播行爲 isolation:隔離級別 --> <tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT" /> </tx:attributes> </tx:advice> <!--4.3 AOP編程,目標類有ABCD4個方法(即四個連接點),切入點表達式:確定需要增強的連接點,從而獲得切入點:--> <aop:config> <aop:advisor advice-ref="txAdvice" pointcut="execution(* tx4_service..*.*(..))"></aop:advisor> </aop:config> </beans
3.4.2、測試類
package tx4_test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import tx4_service.AccountService; public class TestApp { @Test public void demo(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); AccountService accountService = (AccountService)applicationContext.getBean("accountServiceId"); accountService.transfer("jack","rose",100); } }
3.5、AOP配置基於註解
- 配置事務管理器,將事務管理器交與Spring
- 在目標類或者目標方法添加註解 @Transactional
3.5.1、Spring的配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--1、配置datasource--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value = "com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value = "jdbc:mysql://localhost:3306/spring03"></property> <property name="user" value = "root"></property> <property name="password" value = "hmx123456"></property> </bean> <bean id = "accountDao" class="tx5_dao_impl.AccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id = "accountService" class="tx5_service_impl.AccountServiceImpl"> <property name="accountDao" ref = "accountDao"></property> </bean> <!--配置事務管理 1、事務管理器 2、將事務管理器交予spring --> <bean id = "txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref = "dataSource"></property> </bean> <!--將管理器交與spring--> <tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven> </beans>
3.5.2、實現類中添加註解
package tx5_service_impl; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import tx5_dao.AccountDao; import tx5_service.AccountService; //@Transactional:如果給類加註解,那麼就是對類,該類中的所有方法都適用 public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } @Override @Transactional(propagation= Propagation.REQUIRED,isolation = Isolation.DEFAULT) public void transfer(String outer, String inner, Integer money) { accountDao.outer(outer,money); accountDao.inner(inner,money); } }