到底什么是事務管理?
場景:
下訂單,需要對數據的操作有
- 檢查庫存
- 扣除庫存
- 生成訂單記錄
三次數據操作的集合就是一個事務。
對于這樣的事務,報錯情況有
- 庫存不滿足 拋出 NotEnoughException
- 扣除庫存時失敗 拋出 RemoveInventoryException
- 生成訂單時失敗 拋出 CreateOrderException
如果第一步和第二步出錯還好,可以直接在業務邏輯里處理Exception就好,但是如果在第三步的時候除了錯,就需要把第二步扣除掉的庫存恢復。這是最原始的邏輯。
數據庫提供了事務操作的語句
begin
commit
rollback
對應Java 封裝操作
Connection cnn=null;
conn=DriverManager.getConnection(url,user,password);
conn.setAutoCommit(false);
conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
//這里執行語句
//如果成功的話
conn.commit();
//如果失敗的話
conn.rollback();
如果你愿意,可以把所有的業務邏輯這么封裝。但是如果借助AOP+Spring的話,一切都變得異常簡單了!
從配置開始
Maven中添加tx依賴庫(其余Spring系列、Mybatis 等參照前文配置)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
依然是Java Config,只需要兩步,第一步配置DataSourceTransactionManager
成Bean
。第二步聲明使用注解事務@EnableTransactionManagement
因為我將DataSource
配置在了 MyBatisConfig.java
內,所以和數據庫關聯的一起配置在內了(亦可新建.java
,注解@Configuration
,在WebMVCIniatializer.java
內的getRootConfigClasses()
里添加這個配置類)
完整類如下。
@Configuration
@MapperScan("jufou.info.mapper")
@EnableTransactionManagement
public class MyBatisConfig {
@Bean
public DataSource dataSource(){
BasicDataSource basicDataSource=new BasicDataSource();
basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
basicDataSource.setUsername("root");
basicDataSource.setPassword("Nieve7658");
basicDataSource.setUrl("jdbc:mysql://localhost:3306/test");
return basicDataSource;
}
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(){
SqlSessionFactoryBean sqlSessionFactoryBean=new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
try {
sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:/jufou/info/mybatis/mappers.xml"));
} catch (IOException e) {
e.printStackTrace();
}
return sqlSessionFactoryBean;
}
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(){
return new DataSourceTransactionManager(dataSource());
}
@Bean
public SqlSession sqlSession(){
SqlSessionTemplate sqlSessionTemplate= null;
try {
sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactoryBean().getObject());
} catch (Exception e) {
e.printStackTrace();
}
finally {
return sqlSessionTemplate;
}
}
}
使用注解事務管理
在你需要事務管理函數上使用@Transactional
即可。如果在類上注解,則該類所有函數都會有事務管理
事務管理的具體配置
事務管理配置分為
傳播行為
REQUIRED:業務方法需要在一個容器里運行。如果方法運行時,已經處在一個事務中,那么加入到這個事務,否則自己新建一個新的事務。
NOT_SUPPORTED:聲明方法不需要事務。如果方法沒有關聯到一個事務,容器不會為他開啟事務,如果方法在一個事務中被調用,該事務會被掛起,調用結束后,原先的事務會恢復執行。
REQUIRESNEW:不管是否存在事務,該方法總匯為自己發起一個新的事務。如果方法已經運行在一個事務中,則原有事務掛起,新的事務被創建。
MANDATORY:該方法只能在一個已經存在的事務中執行,業務方法不能發起自己的事務。如果在沒有事務的環境下被調用,容器拋出例外。
SUPPORTS:該方法在某個事務范圍內被調用,則方法成為該事務的一部分。如果方法在該事務范圍外被調用,該方法就在沒有事務的環境下執行。
NEVER:該方法絕對不能在事務范圍內執行。如果在就拋例外。只有該方法沒有關聯到任何事務,才正常執行。
NESTED:如果一個活動的事務存在,則運行在一個嵌套的事務中。如果沒有活動事務,則按REQUIRED屬性執行。它使用了一個單獨的事務,這個事務擁有多個可以回滾的保存點。內部事務的回滾不會對外部事務造成影響。它只對DataSourceTransactionManager事務管理器起效。
隔離級別(來自百度百科)
未授權讀取也稱為讀未提交(Read Uncommitted):允許臟讀取,但不允許更新丟失。如果一個事務已經開始寫數據,則另外一個事務則不允許同時進行寫操作,但允許其他事務讀此行數據。該隔離級別可以通過“排他寫鎖”實現。
授權讀取也稱為讀提交(Read Committed):允許不可重復讀取,但不允許臟讀取。這可以通過“瞬間共享讀鎖”和“排他寫鎖”實現。讀取數據的事務允許其他事務繼續訪問該行數據,但是未提交的寫事務將會禁止其他事務訪問該行。
可重復讀取(Repeatable Read):禁止[不可重復讀取]和臟讀取,但是有時可能出現幻讀數據。這可以通過“共享讀鎖”和“排他寫鎖”實現。讀取數據的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。
序列化(Serializable):提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接著一個地執行,不能并發執行。僅僅通過“行級鎖”是無法實現事務序列化的,必須通過其他機制保證新插入的數據不會被剛執行查詢操作的事務訪問到。
設定對特定Exception生效
默認對所有Exception生效
使用rollbackFor
與notRollbackFor
完成
Sample
@Transactional(isolation = Isolation.READ_COMMITTED,rollbackFor = Exception.class,propagation = Propagation.REQUIRED)