1.Spring事務(wù)管理概述
Spring的事務(wù)管理簡(jiǎn)化了傳統(tǒng)的事務(wù)管理流程,并且在一定程度上減少了開(kāi)發(fā)者的工作量。
1.1 事務(wù)管理的核心接口
在Spring的所有JAR包中包含一個(gè)名為Spring-tx-4.3.6.RELEASE的JAR包,該包就是Spring提供的用于事務(wù)管理的依賴(lài)包。在該JAR包的org.Springframework.transaction包中有3個(gè)接口文件:PlatformTransactionManager、TransactionDefinition和TransactionStatus。
1. PlatformTransactionManager
PlatformTransactionManager接口是Spring提供的平臺(tái)事務(wù)管理器,主要用于管理事務(wù)。該接口中提供了3個(gè)事務(wù)操作的方法,具體如下。
-
TransactionStatus getTransaction(TransactionDefinition definition):用于獲取事務(wù)狀態(tài)信息。該方法會(huì)根據(jù)TransactionDefinition參數(shù)返回一個(gè)TransactionStatus對(duì)象。TransactionStatus對(duì)象表示一個(gè)事務(wù),被關(guān)聯(lián)在當(dāng)前執(zhí)行的線程上。
-
void commit(TransactionStatus status):用于提交事務(wù)。
-
void rollback(TransactionStatus status):用于回滾事務(wù)。
PlatformTransactionManager接口只是代表事務(wù)管理的接口,并不知道底層是如何管理事務(wù)的,它只需要事務(wù)管理提供上面的3個(gè)方法,但具體如何管理事務(wù)則由它的實(shí)現(xiàn)類(lèi)來(lái)完成。
PlatformTransactionManager接口有許多不同的實(shí)現(xiàn)類(lèi),常見(jiàn)的幾個(gè)實(shí)現(xiàn)類(lèi)如下。
- org.springframework.jdbc.datasource.DataSourceTransactionManager:用于配置JDBC數(shù)據(jù)源的事務(wù)管理器。
- org.springframework.orm.Hibernate4.HibernateTransactionManager:用于配置Hibernate的事務(wù)管理器。
- org.springframework.transaction.jta.JtaTransactionManager:用于配置全局事務(wù)管理器。
當(dāng)?shù)讓硬捎貌煌某志脤蛹夹g(shù)時(shí),系統(tǒng)只需使用不同的PlatformTransactionManager實(shí)現(xiàn)類(lèi)即可。
2. TransactionDefinition
TransactionDefinition接口是事務(wù)定義(描述)的對(duì)象,該對(duì)象中定義了事務(wù)規(guī)則,并提供了獲取事務(wù)相關(guān)信息的方法,具體如下:
- string getName():獲取事務(wù)對(duì)象名稱(chēng)。
- int getlsolationLeve():獲取事務(wù)的隔離級(jí)別。
- int getPropagationBehavior():獲取事務(wù)的傳播行為。
- int setTimeout():獲取事務(wù)的超時(shí)時(shí)間。
- boolean isReadOnly():獲取事務(wù)是否只讀。
上述方法中,事務(wù)的傳播行為是指在同一個(gè)方法中,不同操作前后所使用的事務(wù)。傳播行為有很多種,具體如表所示。
在事務(wù)管理過(guò)程中,傳播行為可以控制是否需要?jiǎng)?chuàng)建事務(wù)以及如何創(chuàng)建事務(wù)。通常情況下,數(shù)據(jù)的查詢(xún)不會(huì)影響原數(shù)據(jù)的改變,所以不需要進(jìn)行事務(wù)管理,而對(duì)于數(shù)據(jù)的插入、更新和刪除操作,必須進(jìn)行事務(wù)管理。如果沒(méi)有指定事務(wù)的傳播行為,Spring默認(rèn)傳播行為是REQUIRED。
3. TransactionStatus
TransactionStatus接口是事務(wù)的狀態(tài),描述了某一時(shí)間點(diǎn)上事務(wù)的狀態(tài)信息。該接口中包含6個(gè)方法,具體如下:
- void flush():刷新事務(wù)。
- boolean hasSavepoint():獲取是否存在保存點(diǎn)。
- boolean isCompleted():獲取事務(wù)是否完成。
- boolean isNewTransaction():獲取是否是新事務(wù)。
- boolean isRollbackOnly():獲取是否回滾。
- void setRollbackOnly():設(shè)置事務(wù)回滾。
1.2 事務(wù)管理的方式
Spring中的事務(wù)管理分為兩種方式:一種是傳統(tǒng)的編程序事務(wù)管理;另一種是聲明式事務(wù)管理。
- 編程序事務(wù)管理:通過(guò)編寫(xiě)代碼實(shí)現(xiàn)的事務(wù)管理,包括定義事務(wù)的開(kāi)始、正常執(zhí)行后的事務(wù)提交和異常時(shí)的事務(wù)回滾。
- 聲明式事務(wù)管理:通過(guò)AOP技術(shù)實(shí)現(xiàn)的事務(wù)管理,其主要思想是將事務(wù)管理作為一個(gè)“切面”代碼單獨(dú)編寫(xiě),然后通過(guò)AOP技術(shù)將事務(wù)管理的“切面”代碼植入業(yè)務(wù)目標(biāo)類(lèi)中。
聲明式事務(wù)管理最大的優(yōu)點(diǎn)在于開(kāi)發(fā)者無(wú)須通過(guò)編程的方式來(lái)管理事務(wù),只需在配置文件中進(jìn)行相關(guān)的事務(wù)規(guī)則聲明,就可以將事務(wù)規(guī)則應(yīng)用到業(yè)務(wù)邏輯中。這使得開(kāi)發(fā)人員可以更加專(zhuān)注于核心業(yè)務(wù)邏輯代碼的編寫(xiě),在一定程度上減少了工作量,提高了開(kāi)發(fā)效率。所以在實(shí)際開(kāi)發(fā)中,通常都推薦使用聲明式事務(wù)管理。
2.聲明式事務(wù)管理
Spring的聲明式事務(wù)管理可以通過(guò)兩種方式來(lái)實(shí)現(xiàn):一種是基于XML的方式;另一種是基于Annotation的方式。
2.1 基于XML方式的聲明式事務(wù)
基于XML方式的聲明式事務(wù)管理是通過(guò)在配置文件中配置事務(wù)規(guī)則的相關(guān)聲明來(lái)實(shí)現(xiàn)的。
Spring 2.0以后,提供了tx命名空間來(lái)配置事務(wù),tx命名空間下提供了tx:advice元素來(lái)配置事務(wù)的通知(增強(qiáng)處理)。當(dāng)使用tx:advice元素配置了事務(wù)的增強(qiáng)處理后,就可以通過(guò)編寫(xiě)的AOP配置讓Spring自動(dòng)對(duì)目標(biāo)生成代理。配置tx:advice元素時(shí),通常需要指定id和transaction-manager屬性,其中id屬性是配置文件中的唯一標(biāo)識(shí),transaction-manager屬性用于指定事務(wù)管理器。除此之外,還需要配置一個(gè)tx:attributes子元素,該子元素可通過(guò)配置多個(gè)tx:method子元素來(lái)配置執(zhí)行事務(wù)的細(xì)節(jié)。
關(guān)于tx:method元素的屬性描述如表所示。
了解了如何在XML文件中配置事務(wù)后,接下來(lái)通過(guò)一個(gè)案例來(lái)演示如何通過(guò)XML方式實(shí)現(xiàn)Spring的聲明式事務(wù)管理。
【示例5-1】本案例以基礎(chǔ)模擬一個(gè)會(huì)員贈(zèng)送積分的功能,要求在贈(zèng)送積分時(shí)通過(guò)Spring對(duì)事務(wù)進(jìn)行控制,其具體實(shí)現(xiàn)步驟如下。
步驟01 在idea中創(chuàng)建一個(gè)名為chapter05的Web項(xiàng)目,在項(xiàng)目的lib目錄中導(dǎo)入JAR包,并將AOP所需JAR包也導(dǎo)入lib目錄中。
步驟02 在MySQL中,修改數(shù)據(jù)庫(kù)db_spring中的數(shù)據(jù)表user,增加字段jf(積分),如圖所示。同時(shí),為了后續(xù)操作數(shù)據(jù)表方便,在數(shù)據(jù)表user中設(shè)置所有用戶的初始積分為1000。
步驟03 將chapter00項(xiàng)目中的代碼和配置文件復(fù)制到chapter05項(xiàng)目的src目錄下。接下來(lái),在User類(lèi)中增加jf(積分)成員和對(duì)應(yīng)的getter()和setter()方法。
private Integer jf;//積分
public Integer getJf() {
return jf;
}
public void setJf(Integer jf) {
this.jf = jf;
}
在UserDao接口中創(chuàng)建一個(gè)贈(zèng)送積分的方法transfer()。
//贈(zèng)送積分
public void transfer(String outUser, String inUser, Integer jf);
在實(shí)現(xiàn)類(lèi)UserDaoImpl中實(shí)現(xiàn)transfer()方法。
//贈(zèng)送積分
public void transfer(String outUser,String inUser, Integer jf) {
//接受積分
this.jdbcTemplate.update("update user set jf = jf + ? where username = ?", jf, inUser);
//模擬系統(tǒng)運(yùn)行時(shí)的突發(fā)性問(wèn)題
int i=1/0;
//贈(zèng)送積分
this.jdbcTemplate.update("update user set jf = jf - ? where username = ?", jf, outUser);
}
在上述代碼中,使用了兩個(gè)update()方法對(duì)user表中的數(shù)據(jù)執(zhí)行接收積分和贈(zèng)送積分的更新操作。在兩個(gè)操作之間添加了一行代碼“int i=1/0;”來(lái)模擬系統(tǒng)運(yùn)行時(shí)的突發(fā)性問(wèn)題。如果沒(méi)有事務(wù)控制,那么在transfer()方法執(zhí)行后,接收積分用戶的積分會(huì)增加,而贈(zèng)送積分用戶的積分會(huì)因?yàn)橄到y(tǒng)出現(xiàn)問(wèn)題而不變,這顯然是有問(wèn)題的;如果增加了事務(wù)控制,那么在transfer()方法操作執(zhí)行后,接收積分用戶的積分和贈(zèng)送積分用戶的積分在問(wèn)題出現(xiàn)前后都應(yīng)該保持不變。
步驟04 修改配置文件applicationContext.xml,添加命名空間并編寫(xiě)事務(wù)管理的相關(guān)配置代碼。
<?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:tx="http://www.springframework.org/schema/cache"
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/cache
http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<!-- 1.配置數(shù)據(jù)源
-->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- 數(shù)據(jù)庫(kù)驅(qū)動(dòng) -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!-- 連接數(shù)據(jù)庫(kù)url -->
<property name="url" value="jdbc:mysql://localhost:3306/db_spring"/>
<!-- 連接數(shù)據(jù)庫(kù)的用戶名 -->
<property name="username" value="root"/>
<!-- 連接數(shù)據(jù)庫(kù)的密碼 -->
<property name="password" value="root"/>
</bean>
<!-- 2.配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 默認(rèn)必須使用數(shù)據(jù)源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 定義id為 UserDao 的 Bean -->
<bean id="UserDao" class="com.ssm.jdbc.UserDaoImpl">
<!-- 將jdbcTemplate 注入userDao實(shí)例中 -->
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<!-- 4.事務(wù)管理器,依賴(lài)于數(shù)據(jù)源-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 5.編寫(xiě)通知:對(duì)事務(wù)進(jìn)行增強(qiáng)(通知),需要編寫(xiě)對(duì)切入點(diǎn)和具體執(zhí)行事務(wù)細(xì)節(jié) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"
isolation="DEFAULT" red-only="false" />
</tx:attributes>
</tx:advice>
<!-- 6.編寫(xiě)aop:讓Spring自動(dòng)對(duì)目標(biāo)生產(chǎn)成代理,需要使用AspectJ的表達(dá)式 -->
<aop:config>
<!-- 切入點(diǎn) -->
<aop:pointcut id="txPointCut" expression="execution(* com.ssm.jdbc.*.*(..))"/>
<!-- 切面 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut" />
</aop:config>
</beans>
定義了id為transactionManager的事務(wù)管理器,接下來(lái)通過(guò)編寫(xiě)的通知來(lái)聲明事務(wù),最后通過(guò)聲明AOP的方式讓Spring自動(dòng)生成代理。
步驟05 在com.ssm.jdbc包中創(chuàng)建測(cè)試類(lèi)TransactionTest,并在類(lèi)中編寫(xiě)測(cè)試方法xmlTest()。
package com.ssm.jdbc;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
/**
* 功能描述
*
* @author: 衍生星球
* @date: 2023年05月06日 17:08
*/
public class TransactionTest {
@Test
public void xmlTest() {
//加載配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//獲取userDao實(shí)例
UserDao userDao = (UserDao)applicationContext.getBean("UserDao");
//贈(zèng)送積分
userDao.transfer("zhangsan","lisi", 100);
System.out.println("贈(zèng)送積分成功!");
}
}
獲取UserDao實(shí)例后,調(diào)用了實(shí)例中的贈(zèng)送積分方法,由zhangsan向lisi轉(zhuǎn)入100積分。如果事務(wù)代碼起作用,那么在整個(gè)贈(zèng)送積分方法執(zhí)行完畢后,zhangsan和lisi的積分應(yīng)該都是原來(lái)的值。
2.2 基于Annotation方式的聲明式事務(wù)
Spring的聲明式事務(wù)管理還可以通過(guò)Annotation(注解)的方式來(lái)實(shí)現(xiàn)。這種方式的使用非常簡(jiǎn)單,開(kāi)發(fā)者只需做兩件事情:
(1)在Spring容器中注冊(cè)事務(wù)注解驅(qū)動(dòng),其代碼如下:
<tx:annotation-driven transaction-manager ntransactionManager" />
(2)在需要使用事務(wù)的Spring Bean類(lèi)或者Bean類(lèi)的方法上添加注解@Transactional。如果將注解添加在Bean類(lèi)上,就表示事務(wù)的設(shè)置對(duì)整個(gè)Bean類(lèi)的所有方法都起作用;如果將注解添加在Bean類(lèi)中的某個(gè)方法上,就表示事務(wù)的設(shè)置只對(duì)該方法有效。
使用@Transactional注解時(shí),可以通過(guò)其參數(shù)配置事務(wù)詳情。@Transactional注解可配置的參數(shù)信息。
@Transactional注解與tx:method元素中的事務(wù)屬性基本是對(duì)應(yīng)的,并且其含義也基本相似。
【示例5-2】為了讓讀者更加清楚地掌握@Transactiona注解的使用,以Annotation方式實(shí)現(xiàn)項(xiàng)目中的事務(wù)管理,具體實(shí)現(xiàn)步驟如下。
步驟01 在src目錄下創(chuàng)建一個(gè)Spring配置文件applicationContext-annotation.xml
,在該文件中聲明事務(wù)管理器等配置信息。
<?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:tx="http://www.springframework.org/schema/cache"
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/cache
http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.配置數(shù)據(jù)源 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<!-- 數(shù)據(jù)庫(kù)驅(qū)動(dòng) -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<!-- 連接數(shù)據(jù)庫(kù)url -->
<property name="url" value="jdbc:mysql://localhost:3306/db_spring"/>
<!-- 連接數(shù)據(jù)庫(kù)的用戶名 -->
<property name="username" value="root"/>
<!-- 連接數(shù)據(jù)庫(kù)的密碼 -->
<property name="password" value="root"/>
</bean>
<!-- 2.配置JDBC模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 默認(rèn)必須使用數(shù)據(jù)源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 定義id為 UserDao 的 Bean -->
<bean id="UserDao" class="com.ssm.jdbc.UserDaoImpl">
<!-- 將jdbcTemplate 注入userDao實(shí)例中 -->
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<!-- 4.事務(wù)管理器,依賴(lài)于數(shù)據(jù)源-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 5.編寫(xiě)通知:對(duì)事務(wù)進(jìn)行增強(qiáng)(通知),需要編寫(xiě)對(duì)切入點(diǎn)和具體執(zhí)行事務(wù)細(xì)節(jié) -->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
注意:如果案例中使用了注解式開(kāi)發(fā),就需要在配置文件中開(kāi)啟注解處理器,指定掃描哪些包下的注解。這里沒(méi)有開(kāi)啟注解處理器是因?yàn)樵谂渲梦募幸呀?jīng)配置了UserDaoImpl類(lèi)的Bean,而@Transactional注解就配置在該Bean類(lèi)中,所以可以直接生效。
步驟02 在UserDaoImpl類(lèi)的transfer()方法上添加事務(wù)注解,添加后的代碼如下所示。
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,readOnly = false)
//贈(zèng)送積分
public void transfer(String outUser,String inUser, Integer jf) {
//接收積分
this.jdbcTemplate.update("update user set jf = jf + ? where username = ?", jf, inUser);
//模擬系統(tǒng)運(yùn)行時(shí)的突發(fā)性問(wèn)題
int i=1/0;
//贈(zèng)送積分
this.jdbcTemplate.update("update user set jf = jf - ? where username = ?", jf, outUser);
}
注意在實(shí)際開(kāi)發(fā)中,事務(wù)的配置信息通常是在Spring的配置文件中完成的,而在業(yè)務(wù)層類(lèi)上只需使用@Transactional注解即可,不需要配置@Transactional注解的屬性。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-437997.html
步驟03 在TransactionTest類(lèi)中創(chuàng)建測(cè)試方法annotationTest(),編輯后的代碼如下所示。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-437997.html
@Test
public void annotationTest() {
//加載配置文件
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-annotation.xml");
//獲取userDao實(shí)例
UserDao userDao = (UserDao)applicationContext.getBean("userDao");
//贈(zèng)送積分
userDao.transfer("zhangsan","lisi", 200);
System.out.println("贈(zèng)送積分成功!");
}
到了這里,關(guān)于【Spring】Spring的事務(wù)管理的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!