Spring分布式事务实现
分布式事务的实现方法

分布式事务的实现方法
分布式事务的实现方法有多种,以下是其中几种常见的方案:
1. 两阶段提交(XA 方案):事务管理器先询问各个数据库是否准备好了,每个要操作的数据库都回复事务管理器ok,那么就正式提交事务。
2. TCC方案:TCC的全称是:Try、Confirm、Cancel。
Try阶段是对各个服务的资源做检测以及对资源进行锁定或者预留;Confirm阶段是在各个服务中执行实际的操作;Cancel阶段是如果任何一个服务的业务方法执行出错,那么就需要进行补偿,即执行已经执行成功的业务逻辑的回滚操作。
一般来说,支付、交易等需要严格保证资金正确性的场景会使用TCC方案。
3. 可靠消息最终一致性方案:通过消息队列将分布式事务中的操作进行异步解耦,从而实现最终的一致性。
这种方式能够处理大量并发操作,但是可能会存在数据不一致的风险。
4. 最大努力通知方案:通过最大努力的方式将通知发送给其他服务,以实现分布式事务的一致性。
这种方式简单易行,但是可能会因为网络问题或者服务不可用等原因导致通知失败。
5. SAGA方案:SAGA是一种基于事务的分布式事务处理模型,它将一个分布式事务视为一系列本地事务的组合。
每个本地事务在一个单独的节点上执行,并且通过消息传递进行通信。
SAGA能够保证全局事务的最终一致性,同时具有较好的容错性和可恢复性。
以上是分布式事务的几种常见实现方案,每种方案都有其优缺点,需要根据具体的业务场景和需求来选择适合的方案。
SpringCloud分布式事务的解决方案

SpringCloud分布式事务的解决⽅案常见的分布式解决⽅案1、两阶段提交协议(2PC) 解决分布式系统的数据⼀致性问题出现了两阶段提交协议(2 Phase Commitment Protocol),两阶段提交由协调者和参与者组成,共经过两个阶段和三个操作,部分关系数据库如Oracle、MySQL⽀持两阶段提交协议。
说到2pc就不得不聊聊数据库分布式事务中的XA transactions在XA协议中分为两阶段:第⼀阶段:事务管理器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交.第⼆阶段:事务协调器要求每个数据库提交数据,或者回滚数据。
举⼀个例⼦:1、应⽤程序通过事务协调器向两个库发起prepare,两个数据库收到消息分别执⾏本地事务(记录⽇志), 但不提交,如果执⾏成功则回复yes,否则回复no。
2、事务协调器收到回复,只要有⼀⽅回复no则分别向参与者发起回滚事务,参与者开始回滚事务。
3、事务协调器收到回复,全部回复yes,此时向参与者发起提交事务。
如果参与者有⼀⽅提交事务失败则由事务协调器发起回滚事务。
优点: 尽量保证了数据的强⼀致,实现成本较低,在各⼤主流数据库都有⾃⼰实现,对于MySQL是从5.5开始⽀持。
缺点:单点问题:事务管理器在整个流程中扮演的⾓⾊很关键,如果其宕机,⽐如在第⼀阶段已经完成, 在第⼆阶段正准备提交的时候事务管理器宕机,资源管理器就会⼀直阻塞,导致数据库⽆法使⽤。
同步阻塞:在准备就绪之后,资源管理器中的资源⼀直处于阻塞,直到提交完成,释放资源。
数据不⼀致:两阶段提交协议虽然为分布式数据强⼀致性所设计,但仍然存在数据不⼀致性的可能,⽐如在第⼆阶段中,假设协调者发出了事务commit的通知,但是因为⽹络问题该通知仅被⼀部分参与者所收到并执⾏了commit操作,其余的参与者则因为没有收到通知⼀直处于阻塞状态,这时候就产⽣了数据的不⼀致性。
Java中的分布式事务和分布式锁应用

