当前位置:   article > 正文

【数据库学习】事务及Spring事务机制:@Transactional_transactional 子事务

transactional 子事务

1,概念

提交(commit):
将未存储的SQL语句写入数据库表;

保留点(savepoint):
事务处理中设置的临时占位符(placeholder),可以对它
发布回退(与回退整个事务处理不同)。
保留点越多越好,越多回退会更灵活。

2,事务抽象

Spring Framework对事务管理做了一层抽象:TransactionManager,它是一个空接口。
PlatformTransactionManager 接口继承了TransactionManager,是事务管理的核心接口,包含了事务获取、提交、回滚的三个方法。

在这里插入图片描述
不同数据访问技术对应的PlatformTransactionManager实现:

TransactionManager类数据访问技术举例
DataSourceTransactionManager在仅使用JDBC时使用Spring Boot 默认使用JDBC来控制事务
JpaTransactionManager在使用JPA时使用。同时在实现时还可能使用JDBC
HibernateTransactionManager在使用Hibernate时使用。同时在实现时还可能使用JDBC
JtaHibernate在使用全局事务(也就是使用应用程序服务器的分布式事务管理功能)时使用。此时可以使用任何数据访问技术

3,事务属性

TransactionDefinition接口描述了事务定义,包含了几个与事务密切相关的属性:

1)传播性

事务传播性定义了7个级别,规定了事务方法和事务方法发生嵌套调用时事务如何进行传播:

A方法调用B方法,B定义了以下传播行为:

传播行为类型规则举例
PROPAGATION_REQUIRED
请求事务(required)
0(默认)当前有事务就用当前事务,没有事务就重启一个事务。A无事务,B开启一个新事务;
A有事务,那么B加入A的事务。B异常,一起回滚。
PROPAGATION_SUPPORTS
可支持事务
1事务不是必须的,可以有也可以没有A无事务,B也无;
A有事务,那么B加入A的事务。
PROPAGATION_MANDATORY
强制事务(mandatory,强制的)
2必须有一个事务,否则报错A无事务,就抛出异常;===>强制必须有事务
A有事务,那么B加入A的事务。
PROPAGATION_REQUIRES_NEW
请求新事务
3新启动一个事务,如果当前存在一个事务则将其挂起A无事务,B新建事务;
A有事务,B新建一个事务,且与A的事务独立。B异常,B回滚;A如果吃掉了B的异常,不回滚。
PROPAGATION_NOT_SUPPORTED
不支持事务
4不支持事务,以非事务方式运行A无事务,B也无;
A有事务,B把A的事务挂起,无事务执行
PROPAGATION_NEVER
拒绝事务
5不支持事务,如果当前存在一个事务则抛异常-
PROPAGATION_NESTED
嵌套事务(nested,嵌套)
6如果当前存在一个事务,则在该事务内再启动一个事务A无事务,B创建新事务;
A有事务,B创建其子事务。==》父事务回滚,子也回滚。子事务异常,回滚;A如果吃掉了B的异常,不回滚。
B方法被调用时创建了一个保存点,B异常A不一定回滚。

2)隔离级别

对应数据库的四个隔离级别定义了4种隔离类型,默认隔离级别设置为-1,使用底层数据源的配置。
比如:mysql默认REPEATABLE_READ;Oracle和pg默认READ_COMMITTED。

3)超时时间

4)是否只读

4,注解方式

1)@Transactional 使用

  1. 开启注解式事务的支持@EnableTransactionManagement
  2. @Transactional(rollbackFor = Exception.class, propagation = Propagation.PROPAGATION_REQUIRED)
    常用属性有:
属性默认值作用
transactionManager
或者value
transactionManager指定事务管理器
propagationREQUIRED事务传播性
isolationDEFAULT事务隔离级别
(使用的同时需要底层数据库支持这些值)
timeoutTIMEOUT_DEFAULT,-1事务超时时间;
超时自动回滚事务。
readOnlyfalse是否为只读事务;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
rollbackFor
rollbackForClassName
java.lang.RuntimeException或者子类异常时回滚,可用于单元测试;
noRollbackForjava.lang.Exception或者子类当期待的异常发生时,不回滚
  1. @Transactional作用域
作用域说明
表示类中的所有公共方法都被事务化
方法优先级高于类的作用域
  1. 提前回滚
    又是没有异常发生,我们依然想回滚:
    (可以用在单元测试上测试回滚)
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  • 1

2)@Transactional 原理

目标方法声明式调用 @Transactional,通过AOP代理,目标方法形成一个切面对象,在spring的bean的初始化过程中,创建代理对象注册到IOC容器中。方法执行前先把事务自动提交设置为false;方法执行时如果没有异常,会自动将事务提交。默认会对方法中的RuntimeException和Error进行回滚。

