回顧
先來回顧一下事務的相關知識吧;
事務的概念:
- 事務是一種SQL 語句的執行,要麼成功,要麼失敗,不能出現部分成功,部分失敗,具有原子性;
- 事務所有的SQL全部執行完,才能提交(commit)事務,將數據存儲到磁盤;
- 事務執行過程中只要有SQL出現問題,那麼事務就必須回滾到最初的狀態;
事務的特徵:ACID
- A:事務的原子性;事務是一個不可分割的整體,事務必須具有原子特徵,事務操作時,要麼全部執行,要麼全部不執行;
- C:事務的一致性;一個事務執行之前和之後,數據庫數據必須保持一致性狀態;(比如銀行轉賬時,金額總和不變);
- I:事務的隔離性;當兩個或者多個事務併發執行時,爲保證數據的安全,將一個事務內的操作和其他事務隔離起來,不被其他正在執行的事務看到;
- D:事務的持久性;事務完成之後,數據庫保證數據庫中的數據修改是永久性的,即使數據庫出現故障,也能保證恢復數據;
事務隔離性使用不當會照成髒數據問題:
- 髒讀:一個事務讀取了另一個事務未提交的數據;(當事務A和事務B併發操作時,事務A更新數據後,事務B讀取到A未提交的數據,此時事務A回滾,事務B就讀取到了事務A未提交的無效的髒數據);
- 不可重複讀:一個事務操作導致另一個事務讀取到前後兩次不同的數據;(例如,事務A和事務B進行併發操作,事務B查詢讀取數據後,事務A更新操作事務B讀取的數據,此時事務B繼續查詢數據,會發現自己前後兩次讀取的數據結果不一致);
- 幻讀:一個事務的操作導致另一個事務前後兩次查詢的結果的數量不同;(例如,事務A和事務B進行併發操作,當事務B查詢讀取數據後,事務A操作新增或刪除了一條滿足事務B的條件的數據,此時事務B再次進行查詢到前一次不存在的數據或者前一次沒有的數據),(事務B讀取了事務A新增的內容或者讀不到事務A刪除的數據);
在JDBC中,定義了以下5種事務隔離級別:
- TRANSACTION_NONE。 表示不支持事務
- TRANSACTION_READ_UNCOMMITTED。未提交讀。說明在提交前一個事務可以看到另一個事務的變化。這樣讀”髒”數據,不可重複讀和虛讀都是被允許的。
- TRANSACTION_READ_COMMITTED。已提交讀。說明讀取未提交的數據是不允許的。這個級別仍然允許不可重複讀和虛讀產生。
- TRANSACTION_REPEATABLE_READ。可重複讀。說明事務保證能夠再次讀取相同的數據而不會失敗,但虛讀仍然會出現。
- TRANSACTION_SERIALIZABLE。可序列化/串行化。是最高的事務級別,它防止讀髒數據,不可重複讀和虛讀。
現在開始看看Spring是如何管理事務的吧,它有兩種實現方式,即通過配置或註解實現;
Spring對事務管理的兩種實現方式
下面寫的都是僞代碼啊,重在Spring對事務的管理是怎麼實現的:
通過配置實現
使用步驟:
- 引入依賴;
- 創建相關的類;
- 創建配置文件;
- 使用;
一、引入依賴;
和前面幾節用的依賴一樣,略;
二、創建類
裏面是僞代碼,並沒有進行真正的數據庫操作;
public class StudentDao {
//插入多個學生是需要同時成功的,這就需要藉助事務處理
public void add() {
//添加多個學生
System.out.println("事務處理中");
System.out.println("添加學生的方法");
}
}
三、創建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: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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--配置數據源 通過連接池c3p0來操作-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--配置數據連接的核心配置-->
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--第一步 配置事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--第二步 配置事務增強(AOP)即把事務當增強來處理-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--事務操作-->
<tx:attributes>
<!--設置進行事務操作的方法匹配 -->
<!--name標籤:匹配方法 add*: 所有的以add開頭的方法都要進行事務操作-->
<tx:method name="add*"/>
</tx:attributes>
</tx:advice>
<!--第三步:配置切面-->
<aop:config>
<!--切入點 實際需要被增強的方法-->
<aop:pointcut id="pointcut" expression="execution(* com.tulun7.dao.StudentDao.add(..))"/>
<!--配置切面 將切入點進行增強的過程-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
<!--創建StudentDao對象,這個時候只要調用該對象的方法,就會進行事務處理-->
<bean id="studentDao" class="com.tulun7.dao.StudentDao"/>
</beans>
四、使用
public class TestDemo1 {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("事務/spring-transaction.xml");
StudentDao studentDao = (StudentDao) context.getBean("studentDao");
studentDao.add();
}
}
通過註解實現
註解實現就更簡單了,所以說在Spring裏面更推薦使用註解;
使用步驟:
- 引入依賴;
- 創建相關的類;
- 創建配置文件;
- 使用;
一、引入依賴
和前面幾節用的依賴一樣,不過需要注意的是,因爲這是使用註解實現,所以對版本的要求比較高,以前我引入的依賴都是 4.1.7 版本的,然後我寫完它一直報錯,我檢查了幾遍覺得沒錯啊,但就是報錯,最後搜博客,有人說是包沒引,但需要的就這些jar包,也沒錯,最後有博主說,是版本問題,最後我將 4.1.7 改成 4.3.9 就對了,所以注意這裏需要改版本號;
二、創建相關的類
還是一樣,只不過這裏需要添加註解@Transactional,如果需要對這個類進行事務操作,將@Transactional添加到類前面就可以了,這裏我只需要對這個方法進行事務操作,所以我就只添加在了add方法前面:
public class StudentDao1 {
@Transactional
public void add() {
//添加多個學生
System.out.println("事務處理中");
System.out.println("添加學生的方法");
}
}
三、創建配置文件
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.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">
<!--配置數據源 通過連接池c3p0來操作-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!--配置數據連接的核心配置-->
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="user" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--第一步 配置事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入dataSource-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--第二步 開啓事務註解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
<!--第三步 在對應的類或者方法上面添加註解@Transactional-->
<!--創建StudentDao對象,這個時候只要調用該對象的方法-->
<bean id="studentDao" class="com.tulun7.dao.StudentDao1"/>
</beans>
四、使用
public class TestDemo2 {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("事務/註解實現/spring.xml");
StudentDao1 studentDao1 = (StudentDao1) context.getBean("studentDao");
studentDao1.add();
}
}