Java中的分布式事务和分布式锁应用分布式系统是指在多台计算机中协同工作的系统,它们可以通过网络进行通信和协作,以实现特定的功能。
由于分布式系统的复杂性和需要处理的数据量庞大,分布式事务和分布式锁成为了开发者们关注的热点问题。
在本文中,我们将深入探讨Java中的分布式事务和分布式锁的应用,包括其基本概念、原理、使用场景以及常见的实现方式。
一、分布式事务的概念与实现1.分布式事务的概念在分布式系统中,由于数据存储在不同的节点上,不同的操作可能需要跨多个节点进行协同操作。
分布式事务就是对这些分布在不同节点上的操作进行统一的管理,保证所有操作要么全部成功,要么全部失败,从而确保系统的一致性和可靠性。
2. ACID原则在传统的单机事务中,事务必须满足ACID原则,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。
而在分布式系统中,要求事务的ACID原则不仅适用于单个节点,还需要保证在多个节点上的事务也满足ACID原则。
3.分布式事务的实现方式Java中常见的分布式事务解决方案有两种:XA事务和柔性事务。
(1) XA事务是分布式事务的一种标准化解决方案,它允许多个资源(如数据库)参与到一个事务中,通过两阶段提交协议来保证事务的一致性。
(2)柔性事务是一种更加灵活和轻量级的分布式事务解决方案,它通常采用了类似“最终一致性”、“补偿事务”等策略来保证分布式系统中的数据一致性。
二、分布式事务的应用场景由于分布式系统中的数据分片和分布在不同的节点上,导致了分布式事务在应用场景中的困难与复杂度。
以下是几个常见的分布式事务应用场景:1.跨服务的事务处理:当一个业务操作需要跨多个服务进行协同操作时,需要使用分布式事务来协调各个服务的操作。
2.跨数据库的事务管理:当业务系统中使用多个数据库进行数据存储时,需要使用分布式事务来保证多个数据库操作的一致性。
功能测试中的分布式事务验证

功能测试中的分布式事务验证分布式系统是现代软件开发中常见的架构模式,它将系统的各个组件分散在不同的物理或逻辑节点上,通过网络进行通信和协调。
在分布式系统中,事务管理一直是一个挑战,特别是对于涉及多个节点的分布式事务。
为了保证分布式事务的一致性和完整性,功能测试中的分布式事务验证变得至关重要。
一、分布式事务概述分布式事务是指涉及多个节点的事务操作,这些节点可以是不同的进程、不同的服务或不同的数据库。
在分布式系统中,各个节点之间的通信存在网络延迟、中断等问题,因此需要一种机制来确保在事务执行期间的一致性和隔离性。
分布式事务的核心目标是保证所有节点的数据在执行事务期间保持一致性,并在事务提交或回滚后得到正确的结果。
二、功能测试中的分布式事务验证重要性在开发和测试分布式系统时,保证系统在各种异常情况下依然能够保持一致性和完整性是至关重要的。
功能测试中的分布式事务验证可以帮助开发人员和测试人员发现潜在的问题,并及时修复,从而提高系统的可靠性和稳定性。
通过对分布式事务进行验证,可以确保系统的各个组件在联合工作时能够正确处理事务边界、失败恢复、数据一致性等关键问题。
三、功能测试中的分布式事务验证方法1. 设计合理的测试用例:根据系统的具体场景和需求,设计一组全面、完整的测试用例来验证分布式事务的各个方面。
测试用例应该覆盖不同的分布式事务场景,包括正常情况下的事务执行、异常情况下的事务回滚、网络故障处理等。
2. 模拟网络延迟和中断:通过模拟网络延迟和中断等异常情况,验证系统在不稳定的网络环境下的分布式事务处理能力。
例如,可以使用工具模拟网络故障或者手动断开节点之间的通信,观察系统的行为和响应。
3. 监控和记录事务执行过程:使用监控工具,实时监测分布式事务的执行情况,包括各个节点的状态、事务日志、数据传输等。
通过记录事务执行过程中的关键信息,便于分析和排查问题。
4. 数据一致性验证:在分布式系统中,数据一致性是一个核心问题。
SpringCloudAlibaba使用Seata处理分布式事务的技巧