3)@Transacational 失效场景

  1. 数据库不支持事务;
  2. 异常失效导致事务不回滚;
    异常被吃掉 或者 checked 异常,此时不回滚事务。
  3. aop场景失效
    a) 方法自调用;==》相当于this.方法调用,不走aop。
    解决方式:通过aop方式调用;如果不能通过@Autowired的对象调用,也可以从ApplicationContext中获取自己的代理对象操作这个方法。
    b) 类没有被注入IOC容器;
  4. 非public方法
    proxy-target-class=true,那么基于类的代理(CGLIB动态代理)将起作用;
    缺省为false,那么标准的JDK基于接口的代理将起作用。
    a) JDK:只能动态代理publicpublic final修饰符的方法;
    b) CGLIB:由于使用final,static,private修饰符的方法都不能被子类复写,即:除了public的非final的实例方法,其他方法均无效;
    c) ApectJ代理模式:非public修饰的方法也可以被代理。
  5. 多个事务管理器。
    多数据源情况下,存在多个事务管理器时,如果不指定事务管理器,会按照事务管理器在配置文件中的初始化顺序使用其中一个。显示指定采用哪个事务@Transaction(value=“xxx”)

5,多数据源事务管理

1)单数据源事务

直接用@Transactional 注解,无需自己管理事务,Spring Boot 默认使用DataSourceTransactionManager 来管理事务。

@Transactional注解的value属性就是用来指定具体TransactionManager的,通过id或者name来指定唯一一个TransactionManager。

    @Transactional(value = "database2TransactionManager")
    public void test(String a) {
        // business operation
    }
  • 1
  • 2
  • 3
  • 4

2)分布式事务管理器(JTA)

在使用Transactional注解的时候,通过value指定不同的事务管理器。

1>场景

  1. 一个方法里操作两个数据源;
  2. 一个数据分布在不同的节点上。

2>实现

import org.neo4j.ogm.session.SessionFactory;
import org.springframework.data.neo4j.transaction.Neo4jTransactionManager;
import org.springframework.data.transaction.ChainedTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.persistence.EntityManagerFactory;

@Configuration
public class TransactionalConfig {

    /**
     * pg事务管理器 (默认)
     */
    @Bean
    @Primary
    public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
        return new JpaTransactionManager(emf);
    }

    /**
     * 定义neo4j事务管理器
     */
    @Bean("neo4jTransactionManager")
    public Neo4jTransactionManager neo4jTransactionManager(SessionFactory sessionFactory) {
        return new Neo4jTransactionManager(sessionFactory);
    }


    /**
     * 多事务管理器
     * ChainedTransactionManager只是同时开启两个事务,自动的对两个事务,同时开启,同时提交(或回滚)。它对多个数据写数据,并不是一个原子操作,不能像XA那样保证对数据操作的完整性,一致性。
看源代码:它用一个列表来管理多个数据源的事务管理器。
     */
    @Autowired
    @Bean(name = "multiTransactionManager")
    public PlatformTransactionManager multiTransactionManager(
            Neo4jTransactionManager neo4jTransactionManager,
            JpaTransactionManager pgTransactionManager) {
        return new ChainedTransactionManager(
                neo4jTransactionManager, pgTransactionManager);
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

有个坑:如果在junit中测试(加上@Transactional):无论如何都不能成功提交,会rollback。

3>XA规范

XA是分布式事务规范,定义了分布式事务模型。定义了以下四个角色:

  1. 应用程序(AP)
  2. 事务管理器(协调者TM)
  3. 资源管理器(参与者RM)
  4. 通信资源管理器(CRM)

JTA是java对XA规范的实现。
AP告诉TM要开始事务,TM协调RM并将事务的xid分配给每个RM。

a)两阶段协议
  1. prepare阶段
    每个参与者执行本地事务,但不提交,进入ready状态,并通知TM已经准备就绪。
  2. commit阶段
    当TM收到每个参与者都ready后,通知所有参与者commit;如果有参与者是fail,则发送rollback命令,所有参与者做回滚操作。
    这个阶段如果发生了网络故障容易引发数据不一致问题。
b)三阶段协议

优化两阶段单点故障问题,但是不一致问题依然无法解决。

超时机制:如果preCommit消息执行成功,一定时间后还没有收到doCommit消息,则会认为TM挂掉了,自己执行doCommit。

  1. 发送canCommit消息,确认数据库环境正常(其他结点都正常);
  2. 发送preCommit消息,完成sql操作;
  3. 发送doCommit消息,通知所有库提交事务/回滚。
c)TCC(补偿事务)

针对每个操作,都要注册一个与其对应的确认、撤销操作。

  1. Try
    检查资源;try之后执行具体业务逻辑。
  2. Confirm
    确认执行完成 可以是空方法。执行完后整个业务事务完成。
  3. Cancel
    执行撤销操作。执行完后整个业务事务完成。

4>消息队列的事务消息

A调用B时,通过MQ来调用,将调用信息放MQ中。

  1. A执行事务,将message放入MQ1中。
  2. A执行本地事务。
  3. A执行完毕,发送一条commit消息给MQ1,MQ1数据出队并入MQ2中。B监控并消费MQ2数据,如果失败不断重试消费。
    如果A执行失败,发送消息给MQ1,删除所有数据。
声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号