日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring JDBC-事务方法嵌套调用解读

發布時間:2025/3/21 javascript 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring JDBC-事务方法嵌套调用解读 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

  • Spring事務傳播機制回顧
  • 相互嵌套的服務方法
  • 源碼

Spring事務傳播機制回顧

關于Spring事務的一個錯誤的說法:一個事務方法中不應該調用另外一個事務方法,否則將產生兩個事務,其實這是不正確的。

這是因為未正確認識Spring事務傳播機制而造成的誤解。 Spring對事務控制的支持統一在TransactionDefinition類中描述

我們來看下該類中的接口方法

  • int getPropagationBehavior() 事務的傳播行為
  • int getIsolationLevel(); 事務的隔離級別
  • int getTimeout();事務的過期時間
  • boolean isReadOnly();事務的讀、寫特性
  • String getName();事務的名稱

除了事務的傳播行為外,事務的其他特性Spring是借助底層資源的功能來完成的,Spring無非只充當個代理的角色。但是事務的傳播行為卻是Spring憑借自身的框架提供的功能

所謂事務傳播的行為,就是多個事務方法相互調用時,事務如何在這些方法間傳播。 Spring在TransactionDefinition接口中規定了7種類型的事務傳播行為,它們規定了事務方法和事務方法發生嵌套調用時事務如何進行傳播:

事務傳播行為類型說明
PROPAGATION_REQUIRED如果當前沒有事務,就新建一個事務,如果已經存在一個事務中,加入到這個事務中。這是最常見的選擇
PROPAGATION_SUPPORTS支持當前事務,如果當前沒有事務,就以非事務方式執行。
PROPAGATION_MANDATORY使用當前的事務,如果當前沒有事務,就拋出異常。
PROPAGATION_REQUIRES_NEW新建事務,如果當前存在事務,把當前事務掛起
PROPAGATION_NOT_SUPPORTED以非事務方式執行操作,如果當前存在事務,就把當前事務掛起
PROPAGATION_NEVER以非事務方式執行,如果當前存在事務,則拋出異常。
PROPAGATION_NESTED如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行與PROPAGATION_REQUIRED類似的操作。

當使用PROPAGATION_NESTED時,底層的數據源必須基于JDBC 3.0,并且實現者需要支持保存點事務機制。

Spring默認的事務傳播行為是PROPAGATION_REQUESTED, 它適合絕大多數情況。

如果多個ServiceX#methodX() 均工作下在事務環境下(均被Spring事務增強),且程序中存在調用鏈Service1#method1()—->Service2#method2()——>Service#method3(),那么這3個服務類的3個方法通過Spring的事務傳播機制都工作在同一個事務中。


相互嵌套的服務方法

我們來舉個例子,TeacherService#doSomething()方法內部調用了 調用本類的udpateTeacherInfo還有StudentService#updateSutdent()方法,這兩個類都繼承于BaseService,類結構如下:

package com.xgj.dao.transaction.nestedCall.service;import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;import com.xgj.dao.transaction.nestedCall.dao.TeacherDao; import com.xgj.dao.transaction.nestedCall.domain.Student; import com.xgj.dao.transaction.nestedCall.domain.Teacher;/*** * * @ClassName: TeacherService* * @Description: @Service標注的service層 繼承BaseService* * @author: Mr.Yang* * @date: 2017年9月24日 下午4:56:35*/@Service public class TeacherService extends BaseService {Logger logger = Logger.getLogger(TeacherService.class);private TeacherDao teacherDao;private StudentService studentService;@Autowiredpublic void setTeacherDao(TeacherDao teacherDao) {this.teacherDao = teacherDao;}@Autowiredpublic void setStudentService(StudentService studentService) {this.studentService = studentService;}/*** * * @Title: init* * @Description: 改方法嵌套調用了本類的其他方法以及其他類的方法* * * @return: void*/public void doSomething() {logger.info("before TeacherService#udpateTeacherInfo");// 調用本類的其他方法udpateTeacherInfo(simulateTeacher());logger.info("after TeacherService#udpateTeacherInfo");// 調用其他類的方法logger.info("before StudentService#updateSutdent");studentService.updateSutdent(simulateStudent());logger.info("after StudentService#updateSutdent");}public void udpateTeacherInfo(Teacher teacher) {teacherDao.updateTeacher(teacher);}/*** * * @Title: simulateTeacher* * @Description: 模擬獲取一個teacher實例* * @return* * @return: Teacher*/private Teacher simulateTeacher() {Teacher teacher = new Teacher();teacher.setName("FTT");teacher.setAge(88);teacher.setSex("FF");teacher.setTeacherId(2);return teacher;}private Student simulateStudent() {Student student = new Student();student.setName("FSS");student.setAge(22);student.setSex("MM");student.setStudentId(2);return student;}}

配置文件:

<?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:p="http://www.springframework.org/schema/p"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/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!-- 掃描類包,將標注Spring注解的類自動轉化Bean,同時完成Bean的注入 --><context:component-scan base-package="com.xgj.dao.transaction.nestedCall" /><!-- 使用context命名空間,配置數據庫的properties文件 --><context:property-placeholder location="classpath:spring/jdbc.properties" /><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close" p:driverClassName="${jdbc.driverClassName}"p:url="${jdbc.url}" p:username="${jdbc.username}" p:password="${jdbc.password}" /><!-- 配置Jdbc模板 --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"p:dataSource-ref="dataSource" /><!--事務管理器,通過屬性引用數據源 --><bean id="jdbcManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"p:dataSource-ref="dataSource"/><!-- 通過以下配置為繼承BaseService的所有子類的所有public方法添加事務增強 --><aop:config proxy-target-class="true"><!-- 切點 --><aop:pointcut id="serviceJdbcMethod" expression="within(com.xgj.dao.transaction.nestedCall.service.BaseService+)"/><!-- 切面 --><aop:advisor pointcut-ref="serviceJdbcMethod" advice-ref="txAdvice"/></aop:config><!-- 增強,供aop:advisor引用 --><tx:advice id="txAdvice" transaction-manager="jdbcManager"><tx:attributes><tx:method name="*"/></tx:attributes></tx:advice></beans>

通過Spring配置為TeacherService以及StudentService中的所有公共方法都添加Spring AOP的事務增強,讓TeacherService的doSomething()和udpateTeacherInfo()以及StudentService的updateSutdent方法都工作在事務環境下。

關鍵代碼:

<aop:pointcut id="serviceJdbcMethod" expression="within(com.xgj.dao.transaction.nestedCall.service.BaseService+)"/>

我們將日志級別調整為DEBUG,運行測試類

package com.xgj.dao.transaction.nestedCall.service;import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.context.support.ClassPathXmlApplicationContext;public class TeacherServiceTest {ClassPathXmlApplicationContext ctx = null;TeacherService teacherService = null;@Beforepublic void initContext() {// 啟動Spring 容器ctx = new ClassPathXmlApplicationContext("classpath:com/xgj/dao/transaction/nestedCall/conf_tx_nestedCall.xml");teacherService = ctx.getBean("teacherService", TeacherService.class);System.out.println("initContext successfully");}@Testpublic void testNestedCallInOneTransaction() {teacherService.doSomething();}@Afterpublic void closeContext() {if (ctx != null) {ctx.close();}System.out.println("close context successfully");} }

觀察日志:

2017-09-24 18:36:23,428 DEBUG [main] (AbstractPlatformTransactionManager.java:367) - Creating new transaction with name [com.xgj.dao.transaction.nestedCall.service.TeacherService.doSomething]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT 2017-09-24 18:36:23,777 DEBUG [main] (DataSourceTransactionManager.java:248) - Acquired Connection [jdbc:oracle:thin:@172.25.246.11:1521:testbed, UserName=CC, Oracle JDBC driver] for JDBC transaction 2017-09-24 18:36:23,781 DEBUG [main] (DataSourceTransactionManager.java:265) - Switching JDBC Connection [jdbc:oracle:thin:@172.25.246.11:1521:testbed, UserName=CC, Oracle JDBC driver] to manual commit 2017-09-24 18:36:23,820 INFO [main] (TeacherService.java:52) - before TeacherService#udpateTeacherInfo 2017-09-24 18:36:23,824 DEBUG [main] (JdbcTemplate.java:869) - Executing prepared SQL update 2017-09-24 18:36:23,825 DEBUG [main] (JdbcTemplate.java:616) - Executing prepared SQL statement [update teacher set name = ? ,age = ? ,sex = ? where id = ?] 2017-09-24 18:36:23,974 DEBUG [main] (JdbcTemplate.java:879) - SQL update affected 1 rows 2017-09-24 18:36:23,978 INFO [main] (TeacherDaoImpl.java:64) - updateTeacher successfully 2017-09-24 18:36:23,978 INFO [main] (TeacherService.java:55) - after TeacherService#udpateTeacherInfo 2017-09-24 18:36:23,978 INFO [main] (TeacherService.java:58) - before StudentService#updateSutdent 2017-09-24 18:36:23,978 DEBUG [main] (AbstractPlatformTransactionManager.java:476) - Participating in existing transaction 2017-09-24 18:36:24,004 DEBUG [main] (JdbcTemplate.java:869) - Executing prepared SQL update 2017-09-24 18:36:24,005 DEBUG [main] (JdbcTemplate.java:616) - Executing prepared SQL statement [update student set name = ? ,age = ? ,sex = ? where id = ?] 2017-09-24 18:36:24,007 DEBUG [main] (JdbcTemplate.java:879) - SQL update affected 1 rows 2017-09-24 18:36:24,007 INFO [main] (StudentDaoImpl.java:82) - updateStudent successfully 2017-09-24 18:36:24,008 INFO [main] (TeacherService.java:60) - after StudentService#updateSutdent 2017-09-24 18:36:24,008 DEBUG [main] (AbstractPlatformTransactionManager.java:759) - Initiating transaction commit 2017-09-24 18:36:24,008 DEBUG [main] (DataSourceTransactionManager.java:310) - Committing JDBC transaction on Connection [jdbc:oracle:thin:@172.25.246.11:1521:testbed, UserName=CC, Oracle JDBC driver]

Creating new transaction with name [com.xgj.dao.transaction.nestedCall.service.TeacherService.doSomething]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT

為TeacherService#doSomething開啟一個事務, 然后直接執行udpateTeacherInfo方法,由于doSomething和udpateTeacherInfo在一個類中,沒有觀察到有事務傳播行為的發生,

然而當執行到updateSutdent方法時,我們觀察到一個事務傳播行為: Participating in existing transaction ,這說明 StudentService#updateSutdent方法添加到了TeacherService#doSomething()方法的事務上下文中,二者共享同一個事務。 所以最終的結果是TeacherService的doSomething 和 udpateTeacherInfo 以及 StudentService#updateSutdent()方法工作在同一個事務中。

最后 Initiating transaction commit—-提交事務


源碼

代碼已托管到Github—> https://github.com/yangshangwei/SpringMaster

總結

以上是生活随笔為你收集整理的Spring JDBC-事务方法嵌套调用解读的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。