SpringCloudAlibaba使⽤Seata处理分布式事务的技巧Seata简介在传统的单体项⽬中,我们使⽤@Transactional注解就能实现基本的ACID事务了。
但是前提是:1)数据库⽀持事务(如:MySQL的innoDB引擎)2)所有业务都在同⼀个数据库中执⾏随着微服务架构的引⼊,需要对数据库进⾏分库分表,每个服务拥有⾃⼰的数据库,这样传统的事务就不起作⽤了,那么我们如何保证多个服务中数据的⼀致性呢?这样就出现了分布式事务,⽽Seata就是为微服务架构⽽⽣的⼀种⾼性能、易于使⽤的分布式事务解决⽅案。
Seata 中有三个基础组件:1. Transaction Coordinator(TC协调者):维护全局和分⽀事务的状态,驱动全局提交或回滚。
2. Transaction Manager(TM事务管理):定义全局事务的范围,开启、提交或回滚⼀个全局事务。
3. Resource Manager(RM资源管理):管理分⽀事务资源,与 TC 通讯并报告分⽀事务状态,管理本地事务的提交与回滚。
可以这么说⼀个分布式事务就是全局事务GlobalTransaction,⽽全局事务是由⼀个个的分⽀事务组成的,每个分⽀事务就是⼀个本地事务。
Seata的⽣命周期1. TM 要求 TC ⽣成⼀个全局事务,并由 TC ⽣成⼀个全局事务XID 返回。
2. XID 通过微服务调⽤链传播。
3. RM 向 TC 注册本地事务,将其注册到 ID 为 XID 的全局事务中。
4. TM 要求 TC 提交或回滚XID 对应的全局事务。
5. TC 驱动 XID 对应的全局事务对应的所有的分⽀事务提交或回滚。
Seata安装和配置startup -m standalone安装和配置Seatafile.conf主要是数据库的配置,配置如下registry.conf 是注册中⼼的配置另外conf⽬录中还需要⼀个脚本⽂件:nacos-config.sh ⽤于对nacos进⾏初始化配置在seata1.4.0中是没有的,需要⾃⾏创建,内容如下:#!/usr/bin/env bash# Copyright 1999-2019 Seata.io Group.## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at、## /licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.while getopts ":h:p:g:t:u:w:" optdocase $opt inh)host=$OPTARG;;p)port=$OPTARG;;g)group=$OPTARG;;t)tenant=$OPTARG;;u)username=$OPTARG;;w)password=$OPTARG;;)echo " USAGE OPTION: $0 [-h host] [-p port] [-g group] [-t tenant] [-u username] [-w password] " exit 1;;esacdoneurlencode() {for ((i=0; i < ${#1}; i++))dochar="${1:$i:1}"case $char in[a-zA-Z0-9.~_-]) printf $char ;;*) printf '%%%02X' "'$char" ;;esacdone}if [[ -z ${host} ]]; thenhost=localhostfiif [[ -z ${port} ]]; thenport=8848fiif [[ -z ${group} ]]; thengroup="SEATA_GROUP"fiif [[ -z ${tenant} ]]; thentenant=""fiif [[ -z ${username} ]]; thenusername=""fiif [[ -z ${password} ]]; thenpassword=""finacosAddr=$host:$portcontentType="content-type:application/json;charset=UTF-8"echo "set nacosAddr=$nacosAddr"echo "set group=$group"failCount=0tempLog=$(mktemp -u)function addConfig() {curl -X POST -H "${contentType}" "http://$nacosAddr/nacos/v1/cs/configs?dataId=$(urlencode $1)&group=$group&content=$(urlencode $2)&tenant=$tenant&username=$username&password=$password" >"${tempLog}" 2>/dev/null if [[ -z $(cat "${tempLog}") ]]; thenecho " Please check the cluster status. "exit 1fiif [[ $(cat "${tempLog}") =~ "true" ]]; thenecho "Set $1=$2 successfully "elseecho "Set $1=$2 failure "(( failCount++ ))fi}count=0for line in $(cat $(dirname "$PWD")/config.txt | sed s/[[:space:]]//g); do(( count++ ))key=${line%%=*}value=${line#*=}addConfig "${key}" "${value}"doneecho "========================================================================="echo " Complete initialization parameters, total-count:$count , failure-count:$failCount "echo "========================================================================="if [[ ${failCount} -eq 0 ]]; thenecho " Init nacos config finished, please start seata-server. "elseecho " init nacos config fail. "fi在seata的根⽬录,与conf同级的⽬录下,还需要config.txt 配置⽂件,默认也是没有的只需要对mysql的配置进⾏修改完整⽂件:transport.type=TCPtransport.server=NIOtransport.heartbeat=truetransport.enableClientBatchSendRequest=truetransport.threadFactory.bossThreadPrefix=NettyBosstransport.threadFactory.workerThreadPrefix=NettyServerNIOWorkertransport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandlertransport.threadFactory.shareBossWorker=falsetransport.threadFactory.clientSelectorThreadPrefix=NettyClientSelectortransport.threadFactory.clientSelectorThreadSize=1transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThreadtransport.threadFactory.bossThreadSize=1transport.threadFactory.workerThreadSize=defaulttransport.shutdown.wait=3service.vgroupMapping.my_test_tx_group=defaultservice.default.grouplist=127.0.0.1:8091service.enableDegrade=falseservice.disableGlobalTransaction=falseclient.rm.asyncCommitBufferLimit=10000client.rm.lock.retryInterval=10client.rm.lock.retryTimes=30client.rm.lock.retryPolicyBranchRollbackOnConflict=trueclient.rm.reportRetryCount=5client.rm.tableMetaCheckEnable=falseclient.rm.tableMetaCheckerInterval=60000client.rm.sqlParserType=druidclient.rm.reportSuccessEnable=falseclient.rm.sagaBranchRegisterEnable=falseclient.rm.tccActionInterceptorOrder=-2147482648mitRetryCount=5client.tm.rollbackRetryCount=5client.tm.defaultGlobalTransactionTimeout=60000client.tm.degradeCheck=falseclient.tm.degradeCheckAllowTimes=10client.tm.degradeCheckPeriod=2000client.tm.interceptorOrder=-2147482648store.mode=filestore.lock.mode=filestore.session.mode=filestore.publicKey=xxstore.file.dir=file_store/datastore.file.maxBranchSessionSize=16384store.file.maxGlobalSessionSize=512store.file.fileWriteBufferCacheSize=16384store.file.flushDiskMode=asyncstore.file.sessionReloadReadSize=100store.db.datasource=druidstore.db.dbType=mysqlstore.db.driverClassName=com.mysql.jdbc.Driverstore.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true er=rootstore.db.password=123456store.db.minConn=5store.db.maxConn=30store.db.globalTable=global_tablestore.db.branchTable=branch_tablestore.db.queryLimit=100store.db.lockTable=lock_tablestore.db.maxWait=5000store.redis.mode=singlestore.redis.single.host=127.0.0.1store.redis.single.port=6379store.redis.sentinel.masterName=xxstore.redis.sentinel.sentinelHosts=xxstore.redis.maxConn=10store.redis.minConn=1store.redis.maxTotal=100store.redis.database=0store.redis.password=xxstore.redis.queryLimit=100mittingRetryPeriod=1000server.recovery.asynCommittingRetryPeriod=1000server.recovery.rollbackingRetryPeriod=1000server.recovery.timeoutRetryPeriod=1000server.maxCommitRetryTimeout=-1server.maxRollbackRetryTimeout=-1server.rollbackRetryTimeoutUnlockEnable=falseserver.distributedLockExpireTime=10000client.undo.dataValidation=trueclient.undo.logSerialization=jacksonclient.undo.onlyCareUpdateColumns=trueserver.undo.logSaveDays=7server.undo.logDeletePeriod=86400000client.undo.logTable=undo_logpress.enable=truepress.type=zippress.threshold=64klog.exceptionRate=100transport.serialization=seatapressor=nonemetrics.enabled=falsemetrics.registryType=compactmetrics.exporterList=prometheusmetrics.exporterPrometheusPort=9898在conf⽬录中,使⽤Git Bash进⼊命令⾏,输⼊sh nacos-config.sh 127.0.0.1这是对Seata进⾏初始化配置,上图表⽰所有配置都成功设置了在nacos中可以看到出现了seata相关的配置接下来在seata数据库中,新建三个表drop table if exists `global_table`;create table `global_table` (`xid` varchar(128) not null,`transaction_id` bigint,`status` tinyint not null,`application_id` varchar(32),`transaction_service_group` varchar(32),`transaction_name` varchar(128),`timeout` int,`begin_time` bigint,`application_data` varchar(2000),`gmt_create` datetime,`gmt_modified` datetime,primary key (`xid`),key `idx_gmt_modified_status` (`gmt_modified`, `status`),key `idx_transaction_id` (`transaction_id`));drop table if exists `branch_table`;create table `branch_table` (`branch_id` bigint not null,`xid` varchar(128) not null,`transaction_id` bigint ,`resource_group_id` varchar(32),`resource_id` varchar(256) ,`lock_key` varchar(128) ,`branch_type` varchar(8) ,`status` tinyint,`client_id` varchar(64),`application_data` varchar(2000),`gmt_create` datetime,`gmt_modified` datetime,primary key (`branch_id`),key `idx_xid` (`xid`));drop table if exists `lock_table`;create table `lock_table` (`row_key` varchar(128) not null,`xid` varchar(96),`transaction_id` long ,`branch_id` long,`resource_id` varchar(256) ,`table_name` varchar(32) ,`pk` varchar(36) ,`gmt_create` datetime ,`gmt_modified` datetime,primary key(`row_key`));在项⽬相关的数据库中,新建表undo_log ⽤于记录撤销⽇志CREATE TABLE `undo_log` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`branch_id` bigint(20) NOT NULL,`xid` varchar(100) NOT NULL,`context` varchar(128) NOT NULL,`rollback_info` longblob NOT NULL,`log_status` int(11) NOT NULL,`log_created` datetime NOT NULL,`log_modified` datetime NOT NULL,`ext` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;最后在bin⽬录中,启动命令⾏,执⾏seata-server.bat 启动Seata服务项⽬应⽤SeataSpringCloud项⽬中有两个服务:订单服务和库存服务,基本业务是:购买商品插⼊订单减少库存订单详情表DROP TABLE IF EXISTS `tb_order_detail`;CREATE TABLE `tb_order_detail` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单详情id ',`order_id` bigint(20) NOT NULL COMMENT '订单id',`sku_id` bigint(20) NOT NULL COMMENT 'sku商品id',`num` int(11) NOT NULL COMMENT '购买数量',`title` varchar(256) NOT NULL COMMENT '商品标题',`own_spec` varchar(1024) DEFAULT '' COMMENT '商品动态属性键值集',`price` bigint(20) NOT NULL COMMENT '价格,单位:分',`image` varchar(128) DEFAULT '' COMMENT '商品图⽚',PRIMARY KEY (`id`),KEY `key_order_id` (`order_id`) USING BTREE) ENGINE=MyISAM AUTO_INCREMENT=131 DEFAULT CHARSET=utf8 COMMENT='订单详情表';库存表DROP TABLE IF EXISTS `tb_stock`;CREATE TABLE `tb_stock` (`sku_id` bigint(20) NOT NULL COMMENT '库存对应的商品sku id',`seckill_stock` int(9) DEFAULT '0' COMMENT '可秒杀库存',`seckill_total` int(9) DEFAULT '0' COMMENT '秒杀总数量',`stock` int(9) NOT NULL COMMENT '库存数量',PRIMARY KEY (`sku_id`)) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='库存表,代表库存,秒杀库存等信息';⽗项⽬定义了springboot、springcloud、springcloud-alibaba的版本<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.10.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>0.9.0.RELEASE</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2.2.1.RELEASE</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Hoxton.SR8</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>⼦项⽬的依赖定义了nacos和seata客户端<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><exclusions><exclusion><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId></exclusion></exclusions></dependency><dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>1.2.0</version></dependency>⼦项⽬配置⽂件完整配置server:port: 8001spring:application:name: stock-servicecloud:nacos:discovery:server-addr: localhost:8848alibaba:seata:enabled: trueenable-auto-data-source-proxy: truetx-service-group: my_test_tx_groupregistry:type: nacosnacos:application: seata-serverserver-addr: 127.0.0.1:8848username: nacospassword: nacosconfig:type: nacosnacos:server-addr: 127.0.0.1:8848group: SEATA_GROUPusername: nacospassword: nacosservice:vgroup-mapping:my_test_tx_group: defaultdisable-global-transaction: falseclient:rm:report-success-enable: falsedatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/eshop?serverTimezone=UTC&useUnicode=true&useSSL=false&characterEncoding=utf8 username: rootpassword: 123456库存服务定义了减库存的⽅法@RestControllerpublic class StockController {@Autowiredprivate IStockService stockService;@PutMapping("/stock")public ResponseEntity<Stock> reduceSkuStock(@RequestParam("skuId")Long skuId,@RequestParam("number")Integer number){Stock stock = stockService.getById(skuId);if(stock.getStock() < number){throw new RuntimeException("库存不⾜,SkuId:" + skuId);}stock.setStock(stock.getStock() - number);stockService.updateById(stock);return ResponseEntity.ok(stock);}}订单服务在插⼊订单后,使⽤Feign调⽤了减库存的服务@Servicepublic class OrderDetailServiceImpl extends ServiceImpl<OrderDetailMapper, OrderDetail> implements IOrderDetailService {//库存服务Feign@Autowiredprivate StockFeignClient stockFeignClient;// @Transactional@GlobalTransactional(rollbackFor = {Exception.class})@Overridepublic void makeOrder(OrderDetail orderDetail) {this.save(orderDetail); //保存订单int x = 11 / 0; //抛出异常//减库存stockFeignClient.reduceSkuStock(orderDetail.getSkuId(),orderDetail.getNum());}}插订单和减库存属于两个服务,传统的@Transactional已经不能保证它们的原⼦性了这⾥使⽤了Seata提供的@GlobalTransactional全局事务注解,出现任何异常后都能实现业务回滚。
Spring事务配置的五种方式

