javascript
Spring事务详解与使用
Spring事務(wù)核心對象
J2EE開發(fā)使用分層設(shè)計(jì)的思想進(jìn)行,對于簡單的業(yè)務(wù)層轉(zhuǎn)調(diào)數(shù)據(jù)層的單一操作,事務(wù)開啟在業(yè)務(wù)層或者數(shù)據(jù)層并無太大差別,當(dāng)業(yè)務(wù)中包含多個(gè)數(shù)據(jù)層的調(diào)用時(shí),需要在業(yè)務(wù)層開啟事務(wù),對數(shù)據(jù)層中多個(gè)操作進(jìn)行組合并歸屬于同一個(gè)事務(wù)進(jìn)行處理
Spring為業(yè)務(wù)層提供了整套的事務(wù)解決方案:
- PlatformTransactionManager
- TransactionDefinition
- TransactionStatus
PlatformTransactionManager
PlatformTransactionManager:平臺事務(wù)管理器實(shí)現(xiàn)類,是一個(gè)接口,需要使用它的實(shí)現(xiàn)類
-
DataSourceTransactionManager 適用于Spring JDBC或MyBatis
-
HibernateTransactionManager 適用于Hibernate3.0及以上版本
-
JpaTransactionManager 適用于JPA
-
JdoTransactionManager 適用于JDO
-
JtaTransactionManager 適用于JTA
-
JPA(Java Persistence API)Java EE 標(biāo)準(zhǔn)之一,為POJO提供持久化標(biāo)準(zhǔn)規(guī)范,并規(guī)范了持久化開發(fā)的統(tǒng)一API,符合JPA規(guī)范的開發(fā)可以在不同的JPA框架下運(yùn)行
-
JDO(Java Data Object )是Java對象持久化規(guī)范,用于存取某種數(shù)據(jù)庫中的對象,并提供標(biāo)準(zhǔn)化API。與JDBC相比,JDBC僅針對關(guān)系數(shù)據(jù)庫進(jìn)行操作,JDO可以擴(kuò)展到關(guān)系數(shù)據(jù)庫、文件、XML、對象數(shù)據(jù)庫(ODBMS)等,可移植性更強(qiáng)
-
JTA(Java Transaction API)Java EE 標(biāo)準(zhǔn)之一,允許應(yīng)用程序執(zhí)行分布式事務(wù)處理。與JDBC相比,JDBC事務(wù)則被限定在一個(gè)單一的數(shù)據(jù)庫連接,而一個(gè)JTA事務(wù)可以有多個(gè)參與者,比如JDBC連接、JDO 都可以參與到一個(gè)JTA事務(wù)中
此接口定義了事務(wù)的基本操作
- 獲取事務(wù) :
- 提交事務(wù) :
- 回滾事務(wù) :
TransactionDefinition
此接口定義了事務(wù)的基本信息
- 獲取事務(wù)定義名稱
- 獲取事務(wù)的讀寫屬性
- 獲取事務(wù)隔離級別
- 獲事務(wù)超時(shí)時(shí)間
- 獲取事務(wù)傳播行為特征
TransactionStatus
此接口定義了事務(wù)在執(zhí)行過程中某個(gè)時(shí)間點(diǎn)上的狀態(tài)信息及對應(yīng)的狀態(tài)操作
獲取事務(wù)是否處于新開啟事務(wù)狀態(tài)
boolean isNewTransaction()獲取事務(wù)是否處于已完成狀態(tài)
boolean isCompleted()獲取事務(wù)是否處于回滾狀態(tài)
boolean isRollbackOnly()刷新事務(wù)狀態(tài)
void flush()獲取事務(wù)是否具有回滾存儲點(diǎn)
boolean hasSavepoint()設(shè)置事務(wù)處于回滾狀態(tài)
void setRollbackOnly()事務(wù)控制方式
-
編程式
-
聲明式(XML)
-
聲明式(注解)
案例:
模擬銀行轉(zhuǎn)賬業(yè)務(wù)說明,銀行轉(zhuǎn)賬操作中,涉及從A賬戶到B賬戶的資金轉(zhuǎn)移操作。數(shù)據(jù)層僅提供單條數(shù)據(jù)的基礎(chǔ)操作,未設(shè)計(jì)多賬戶間的業(yè)務(wù)操作。
案例環(huán)境(基于Spring、Mybatis整合):
pom.xml
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.9.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.1.9.RELEASE</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.3</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.0</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>RELEASE</version><scope>test</scope></dependency></dependencies>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:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><context:property-placeholder location="classpath:*.properties"/><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean><bean id="accountService" class="com.itzhuzhu.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"/><property name="dataSource" ref="dataSource"/></bean><bean class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="typeAliasesPackage" value="com.itzhuzhu.domain"/></bean><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.itzhuzhu.dao"/></bean></beans>jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db1 jdbc.username=root jdbc.password=不告訴你AccountDao.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.itzhuzhu.dao.AccountDao"><update id="inMoney">update account set money = money + #{money} where name = #{name}</update><update id="outMoney">update account set money = money - #{money} where name = #{name}</update></mapper>dao包下AccountDao
public interface AccountDao {void inMoney(@Param("name") String name, @Param("money") Double money);void outMoney(@Param("name") String name, @Param("money") Double money); }domain包下Account
public class Account implements Serializable {private Integer id;private String name;private Double money; }service包下AccountService
public interface AccountService {/*** 轉(zhuǎn)賬操作** @param outName 出賬用戶名* @param inName 入賬用戶名* @param money 轉(zhuǎn)賬金額*/public void transfer(String outName, String inName, Double money); }service.impl包下AccountServiceImpl
public class AccountServiceImpl implements AccountService {private AccountDao accountDao;private DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}public void transfer(String outName, String inName, Double money) {// 開啟事務(wù)PlatformTransactionManager pt = new DataSourceTransactionManager(dataSource);// 事務(wù)定義TransactionDefinition td = new DefaultTransactionDefinition();// 事務(wù)狀態(tài)TransactionStatus ts = pt.getTransaction(td);accountDao.inMoney(outName, money);int i = 1 / 0;accountDao.outMoney(inName, money);// 提交事務(wù)pt.commit(ts);} }測試類:
public class Test {public static void main(String[] args) {ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");AccountService accountService = (AccountService) ctx.getBean("accountService");accountService.transfer("張三","李四",100D);} }編程式事務(wù):
public void transfer(String outName,String inName,Double money){//創(chuàng)建事務(wù)管理器DataSourceTransactionManager dstm = new DataSourceTransactionManager();//為事務(wù)管理器設(shè)置與數(shù)據(jù)層相同的數(shù)據(jù)源dstm.setDataSource(dataSource);//創(chuàng)建事務(wù)定義對象TransactionDefinition td = new DefaultTransactionDefinition();//創(chuàng)建事務(wù)狀態(tài)對象,用于控制事務(wù)執(zhí)行TransactionStatus ts = dstm.getTransaction(td);accountDao.inMoney(outName,money);int i = 1/0; //模擬業(yè)務(wù)層事務(wù)過程中出現(xiàn)錯(cuò)誤accountDao.outMoney(inName,money);//提交事務(wù)dstm.commit(ts); }使用AOP控制事務(wù)
將業(yè)務(wù)層的事務(wù)處理功能抽取出來制作成AOP通知,利用環(huán)繞通知運(yùn)行期動(dòng)態(tài)織入。
aop包下TxAdvice
AccountServiceImpl
public class AccountServiceImpl implements AccountService {private AccountDao accountDao;private DataSource dataSource;public void setDataSource(DataSource dataSource) {this.dataSource = dataSource;}public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}public void transfer(String outName, String inName, Double money) {accountDao.inMoney(outName, money);int i = 1 / 0;accountDao.outMoney(inName, money);} }applicationContext.xml
<!--AOP配置--><bean id="txAdvice" class="com.itzhuzhu.aop.TxAdvice"><property name="dataSource" ref="dataSource"/></bean><aop:config><aop:pointcut id="pt" expression="execution(* *..transfer(..))"/><aop:aspect ref="txAdvice"><aop:around method="transactionManager" pointcut-ref="pt"/></aop:aspect></aop:config>聲明式事務(wù)(XML)
聲明式事務(wù)是由Spring操控事務(wù),基于上面的案例改造
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:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttps://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><context:property-placeholder location="classpath:*.properties"/><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean><bean id="accountService" class="com.itzhuzhu.service.impl.AccountServiceImpl"><property name="accountDao" ref="accountDao"/><!-- <property name="dataSource" ref="dataSource"/>--></bean><bean class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="typeAliasesPackage" value="com.itzhuzhu.domain"/></bean><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="basePackage" value="com.itzhuzhu.dao"/></bean><!-- <!–AOP配置–><bean id="txAdvice" class="com.itzhuzhu.aop.TxAdvice"><property name="dataSource" ref="dataSource"/></bean><aop:config><aop:pointcut id="pt" expression="execution(* *..transfer(..))"/><aop:aspect ref="txAdvice"><aop:around method="transactionManager" pointcut-ref="pt"/></aop:aspect></aop:config>--><!--TX方式--><bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!--定義事務(wù)管理的通知類--><tx:advice id="txAdvice" transaction-manager="txManager"><!--定義控制的事務(wù)--><tx:attributes><tx:method name="*" read-only="false"/><tx:method name="get*" read-only="true"/><tx:method name="find*" read-only="true"/><tx:methodname="transfer"read-only="false"timeout="-1"isolation="DEFAULT"no-rollback-for=""rollback-for=""propagation="REQUIRED"/><!--<tx:method name="transfer" read-only="false"/>--></tx:attributes></tx:advice><aop:config><aop:pointcut id="pt" expression="execution(* com.itzhuzhu.service.*Service.*(..))"/><aop:pointcut id="pt2" expression="execution(* com.itzhuzhu.dao.*.b(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/><aop:advisor advice-ref="txAdvice" pointcut-ref="pt2"/></aop:config> </beans>使用tx命名空間配置事務(wù)專屬通知類
<tx:advice id="txAdvice" transaction-manager="txManager"><tx:attributes><tx:method name="*" read-only="false" /><tx:method name="get*" read-only="true" /><tx:method name="find*" read-only="true" /></tx:attributes> </tx:advice>使用aop:advisor在AOP配置中引用事務(wù)專屬通知類
<aop:config><aop:pointcut id="pt" expression="execution(* *..*(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/> </aop:config>aop:advice與aop:advisor區(qū)別:
-
aop:advice配置的通知類可以是普通java對象,不實(shí)現(xiàn)接口,也不使用繼承關(guān)系
-
aop:advisor配置的通知類必須實(shí)現(xiàn)通知接口
-
MethodBeforeAdvice
-
AfterReturningAdvice
-
ThrowsAdvice
-
tx配置——tx:advice
-
名稱:tx:advice
-
類型:標(biāo)簽
-
歸屬:beans標(biāo)簽
-
作用:專用于聲明事務(wù)通知
-
格式:
-
基本屬性:
-
id :用于配置aop時(shí)指定通知器的id
-
transaction-manager :指定事務(wù)管理器bean
-
tx配置——tx:attributes
-
名稱:tx:attributes
-
類型:標(biāo)簽
-
歸屬:tx:advice標(biāo)簽
-
作用:定義通知屬性
-
格式:
-
基本屬性:
- 無
tx配置——tx:method
-
名稱:tx:method
-
類型:標(biāo)簽
-
歸屬:tx:attribute標(biāo)簽
-
作用:設(shè)置具體的事務(wù)屬性
-
格式:
-
說明:
通常事務(wù)屬性會配置多個(gè),包含1個(gè)讀寫的全事務(wù)屬性,1個(gè)只讀的查詢類事務(wù)屬性
tx:method屬性:
<tx:methodname="transfer" 待添加事務(wù)的方法名表達(dá)式(支持*號通配符),例如get*、*、......read-only="false" 待設(shè)置事務(wù)的讀寫屬性,true為只讀,false為讀寫timeout="-1" 設(shè)置事務(wù)超時(shí)時(shí)長,單位秒isolation="DEFAULT" 設(shè)置事務(wù)隔離級別,該隔離級設(shè)定是基于spring的設(shè)定,非數(shù)據(jù)庫端no-rollback-for="" 設(shè)置事務(wù)中不回滾的異常,多個(gè)異常間使用逗號分割rollback-for="" 設(shè)置事務(wù)中必回滾的異常,多個(gè)異常間使用逗號分割propagation="REQUIRED" 設(shè)置事務(wù)的傳播行為事務(wù)傳播行為
-
事務(wù)管理員
-
事務(wù)協(xié)調(diào)員
-
事務(wù)傳播行為描述的是事務(wù)協(xié)調(diào)員對事務(wù)管理員所攜帶事務(wù)的處理態(tài)度
事務(wù)傳播應(yīng)用:
-
場景A:生成訂單業(yè)務(wù)
-
子業(yè)務(wù)S1:記錄日志到數(shù)據(jù)庫表X
-
子業(yè)務(wù)S2:保存訂單數(shù)據(jù)到數(shù)據(jù)庫表Y
-
子業(yè)務(wù)S3:……
-
如果S2或S3或……事務(wù)提交失敗,此時(shí)S1是否回滾?如何控制?
-
(S1需要新事務(wù))
-
-
場景B:生成訂單業(yè)務(wù)
-
背景1:訂單號生成依賴數(shù)據(jù)庫中一個(gè)專門用于控制訂單號編號生成的表M獲取
-
背景2:每次獲取完訂單號,表M中記錄的編號自增1
-
子業(yè)務(wù)S1:從表M中獲取訂單編號
-
子業(yè)務(wù)S2:保存訂單數(shù)據(jù),訂單編號來自于表M
-
子業(yè)務(wù)S3:……
-
如果S2或S3或……事務(wù)提交失敗,此時(shí)S1是否回滾?如何控制?
-
(S1需要新事務(wù))
-
聲明式事務(wù)(注解)
@Transactional
-
名稱:@Transactional
-
類型:方法注解,類注解,接口注解
-
位置:方法定義上方,類定義上方,接口定義上方(不要寫在實(shí)現(xiàn)類,實(shí)現(xiàn)類會換,一般寫接口上,接口方法寫詳細(xì)的,接口類可以做一個(gè)大的控制)
-
作用:設(shè)置當(dāng)前類/接口中所有方法或具體方法開啟事務(wù),并指定相關(guān)事務(wù)屬性
-
范例:
tx:annotation-driven
-
名稱:tx:annotation-driven
-
類型:標(biāo)簽
-
歸屬:beans標(biāo)簽
-
作用:開啟事務(wù)注解驅(qū)動(dòng),并指定對應(yīng)的事務(wù)管理器
-
范例:
<tx:annotation-driven transaction-manager="txManager"/>
聲明式事務(wù)(純注解驅(qū)動(dòng))
-
名稱:@EnableTransactionManagement
-
類型:類注解
-
位置:Spring注解配置類上方
-
作用:開啟注解驅(qū)動(dòng),等同XML格式中的注解驅(qū)動(dòng)
-
范例:
AccountDao
public interface AccountDao {@Update("update account set money = money + #{money} where name = #{name}")void inMoney(@Param("name") String name, @Param("money") Double money);@Update("update account set money = money - #{money} where name = #{name}")void outMoney(@Param("name") String name, @Param("money") Double money);}AccountServiceImpl
@Service("accountService") public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountDao accountDao;public void transfer(String outName, String inName, Double money) {accountDao.inMoney(outName,money);int i = 1/0;accountDao.outMoney(inName,money);} }JDBCConfig
public class JDBCConfig {@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String userName;@Value("${jdbc.password}")private String password;@Bean("dataSource")public DataSource getDataSource(){DruidDataSource ds = new DruidDataSource();ds.setDriverClassName(driver);ds.setUrl(url);ds.setUsername(userName);ds.setPassword(password);return ds;}@Beanpublic PlatformTransactionManager getTransactionManager(DataSource dataSource){return new DataSourceTransactionManager(dataSource);} }MyBatisConfig
public class MyBatisConfig {@Beanpublic SqlSessionFactoryBean getSqlSessionFactoryBean(@Autowired DataSource dataSource){SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setTypeAliasesPackage("com.itzhuzhu.domain");ssfb.setDataSource(dataSource);return ssfb;}@Beanpublic MapperScannerConfigurer getMapperScannerConfigurer(){MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.itzhuzhu.dao");return msc;} }SpringConfig
@Configuration @ComponentScan("com.itzhuzhu") @PropertySource("classpath:jdbc.properties") @Import({JDBCConfig.class,MyBatisConfig.class,TransactionManagerConfig.class}) @EnableTransactionManagement public class SpringConfig { } public class TransactionManagerConfig {@Beanpublic PlatformTransactionManager getTransactionManager(@Autowired DataSource dataSource){return new DataSourceTransactionManager(dataSource);} }總結(jié)
以上是生活随笔為你收集整理的Spring事务详解与使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电子科大计算机调试,电子科大计算机学院
- 下一篇: JSR表单校验框架