Spring的事務(Transaction)
- 事務的相關問題
- 1、什么是事務?
- 2、事務的特點(ACID)
- 3、什么時候想到使用事務?
- 4、通常使用JDBC訪問資料庫,還是mybatis訪問資料庫,怎么處理事務?
- 5、4問題中事務處理的方式有什么不足?
- 6、怎么解決不足?
- 7、處理事務,需要怎么做,做什么?
- (1)事務內部提交、回滾事務使用的是事務管理器物件,代替手動commit、rollback,事務管理器是一個介面和其眾多的實作類,
- (2)業務方法需要什么樣的事務?說明需要事務的型別,
- 事務的隔離級別
- 事務的超時時間
- 事務的傳播行為
- (3)提交事務、回滾事務的時機
- 總結spring事務
- spring框架中提供的事務處理方案(基于AOP)
- 基于注解的方案:(@Transactional):適用于中小型專案 >>【關鍵代碼】
- spring組態檔(applicationContext.xml)
- 使用@Transactional 業務層方法添加事務
- 基于XML組態檔方案(AspectJ框架):適用大型專案 >>【關鍵代碼】
- 業務代碼
- spring的組態檔(applicationContext.xml)
事務的相關問題
1、什么是事務?
事務是指一組SQL陳述句的集合,集合中有多條SQL陳述句,可以是insert、update、select、delete,希望這些SQL陳述句執行是一致的,作為一個整體執行,要么都成功,要么都失敗,
2、事務的特點(ACID)
原子性(Atomicity):事務是一個原子操作,由一系列動作組成,事務的原子性確保動作要么全部完成,要么完全不起作用,
一致性(Consistency):一旦事務完成(不管成功還是失敗),系統必須確保它所建模的業務處于一致的狀態,而不會是部分完成部分失敗,在現實中的資料不應該被破壞,
隔離性(Isolation):可能有許多事務會同時處理相同的資料,因此每個事務都應該與其他事務隔離開來,防止資料損壞,
持久性(Durability):一旦事務完成,無論發生什么系統錯誤,它的結果都不應該受到影響,這樣就能從任何系統崩潰中恢復過來,通常情況下,事務的結果被寫到持久化存盤器中,
3、什么時候想到使用事務?
(1)當操作設計多個表,或者是多個SQL陳述句的insert、update、delete,需要保證這些陳述句都是成功才能完成功能,或者都失敗是符合要求的,(
要么都成功,要么都失敗)
(2)事務在java開發中如何運用?1、事務放在service類的業務方法中,因為業務方法會呼叫多個dao,執行多條SQL陳述句
4、通常使用JDBC訪問資料庫,還是mybatis訪問資料庫,怎么處理事務?
(1)jdbc訪問資料,處理事務,Connection conn;conn.commit();conn.rollback();
(2)mybatis訪問資料庫,處理事務,SqlSession.commit() ;SqlSession.rollback();
(3)hibernate訪問詩句哭,處理事務,Session.commit(); Session.rollback();
5、4問題中事務處理的方式有什么不足?
(1)不同資料庫訪問技術,處理事務的物件、方法不同,需要了解不同資料庫使用事務的原理,
(2)需要掌握多種資料庫事務的處理邏輯,什么時候提交事務,什么時候回滾事務,
(3)處理事務的方法種類多,
總結:就是多種資料庫訪問技術,不同的事務處理的機制、物件、方法,較難掌握,
6、怎么解決不足?
spring提供了處理事務的統一模型,能夠使用統一的步驟、方式完成多種不同資料庫訪問技術的事務處理,使用spring的事務處理機制可以完成mybatis、hibernate訪問資料庫的事務處理,
7、處理事務,需要怎么做,做什么?
spring處理事務的模型,使用的步驟都是固定的,把事務使用的資訊都提供給spring就可以了,
(1)事務內部提交、回滾事務使用的是事務管理器物件,代替手動commit、rollback,事務管理器是一個介面和其眾多的實作類,
介面:PlatformTransactionManager,定義了事務的重要方法 commit、rollback
實作類:spring把每一種資料庫訪問技術對應的事務處理累都創建好了,
a、mybatis訪問資料庫—spring創建好的是DataSourceTransactionManager
b、hibernate訪問資料庫—spring創建好的是HibernateTransactionManager
怎么使用?
只需告訴spring使用哪種資料庫的訪問技術(框架),宣告資料庫訪問技術對應的事務管理器的實作類,在spring的組態檔中使用>><bean>宣告就可以了,例如使用mybatis訪問資料庫:<bean id = "xxx" class = "...DataSourceTransactionManager"/>
(2)業務方法需要什么樣的事務?說明需要事務的型別,
事務的隔離級別
有 5 個值,其中一個是默認,這些常量軍事以ISOLATION_開頭,即形如:ISOLATION_XXX,
1、DEFAULT:采用DB默認的事務隔離級別,MySQL的默認隔離級別:REPEATABLE_READ(可重復讀);Oracle默認的隔離級別:READ_COMMITTED(讀已提交)
2、READ_UNCOMMITTED:讀未提,,未解決任何并發問題,
3、READ_COMMITTED:讀已提交,解決度臟資料,存在不可重復讀與幻讀,
4、REPEATABLE_READ:可重復度,解決臟讀、不可重復讀,存在幻讀,
5、SERIALIZABLE:串行化,不存在并發問題,
事務的超時時間
表示一個方法最長的執行時間,如果方法執行時超過了這個時間,事務就回滾,單位是秒,整數值,默認是:-1(表示沒有限制最長時間),
事務的傳播行為
控制業務方法是不是有事務的,是什么樣的事務的,共有 7 個傳播行為,(標紅常用需掌握)
(1)PROPAGATION_REQUIRED:指定的方法必須在事務內執行,若當前存在事務,就加入當前事務中;若當前沒有事務,則創建一個新事務,這種創博行為時最常見的選擇,也是spring默認的事務傳播行為,
(2)PROPAGATION_REQUIRED_NEW:總是新建一個事務,若當前存在事務,就將當前事務掛起,知道新事物執行完畢,
(3)PROPAGATION_SUPPORTS:指定方法支持當前事務,但若當前沒有事務,也可以以非事務的方式執行,
(4)PROPAGATION_MANDATORY
(5)PROPAGATION_NESTED
(6)PROPAGATION_NEVER
(7)PROPAGATION_NOT_SUPPORTED
(3)提交事務、回滾事務的時機
1、當業務方法執行成功,沒有例外拋出,且方法執行完畢時,spring在方法執行后提交事務,事務管理器commit,
2、當業務代碼拋出運行時例外或ERROR,spring執行回滾,呼叫事務管理器rollback,
3、當業務方法拋出非運行時例外,主要是受查例外時,默認是提交事務,
受查例外:寫代碼是必須處理的例外,例如:IOException、SQLException
總結spring事務
1、管理事務的是,事務管理器和其實作類
2、spring的事務是一個統一的模型
(1)指定要使用的事務管理器實作類,使用<bean>
(2)指定哪些類,哪些方法需要加入事務的功能
(3)指定方法需要的事務的隔離級別、傳播行為、超時時間,
spring框架中提供的事務處理方案(基于AOP)
1、專案的構建可參考:https://blog.csdn.net/qq_36763419/article/details/113925334
基于注解的方案:(@Transactional):適用于中小型專案 >>【關鍵代碼】
spring框架用aop實作給業務方法增加事務的功能,使用
@Transactional注解增加事務,@Transactional注解是spring自己的注解,放在 public 方法的上面,表示當前這個方法具有事務,可以給注解的屬性賦值,來表示具體的隔離級別、傳播行為、例外資訊等等,
@Transactional的屬性
isolation:用于設定隔離級別,該屬性型別為isolation列舉,默認值為 Isolation.DEFUALT,
readOnly:用于設定方法對資料庫的操作是否制度的,該屬性為boolean,默認值為false,
timeout:用于設定本操作與資料庫連接超時時限,單位為妙,型別為int,默認值為-1,即沒有時限,
rollbackFor:指定需要回滾的例外嘞,型別為Class[],默認值為空陣列,當然,若只有一個例外類,可以不使用陣列,
rollbackForClassName:指定需要回滾的例外類類名,型別為String[],默認值為空陣列,當然,若只有一個例外類,可以不使用陣列,
noRollbackFor:指定不需要回滾的例外類,型別為Class[],默認值為空陣列,當然,若只有一個例外類,可以不使用陣列,(與rollbackFor相反)
noRollbackForClassName:指定不需要回滾的例外類類名,型別為Class[],默認值為空陣列,當然,若只有一個例外類,可以不使用陣列,(與rollbackForClassName相反)
使用注解的步驟:
(1)宣告事務管理器物件<!-- mybatis的事務管理器,id自定義 --> <!--1、宣告spring的事務管理器,id自定義--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--連接的資料庫,指定資料源--> <property name="dataSource" ref="myDataSource"/> </bean>(2)開啟事務注解驅動,告訴spring,使用注解的方式管理事務,spring使用aop機制,創建@Transactional所在的類代理物件,給方法加入事務功能,
<!--2、開啟事務注解驅動,告訴spring使用注解管理事務,創建帶物件 transaction-manager:事務管理器id --> <tx:annotation-driven transaction-manager="transactionManager"/>
spring給業務方法加入事務:
在業務方法執行之前,開啟事務;在業務方法之后,提交或回滾事務,使用的是 aop的環繞通知,環繞通知由spring完成,@Around("增加事務功能的業務方法名稱") Object myAround(){ //開啟事務,spring開啟 try{ <!--業務代碼--> //spring的事務管理 .rollback() }catch(Exception e){ //spring的事務管理 .rollback() } //開啟事務,spring開啟 }(3)在目標方法(需要增加事務功能的)方法上面加入@Transactional注解
spring組態檔(applicationContext.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: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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--引入properties屬性檔案-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--使用阿里云的druid連接池-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="${maxActive}"/>
</bean>
<!--
宣告的是mybatis中提供的SqlSessionFactoryBean類,
這個類內部創建SqlSessionFactory的
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<!--mybatis主組態檔的位置
configLocation的屬性是Resource型別,讀取外部的組態檔的
它的賦值,使用value,指定組態檔的路徑,使用classpath:組態檔
-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--創建dao物件,使用SqlSession.getMapper(Student.class)
MapperScannerConfigurer:在內部呼叫getMapper()生成每個dao介面的代理物件
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory物件的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--指定包名,包名是dao介面所在的包名
MapperScannerConfigurer會掃描這個包中的所有介面,包每個介面都執行一次getMapper()方法,得到每個介面的dao物件
創建好的dao物件放入到spring容器中,dao物件的名稱是介面名字首字母小寫
-->
<property name="basePackage" value="com.spring.dao"/>
</bean>
<!--buyGoodsService-->
<bean id="buyService" class="com.spring.service.BuyGoodsServiceImpl">
<property name="saleDao" ref="saleDao"/>
<property name="goodsDao" ref="goodsDao"/>
</bean>
<!--使用spring的事務處理-->
<!--1、宣告spring的事務管理器,id自定義-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--連接的資料庫,指定資料源-->
<property name="dataSource" ref="myDataSource"/>
</bean>
<!--2、開啟事務注解驅動,告訴spring使用注解管理事務,創建帶物件
transaction-manager:事務管理器id
-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
使用@Transactional 業務層方法添加事務
package com.spring.service;
import com.spring.dao.GoodsDao;
import com.spring.dao.SaleDao;
import com.spring.entity.Goods;
import com.spring.entity.Sale;
import com.spring.exception.NotEnoughException;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class BuyGoodsServiceImpl implements BuyGoodsService {
private SaleDao saleDao;
private GoodsDao goodsDao;
public void setSaleDao(SaleDao saleDao) {
this.saleDao = saleDao;
}
public void setGoodsDao(GoodsDao goodsDao) {
this.goodsDao = goodsDao;
}
/**
* propagation 傳播行為
* isolation 隔離級別
* rollbackFor 表示發生陣列中包含的例外一定會回滾
* 處理邏輯:
* (1)spring框架首先會檢查方法拋出的例外是否在rollbackFor屬性值中,
* 如果例外在rollbackFor串列中,不論時候是你么型別的例外,一定會回滾
* (2)如果拋出的例外不在rollbackFor屬性之中,spring會判斷例外是不是RuntimeException,
* 如果是一定會滾,
*
* @param goodsId
* @param nums
*/
/*@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false,
rollbackFor = {
NullPointerException.class,
NotEnoughException.class
}
)*/
/*@Transactional的默認值
* 同上注釋的配置
* 默認當發生運行時例外時,事務回滾
*
*
* */
@Transactional
@Override
public void buy(Integer goodsId, Integer nums) {
System.out.println("======== buy方法開始 ======");
//記錄銷售資訊,向sale表添加記錄
Sale sale = new Sale();
sale.setGid(goodsId);
sale.setNums(nums);
saleDao.insertSale(sale);
//更新庫存
//更新之前查詢商品資訊
Goods goods = goodsDao.selectGoodsById(goodsId);
System.out.println(goods.toString());
if (goods == null) {
//說明說明商品不存在
throw new NullPointerException("編號為:" + goodsId + "的商品不存在");
} else if (goods.getAmount() < nums) {
throw new NotEnoughException("編號為:" + goodsId + "的商品庫存不足");
}
Goods buyGoods = new Goods();
buyGoods.setId(goodsId);
buyGoods.setAmount(nums);
goodsDao.updateGoods(buyGoods);
System.out.println("======== buy方法完成 ======");
}
}
基于XML組態檔方案(AspectJ框架):適用大型專案 >>【關鍵代碼】
1、大型專案中有很多類,方法,需要大量的配置事務,使用aspectj框架功能,在spring組態檔中宣告類,方法需要的事務,這種方式業務方法和事務配置完全分離,
2、實作步驟:都是在xml組態檔中實作的,
(1)要使用的是aspectj框架,加入依賴<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency>(2)宣告事務管理器物件
<!-- mybatis的事務管理器,id自定義 --> <!--1、宣告spring的事務管理器,id自定義--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--連接的資料庫,指定資料源--> <property name="dataSource" ref="myDataSource"/> </bean>(3)宣告方法需要的事務型別(配置方法的事務屬性:
隔離級別、傳播行為、超時)
(4)配置AOP:指定哪些類需要創建代理物件,
業務代碼
package com.spring.service;
import com.spring.dao.GoodsDao;
import com.spring.dao.SaleDao;
import com.spring.entity.Goods;
import com.spring.entity.Sale;
import com.spring.exception.NotEnoughException;
import org.springframework.transaction.annotation.Transactional;
public class BuyGoodsServiceImpl implements BuyGoodsService {
private SaleDao saleDao;
private GoodsDao goodsDao;
public void setSaleDao(SaleDao saleDao) {
this.saleDao = saleDao;
}
public void setGoodsDao(GoodsDao goodsDao) {
this.goodsDao = goodsDao;
}
/**
* 事務所用的方法
*/
@Override
public void buy(Integer goodsId, Integer nums) {
System.out.println("======== buy方法開始 ======");
//記錄銷售資訊,向sale表添加記錄
Sale sale = new Sale();
sale.setGid(goodsId);
sale.setNums(nums);
saleDao.insertSale(sale);
//更新庫存
//更新之前查詢商品資訊
Goods goods = goodsDao.selectGoodsById(goodsId);
System.out.println(goods.toString());
if (goods == null) {
//說明說明商品不存在
throw new NullPointerException("編號為:" + goodsId + "的商品不存在");
} else if (goods.getAmount() < nums) {
throw new NotEnoughException("編號為:" + goodsId + "的商品庫存不足");
}
Goods buyGoods = new Goods();
buyGoods.setId(goodsId);
buyGoods.setAmount(nums);
goodsDao.updateGoods(buyGoods);
System.out.println("======== buy方法完成 ======");
}
}
spring的組態檔(applicationContext.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:tx="http://www.springframework.org/schema/tx" 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/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--引入properties屬性檔案-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--使用阿里云的druid連接池-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="${maxActive}"/>
</bean>
<!--
宣告的是mybatis中提供的SqlSessionFactoryBean類,
這個類內部創建SqlSessionFactory的
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="myDataSource"/>
<!--mybatis主組態檔的位置
configLocation的屬性是Resource型別,讀取外部的組態檔的
它的賦值,使用value,指定組態檔的路徑,使用classpath:組態檔
-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--創建dao物件,使用SqlSession.getMapper(Student.class)
MapperScannerConfigurer:在內部呼叫getMapper()生成每個dao介面的代理物件
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory物件的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!--指定包名,包名是dao介面所在的包名
MapperScannerConfigurer會掃描這個包中的所有介面,包每個介面都執行一次getMapper()方法,得到每個介面的dao物件
創建好的dao物件放入到spring容器中,dao物件的名稱是介面名字首字母小寫
-->
<property name="basePackage" value="com.spring.dao"/>
</bean>
<!--buyGoodsService-->
<bean id="buyService" class="com.spring.service.BuyGoodsServiceImpl">
<property name="saleDao" ref="saleDao"/>
<property name="goodsDao" ref="goodsDao"/>
</bean>
<!--宣告式事務處理,和源代碼完全分離-->
<!--1、宣告事務管理器物件-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSource"/>
</bean>
<!--2、宣告業務方法的事務屬性(隔離級別、傳播行為、超時時間)
id:自定義名稱,表示<tx:advice>和</tx:advice>之間的配置內容的
transaction-manager:事務管理其物件的id
-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<!--tx:attributes :配置事務屬性-->
<tx:attributes>
<!--tx:method :給具體的方法配置事務屬性,method可以有多個,分別給不用的方法設定事務屬性
name:方法名稱:(1)完整的方法名稱,不帶包和類名
(2)方法可以使用通配符,*表示任意字符
propagation:傳播行為,列舉值
isolation:隔離級別
rollback-for:指定的例外類名,全限定類名,發生例外一定回滾
-->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.NullPointerException,com.spring.exception.NotEnoughException"/>
<!--使用通配符,指定很多方法-->
<!--添加方法-->
<tx:method name="add*" propagation="REQUIRES_NEW"/>
<!--修改方法-->
<tx:method name="modify*"/>
<!--洗掉方法-->
<tx:method name="remove*"/>
<!--查詢方法,query、search、find-->
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!--配置aop-->
<aop:config>
<!--配置切入點運算式:指定那些包中的類,要使用事務
id:切入點運算式的名稱,唯一值
expression:切入點運算式,指定哪些類要使用事務,aspectj會創建代理物件
-->
<aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
<!--配置增強器:關聯advice和pointCut
advice-ref:通知,上面tx:advice
pointcut-ref:切入點運算式
-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt"/>
</aop:config>
</beans>
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/263334.html
標籤:其他
下一篇:sql防注入