Spring事务原理统观spring事务,围绕着两个核心PlatformTransactionManager和TransactionStatusspring提供了几个关于事务处理的类:TransactionDefinition //事务属性定义TranscationStatus //代表了当前的事务,可以提交,回滚。
PlatformTransactionManager这个是spring提供的用于管理事务的基础接口,其下有一个实现的抽象类AbstractPlatformTransactionManager,我们使用的事务管理类例如DataSourceTransactionManager等都是这个类的子类。
一般事务定义步骤:TransactionDefinition td = new TransactionDefinition();TransactionStatus ts = transactionManager.getTransaction(td);try{ //do sthmit(ts);}catch(Exception e){transactionManager.rollback(ts);}spring提供的事务管理可以分为两类:编程式的和声明式的。
编程式的,比较灵活,但是代码量大,存在重复的代码比较多;声明式的比编程式的更灵活。
编程式主要使用transactionTemplate。
省略了部分的提交,回滚,一系列的事务对象定义,需注入事务管理对象.void add(){transactionTemplate.execute( new TransactionCallback(){pulic Object doInTransaction(TransactionStatus ts){ //do sth}}}声明式:使用TransactionProxyFactoryBean:<bean id="userManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"><property name="transactionManager"><ref bean="transactionManager"/></property><property name="target"><ref local="userManagerTarget"/></property><property name="transactionAttributes"><props><prop key="insert*">PROPAGATION_REQUIRED</prop><prop key="update*">PROPAGATION_REQUIRED</prop><prop key="*">PROPAGATION_REQUIRED,readOnly</prop></props></property></bean>围绕Poxy的动态代理能够自动的提交和回滚事务org.springframework.transaction.interceptor.TransactionProxyFactoryBeanPROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。
spring 事务 实现原理

spring 事务实现原理Spring事务的实现原理主要依赖于数据库或者其他存储系统的事务功能。
1. 数据库事务支持:Spring事务的实现依赖于数据库的事务功能。
通常情况下,数据库支持ACID(原子性、一致性、隔离性和持久性)事务特性,可以通过SQL的BEGIN、COMMIT 和ROLLBACK等命令来控制事务的开始、提交和回滚操作。
2. 事务管理器:Spring框架提供了事务管理器(TransactionManager)接口,用于管理事务的开始、提交和回滚等操作。
事务管理器可以根据配置信息选择不同的实现,如使用JDBC事务管理器(DataSourceTransactionManager)或者JTA事务管理器(JtaTransactionManager)等。
3. 编程式事务:Spring框架提供了编程式事务管理的支持,通过编写代码显式地控制事务的边界。
开发者可以在代码中使用事务管理器的API来手动管理事务,包括事务的开始、提交和回滚等操作。
这种方式灵活性较高,但也需要开发者手动处理事务的细节。
4. 声明式事务:Spring框架还提供了声明式事务管理的支持,通过使用注解或者XML配置来定义事务的属性和边界。
开发者可以使用@Transactional注解或者事务配置元素来标记方法或者类,指示该方法或者类在执行时需要开启事务。
Spring会在方法调用前后自动地管理事务的开始、提交和回滚等操作。
5. 事务传播行为:Spring事务还支持事务传播行为的定义,即一个方法调用另一个带有事务的方法时事务该如何传播。
例如,当前方法可能会暂停事务、加入到现有的事务中或者创建一个新的事务。
Spring提供了多种事务传播行为的选项,如REQUIRED、REQUIRES_NEW、NESTED等,可以根据需要进行配置。
总的来说,Spring事务的实现原理是通过与数据库或其他存储系统的事务功能结合,通过事务管理器来管理事务的开始、提交和回滚等操作。
spring事务原理

spring事务原理数据库系统内,事务指一系列连续的操作,这系列操作被看作一个整体,要么完全执行,要么完全不执行,不会出现执行部分的情况,这是数据库系统的基本特征之一。
在传统数据库系统中,事务的管理是由数据库系统自身提供的一种功能,然而随着数据库系统的复杂性的增加,数据库中的事务管理越来越复杂,对数据库操作有更多的要求,这样就催生了应用层事务管理技术,而当前最流行的事务管理技术是Spring事务。
Spring事务是一种基于AOP(面向切面编程)的分布式事务管理技术,它能够帮助系统更好的控制事务的处理过程,从而使系统内的数据更加有效率的共享,降低数据库事务的管理复杂度,提高数据库系统的可用性及性能。
Spring事务原理包括几个要素:事务模型、分布式事务处理框架、事务实现机制以及事务管理工具等。
一、事务模型Spring事务原理的核心是事务模型,即它把事务分为两种:提交成功事务和失败事务,在Spring提供的事务模型中,每个事务都只有两种结果,要么提交成功,要么提交失败,而不存在半提交的状态。
在Spring的事务模型中,事务的分类还要求事务的原子性,它要求一旦提交事务,事务就不会再次改变,或者改变的程度会很小。
原子性还表明事务的执行要么完全成功,要么完全失败,不会出现半成功半失败的情况。
二、分布式事务处理框架Spring提供了基于AOP技术的分布式事务处理框架,这种分布式事务处理框架能够有效地支持不同数据库之间的事务处理,它包括三个部分:事务管理器(TransactionManager)、事务拦截器(TransactionInterceptor)和事务事件监听器(TransactionListener)。
事务管理器是Spring对分布式事务的抽象,它可以处理不同类型的事务,它的实现通常会涉及到一些事务拦截器。
事务拦截器是Spring提供的一种安全机制,它能够在事务处理过程中,根据配置的规则,来拦截事务的执行,以便能够在事务处理过程中发现任何可能存在的问题,并对其进行调整,使得事务能够顺利的提交。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Spring分布式事务实现分布式事务是指操作多个数据库之间的事务,spring的org.springframework.transaction.jta.JtaTransactionManager,提供了分布式事务支持。
如果使用WAS的JTA支持,把它的属性改为WebSphere对应的TransactionManager。
在tomcat下,是没有分布式事务的,不过可以借助于第三方软件jotm(Java Open Transaction Manager )和AtomikosTransactionsEssentials实现,在spring中分布式事务是通过jta(jotm,atomikos)来进行实现。
1、/2、/Main/TransactionsEssentials一、使用JOTM例子(1) Dao及实现GenericDao接口:?1 2 3 4 public interface GenericDao {public int save(String ds, String sql, Object[] obj) throws Exception; public intfindRowCount(String ds, String sql);}GenericDaoImpl实现:?1 2 3 4 5 6 7 8 910111213141516171819202122232425262728 public class GenericDaoImpl implements GenericDao{private JdbcTemplatejdbcTemplateA;private JdbcTemplatejdbcTemplateB;public void setJdbcTemplateA(JdbcTemplatejdbcTemplate) {this.jdbcTemplateA = jdbcTemplate;}public void setJdbcTemplateB(JdbcTemplatejdbcTemplate) {this.jdbcTemplateB = jdbcTemplate;}public int save(String ds, String sql, Object[] obj) throws Exception{ if(null == ds || "".equals(ds)) return -1;try{if(ds.equals("A")){return this.jdbcTemplateA.update(sql, obj);}else{return this.jdbcTemplateB.update(sql, obj);}}catch(Exception e){e.printStackTrace();throw new Exception("执行" + ds + "数据库时失败!");}}public intfindRowCount(String ds, String sql) {293031323334353637 if(null == ds || "".equals(ds)) return -1;if(ds.equals("A")){return this.jdbcTemplateA.queryForInt(sql); }else{return this.jdbcTemplateB.queryForInt(sql); }}}(2) Service及实现UserService接口:?1 2 3 public interface UserService {public void saveUser() throws Exception; }UserServiceImpl实现:?1 2 3 4 5 6 7 8 9101112131415161718192021222324 public class UserServiceImpl implements UserService{private GenericDaogenericDao;public void setGenericDao(GenericDaogenericDao) {this.genericDao = genericDao;}public void saveUser() throws Exception {String userName = "user_" + Math.round(Math.random()*10000); System.out.println(userName);StringBuildersql = new StringBuilder();sql.append(" insert into t_user(username, gender) values(?,?); "); Object[] objs = new Object[]{userName,"1"};genericDao.save("A", sql.toString(), objs);sql.delete(0, sql.length());sql.append(" insert into t_user(name, sex) values(?,?); ");objs = new Object[]{userName,"男的"};//值超出范围genericDao.save("B", sql.toString(), objs);}}(3) applicationContext-jotm.xml ?1 2 3 <?xml version="1.0" encoding="UTF-8"?><beans xmlns="/schema/beans" xmlns:xsi="/2001/XMLSchema-instance"4 5 6 7 8 910111213141516171819202122232425262728293031323334353637383940414243444546474849505152 xmlns:context="/schema/context"xmlns:aop="/schema/aop"xmlns:tx="/schema/tx"xsi:schemaLocation="/schema/beans /schema/beans/s /schema/context /schema/context/spring-context-2.5.x /schema/tx /schema/tx/spring-tx-2.5.xsd/schema/aop /schema/aop/spring-aop-2.5.xsd"><description>springJTA</description><!--指定Spring配置中用到的属性文件--><bean id="propertyConfig"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="locations"><list><value>classpath:jdbc.properties</value></list></property></bean><!-- JOTM实例--><bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"><property name="defaultTimeout" value="500000"/></bean><!-- JTA事务管理器--><bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"><property name="userTransaction" ref="jotm" /></bean><!-- 数据源A --><bean id="dataSourceA" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown"><property name="dataSource"><bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"><property name="transactionManager" ref="jotm"/><property name="driverName" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/></bean></property><property name="user" value="${ername}"/><property name="password" value="${jdbc.password}"/></bean><!-- 数据源B --><bean id="dataSourceB" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown"><property name="dataSource"><bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"><property name="transactionManager" ref="jotm"/><property name="driverName" value="${jdbc2.driver}"/>535455565758596061626364656667686970717273747576777879808182838485868788899091 <property name="url" value="${jdbc2.url}"/></bean></property><property name="user" value="${ername}"/><property name="password" value="${jdbc2.password}"/></bean><bean id = "jdbcTemplateA"class = "org.springframework.jdbc.core.JdbcTemplate"><property name = "dataSource" ref="dataSourceA"/></bean><bean id = "jdbcTemplateB"class = "org.springframework.jdbc.core.JdbcTemplate"><property name = "dataSource" ref="dataSourceB"/></bean><!-- 事务切面配置--><aop:config><aop:pointcut id="pointCut"expression="execution(* com.logcd.service..*.*(..))"/><!-- 包及其子包下的所有方法--><aop:advisorpointcut-ref="pointCut" advice-ref="txAdvice"/><aop:advisorpointcut="execution(* *mon.service..*.*(..))" advice-ref="txAdvice"/></aop:config><!-- 通知配置--><tx:advice id="txAdvice" transaction-manager="jtaTransactionManager"><tx:attributes><tx:method name="delete*" rollback-for="Exception"/><tx:method name="save*" rollback-for="Exception"/><tx:method name="update*" rollback-for="Exception"/><tx:method name="find*" read-only="true" rollback-for="Exception"/></tx:attributes></tx:advice><bean id="genericDao" class="com.logcd.dao.impl.GenericDaoImpl" autowire="byName"></bean><bean id="userService" class="erServiceImpl" autowire="byName"></bean> </beans>(4) 测试?1 2 3 4 5 6 7 public class TestUserService{private static UserServiceuserService;@BeforeClasspublic static void init(){ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext-jotm.xml");8 9101112131415161718192021 userService = (UserService)app.getBean("userService"); }@Testpublic void save(){System.out.println("begin...");try{userService.saveUser();}catch(Exception e){System.out.println(e.getMessage());}System.out.println("finish...");}}二、关于使用atomikos实现(1) 数据源配置?1 2 3 4 5 6 7 8 91011121314151617181920212223 <bean id="dataSourceA" class="com.atomikos.jdbc.SimpleDataSourceBean" init-method="init" destroy-method="close"> <property name="uniqueResourceName"><value>${datasource.uniqueResourceName}</value></property><property name="xaDataSourceClassName"><value>${database.driver_class}</value></property><property name="xaDataSourceProperties"><value>URL=${database.url};user=${ername};password=${database.password}</value></property><property name="exclusiveConnectionMode"><value>${connection.exclusive.mode}</value></property><property name="connectionPoolSize"><value>${connection.pool.size}</value></property><property name="connectionTimeout"><value>${connection.timeout}</value></property><property name="validatingQuery"><value>SELECT 1</value></property></bean>(2)、事务配置?1 2 <bean id="atomikosTransactionManager" class="erTransactionManager" init-method="init" destroy-method="close">3 4 5 6 7 8 9101112131415161718192021222324252627 <property name="forceShutdown" value="true"/></bean><bean id="atomikosUserTransaction" class="erTransactionImp"><property name="transactionTimeout" value="${transaction.timeout}"/></bean><!-- JTA事务管理器--><bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager" ref="atomikosTransactionManager"/><property name="userTransaction" ref="atomikosUserTransaction"/></bean><!-- 事务切面配置--><aop:config><aop:pointcut id="serviceOperation" expression="execution(* *..service*..*(..))"/><aop:advisorpointcut-ref="serviceOperation" advice-ref="txAdvice"/></aop:config><!-- 通知配置--><tx:advice id="txAdvice" transaction-manager="springTransactionManager"><tx:attributes><tx:method name="*" rollback-for="Exception"/></tx:attributes></tx:advice>有关JTAJTA全称为Java Transaction API,顾名思义JTA定义了一组统一的事务编程的接口,这些接口如下:XAResourceXAResource接口是对实现了X/Open CAE规范的资源管理器(Resource Manager,数据库就是典型的资源管理器) 的抽象,它由资源适配器(Resource Apdater) 提供实现。