编程式事务:在业务代码中添加事务控制代码,这样的事务控制机制就叫做编程式事务
声明式事务:通过xml或者注解配置的⽅式达到事务控制的⽬的,叫做声明式事务
事务基本概念
1.事务概念
事务指逻辑上的⼀组操作,组成这组操作的各个单元,要么全部成功,要么全部不成功。从⽽确保了数据的准确与安全。
2.事务的四大特性
3.事务的隔离级别
不考虑隔离级别,会出现以下情况:(以下情况全是错误的),也即为隔离级别在解决事务并发问题
脏读:⼀个线程中的事务读到了另外⼀个线程中未提交的数据。
不可重复读:⼀个线程中的事务读到了另外⼀个线程中已经提交的update的数据(前后内容不⼀样)
场景:
员⼯A发起事务1,查询⼯资,⼯资为1w,此时事务1尚未关闭财务⼈员发起了事务2,给员⼯A张了2000块钱,并且提交了事务员⼯A通过事务1再次发起查询请求,发现⼯资为1.2w,原来读出来1w读不到了,叫做不可重复读
虚读(幻读):⼀个线程中的事务读到了另外⼀个线程中已经提交的insert或者delete的数据(前后条数不⼀样)
场景:
事务1查询所有⼯资为1w的员⼯的总数,查询出来了10个⼈,此时事务尚未关闭事务2财务⼈员发起,新来员⼯,⼯资1w,向表中插⼊了2条数据,并且提交了事务事务1再次查询⼯资为1w的员⼯个数,发现有12个⼈,
数据库共定义了四种隔离级别:
Serializable(串⾏化):可避免脏读、不可重复读、虚读情况的发⽣。(串⾏化) 最⾼
Repeatable read(可重复读):可避免脏读、不可重复读情况的发⽣。(幻读有可能发⽣)
第⼆该机制下会对要update的⾏进⾏加锁
Read committed(读已提交):可避免脏读情况发⽣。不可重复读和幻读⼀定会发⽣。 第三
Read uncommitted(读未提交):最低级别,以上情况均⽆法保证。(读未提交) 最低
注意:级别依次升⾼,效率依次降低
MySQL的默认隔离级别是:REPEATABLE READ。
查询当前使⽤的隔离级别:select @@tx_isolation;
设置MySQL事务的隔离级别:set session transaction isolation level xxx;
(设置的是当前mysql连接会话的,并不是永久改变的)
开启事务:start transaction
设置事务的自动提交模式:SET autocommit = 0|1|ON|OFF;
(autocommit 的默认值是 ON)
回滚事务:rollback
提交事务:commit
4.事务的传播行为
事务往往在service层进⾏控制,如果出现service层⽅法A调⽤了另外⼀个service层⽅法B,A和B⽅法本身都已经被添加了事务控制,那么A调⽤B的时候,就需要进⾏事务的⼀些协商,这就叫做事务的传播⾏为。
A调⽤B,我们站在B的⻆度来观察来定义事务的传播⾏为
PROPAGATION_REQUIRED | 如果当前没有事务,就新建⼀个事务,如果已经存在⼀个事务中,加⼊到这个事务中。这是最常⻅的选择。 |
PROPAGATION_SUPPORTS | ⽀持当前事务,如果当前没有事务,就以⾮事务⽅式执⾏。 |
PROPAGATION_MANDATORY | 使⽤当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以⾮事务⽅式执⾏操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以⾮事务⽅式执⾏,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执⾏。如果当前没有事务,则执⾏与PROPAGATION_REQUIRED类似的操作。 |
Spring中事务的API
JDBC中的事务操作:
原始对象(就是我们所开发的一个一个Service):
public class XXXUserServiceImpl{ private xxxDAO xxxDAO set get
1. 原始对象 --》原始方法 --》核心功能(业务处理+Dao调用)
2. 需要调用Dao,就要依赖Dao,就需要通过成员变量,让Spring把Dao注入到Service的成员变量
}
额外功能(通过MethodInterceptor,实现里面的invoke方法)4
public Object invoke(MethodInvocation invocation) {
try {
Connection.setAutoCommit(false);
Object ret = invocation.proceed();
Connection.commit();
} catch (Exception e) {
Connection.rollback();
}
return ret;
}
}
在方法执行前,设置开启事务,执行后,提交事务,只要出现异常,就回滚,所以用try catch抛出!
解释说明:
第一种方法,是人为来书写,那么既然每个人都可以写,那么Spring就帮你把这个事情做了,你不需要再写了,给你封装了一个类叫做org.springframework.jdbc.datasource.DataSourceTransactionManager,这类的核心功能,就是控
制事务这个额外功能,封装的就是我们上面写的invoke这些代码,所以后续再应用Spring开发事务功能,这些代码你就不需要再写了!!!
有个细节要注意下,上面这段代码,控制事务的这些东西,是由Connection来帮你完成的,所以要想这段代码起作用,是不是就需要连接对象来支持??
所以DataSourceTransactionManager需要Connection对象,需要,就等于依赖,就要注入给DataSourceTransactionManager!还有就是我们要在创建连接的过程中,为了提高连接的效率,我们引入了连接池,希望通过连接池来帮我
Spring编程式事务:
public interface PlatformTransactionManager {
//根据事务定义TransactionDefinition,获取事务
TransactionStatus getTransaction(TransactionDefinition definition);
//提交事务
void commit(TransactionStatus status);
//回滚事务
void rollback(TransactionStatus status);
}
此接⼝是Spring的事务管理器核⼼接⼝。Spring本身并不⽀持事务实现,只是负责提供标准,应⽤底层⽀持什么样的事务,需要提供具体实现类。此处也是策略模式的具体应⽤。在Spring框架中,也为我们内置了⼀些具体策略,例如:DataSourceTransactionManager,HibernateTransactionManager等等。(和HibernateTransactionManager事务管理器在spring-orm-5.1.12.RELEASE.jar中)
1.Spring JdbcTemplate(数据库操作⼯具)、Mybatis(mybatis-spring.jar)————>DataSourceTransactionManager
(DataSourceTransactionManager 归根结底是横切逻辑代码,声明式事务要做的就是使⽤Aop(动态代理)来将事务控制逻辑织⼊到业务代码)
2.Hibernate框架 ——————> HibernateTransactionManager
工作中使用案例
import com.weaver.common.form.dto.data.FormDataDetailDto;
import com.weaver.common.form.dto.data.FormDataDto;
import com.weaver.framework.rpc.annotation.RpcReference;
import com.weaver.workflow.common.entity.core.flow.RequestBaseInfoEntity;
import com.weaver.workflow.common.entity.core.flow.RequestUpdateFlowSysInfoEntity;
import com.weaver.workflow.common.entity.formdef.FormDataEntity;
import com.weaver.workflow.common.entity.formdef.WfFormFieldEntity;
import com.weaver.workflow.common.entity.formdef.WfpRelateFormEntity;
import com.weaver.workflow.common.entity.org.WeaUser;
import com.weaver.workflow.common.entity.pathdef.pathnode.base.WfpNodeBaseEntity;
import com.weaver.workflow.common.entity.pathdef.pathnode.nodeformrelate.WfpNodeFormRelateEntity;
import com.weaver.workflow.common.framework.entity.BizLogContext;
import com.weaver.workflow.common.framework.exception.ECException;
import com.weaver.workflow.common.framework.interceptor.AbstractCommonCommand;
import com.weaver.workflow.common.framework.interceptor.CommandContext;
import com.weaver.workflow.common.framework.util.ServiceUtil;
import com.weaver.workflow.common.util.DataTransUtil;
import com.weaver.workflow.common.util.ModuleUtil;
import com.weaver.workflow.common.util.OrgUtil;
import com.weaver.workflow.core.dao.flow.RequestBaseDao;
import com.weaver.workflow.core.form.util.WfcContextUtil;
import com.weaver.workflow.core.form.util.WfcRelateFormDataUtil;
import com.weaver.workflow.core.form.util.WfcRelateFormUtil;
import com.weaver.workflow.core.form.util.WfcWeaFormUtil;
import com.weaver.workflow.core.services.RequestOperateService;
import com.weaver.workflow.core.services.impl.RequestOperateServiceImpl;
import com.weaver.workflow.pathdef.api.rest.mark.WfpGenerateMarkRest;
import com.weaver.workflow.pathdef.api.rest.pathnode.NodeFormRelateRest;
import com.weaver.workflow.pathdef.api.rest.pathnode.WfpNodeRest;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 生成流水编号
*
* @author: szh
* @created: 2021/03/17 15:00
*/
public class RegenerateMarkConfigurator extends AbstractCommonCommand<Map<String, Object>> {
private static Logger logger = LoggerFactory.getLogger(RegenerateMarkConfigurator.class);
@RpcReference(group = "${weaver.workflow.group}")
private WfpGenerateMarkRest wfpGenerateMarkRest;
@RpcReference(group = "${weaver.workflow.group}")
private NodeFormRelateRest nodeFormRelateRest;
@RpcReference(group = "${weaver.workflow.group}")
private WfpNodeRest wfpNodeRest;
@Autowired
WfcRelateFormUtil wfcRelateFormUtil;
@Autowired
private WfcContextUtil wfcContextUtil;
@Autowired
private WfcRelateFormDataUtil wfcRelateFormDataUtil;
@Autowired
private WfcWeaFormUtil wfcWeaFormUtil;
@Autowired
RequestBaseDao requestBaseDao;
/**
* 数据源事务管理类,spring托管,自动注入
*/
@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
/**
* 务管理类,spring托管,自动注入
*/
@Autowired
private TransactionDefinition transactionDefinition;
Map<String, Object> params = new HashMap<>();
Map<String, Object> resultMap = new HashMap<>();
public RegenerateMarkConfigurator(Map<String, Object> params, WeaUser user) {
this.params = params;
this.user = user;
}
@Override
public BizLogContext getLogContext() {
return null;
}
@Override
public Map<String, Object> execute(CommandContext commandContext) {
TransactionStatus transactionStatus = null;
try {
//开启事务
transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
//1.rpc调pathdef获取重新生成编号
resultMap = wfpGenerateMarkRest.regenerateMark(params, user);
// 获取requestId
Long requestId = DataTransUtil.getLongValue((String) params.get("requestId"));
// 获取workflowId
Long workflowId = DataTransUtil.getLongValue((String) params.get("workflowId"));
// 2.更新workflow_requestbase表的requestMark字段
if (!StringUtils.isEmpty((String) resultMap.get("regenerateMark"))) {
RequestOperateService requestOperateService = ServiceUtil.getService(RequestOperateServiceImpl.class, OrgUtil.getCurrentUser());
Map<String,Object> map = new HashMap();
map.put("requestMark",(String)resultMap.get("regenerateMark"));
RequestUpdateFlowSysInfoEntity paramDataEntity = new RequestUpdateFlowSysInfoEntity();
paramDataEntity.setWorkflowId(workflowId);
paramDataEntity.setRequestId(requestId);
paramDataEntity.setSysInfo(map);
requestOperateService.updateFlowSysInfo(paramDataEntity);
}
//2.更新表单字段信息
String markFieldContextId = wfpGenerateMarkRest.getMarkFieldContextId(workflowId, user);
// 更新表单字段的值
if (!StringUtils.isEmpty((String) resultMap.get("regenerateMark")) && !"-1".equals(markFieldContextId)) {
// 上下文转成fieldId
WfFormFieldEntity contextFieldInfo = wfcContextUtil.getContextFieldInfo(Long.parseLong(markFieldContextId), user);
long fieldId = contextFieldInfo.getId();
//获取表单数据dataId
List<Long> requestIds = new ArrayList<>();
requestIds.add(requestId);
List<FormDataEntity> formDataEntities = wfcRelateFormDataUtil.getFormDataEntities(requestIds, user);
long dataId = formDataEntities.get(0).getDataId();
FormDataDto formDataDto = new FormDataDto();
WfpRelateFormEntity firstRelateForm = wfcRelateFormUtil.getFirstRelateForm(workflowId, OrgUtil.getCurrentUser());
long relateKey = firstRelateForm.getRelateKey(); // 表单Id
WfpNodeBaseEntity node = wfpNodeRest.getCreateNodeBaseInfo(workflowId, user.getUserId());
if (node == null) {
throw new ECException("请先创建开始节点");
}
//表单与节点的关联关系
WfpNodeFormRelateEntity nodeFormRelateEntity = nodeFormRelateRest.getNodeFormRelateByNodeIdAndWfId(workflowId, node.getId(), user.getUserId());
long layoutId = nodeFormRelateEntity.getLayoutId(); // 布局Id
// 数据详情
List<FormDataDetailDto> dataDetails = new ArrayList<>();
FormDataDetailDto formDataDetailDto = new FormDataDetailDto();
formDataDetailDto.setFieldId(fieldId);
formDataDetailDto.setContent((String) resultMap.get("requestMark"));
dataDetails.add(formDataDetailDto);
formDataDto.setDataId(dataId);
formDataDto.setFormId(relateKey);
formDataDto.setLayoutMultiId(layoutId);
formDataDto.setDataDetails(dataDetails);
formDataDto.setModule("workflow");
wfcWeaFormUtil.saveFieldData(formDataDto, user);
}
//提交事务
dataSourceTransactionManager.commit(transactionStatus);
} catch (Exception e) {
logger.error("err",e);
// 异常,返回空值,对接到前台渲染函数不进行渲染
resultMap.put("regenerateMark",null);
try {
//回滚数据
if (dataSourceTransactionManager != null && transactionStatus != null) {
dataSourceTransactionManager.rollback(transactionStatus);
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
return resultMap;
}
}
Spring 声明式事务配置
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.1.12.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.4version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.1.12.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>5.1.12.RELEASEversion>
dependency>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" read-only="false" propagation="REQUIRED" isolation="DEFAULT" timeout="-1"/>
<tx:method name="query*" read-only="true" propagation="SUPPORTS"/> tx:attributes>
tx:advice>
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lagou.edu.service.impl.TransferServiceImpl.*(..))"/>
aop:config>
1.xml配置
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--开启spring对注解事务的⽀持-->
<tx:annotation-driven transaction-manager="transactionManager"/>
2.在接⼝、类或者⽅法上添加@Transactional注解
@Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
@EnableTransactionManagement//开启spring注解事务的⽀持
public class SpringConfiguration {
}
Spring声明式事务控制解析
分布式事务解决方案Seata
Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata将为用户提供了AT、TCC、SAGA和XA事务模式,为用户打造一站式的分布式解决方案。
注:官方文档地址:https://seata.io/zh-cn/index.html
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
使用:
@GlobalTransactional这个注解是控制全局事务的,在相应的业务方法上添加这个注解就可以。
AT模式:
整体机制: