spring事务管理及失效场景

事务的隔离级别和事务传播参考:https://blog.csdn.net/sumengnan/article/details/112620349

一、编程式事务

需要手动编程来实现事务,粒度可以控制到代码块级别

(1)原始事务管理方式

关键的三个接口:TransactionDefinition、PlatformTransactionManagerTransactionStatus

1、TransactionDefinition(事务定义接口)

默认实现类是DefaultTransactionDefinition。用于定义一个事务的静态属性。比如事务的传播行为、超时时间等。可以自定义类实现Transactiondefinition接口。

2、PlatformTransactionManager(事务管理器接口)

实现类如下:

根据不同的ORM框架,主要分成如下几类:

  • DateSourceTransactionManager:适用于JDBC和iBatis持久化操作。
  • HibernateTransactionManager:适用于Hibernate框架
  • JpaTransactionManager:适用于使用JPA持久化操作
  • JtaTransactionManager:适用于分布式事务操作
  • 其他

3、TransactionStatus:(事务状态接口)

TransactionStatus继承了SavepointManager接口,SavepointManager是对事务中上述保存点功能的封装。

用法:通过PlatformTransactionManager.getTransaction(...)方法返回TransactionStatus对象。提供了一个简单的控制事务执行和查询事务状态的方法。例如是否是一个新的事务、是否已被标记为回滚

代码示例:

	@Autowired
	private PlatformTransactionManager dataSourceTransactionManager;				
//-------------------------------------------------------分割,上面是注入,下面是关键代码
            TransactionStatus transactionStatus=null;
				try {
					transactionStatus = dataSourceTransactionManager.getTransaction(new DefaultTransactionDefinition());//手动开启事务
					//此处进行业务操作
					dataSourceTransactionManager.commit(transactionStatus);//提交
				} catch (Exception e) {
					if(StringMoreUtils.checkValNotNull(status)){
						dataSourceTransactionManager.rollback(transactionStatus);//回滚
					}
					e.printStackTrace();
				}	

(2)TransactionTemplate模版模式方式(推荐)

实际上是对上面原始事务管理方式的一个封装,继承关系如下:



TransactionTemplate主要依赖TransactionTemplate.execute( ... ),执行事务管理的时候,传入的参数有两种选择:
1、TransactionCallback(有返回值
2、TransactionCallbackWithoutResult(无返回值

代码示例:

//有返回值
      transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus transactionStatus) {
                try {
                    //.......   业务代码
                    return new Object();//有返回值
                } catch (Exception e) {
                    //回滚
                    transactionStatus.setRollbackOnly();
                    return null;
                }
            }
        });
//无返回值
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                try {
                    // ....  业务代码
                } catch (Exception e){
                    //回滚
                    transactionStatus.setRollbackOnly();
                }

            }
        });

二、声明式事务

通过aop方式来实现,粒度只能控制到方法级别

(1)通过xml方式配置,基于tx和aop名字空间

    <!-- 事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 配置 transactionTemplate -->
    <bean id="transactionTemplate"
          class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager">
            <ref bean="transactionManager"/>
        </property>
    </bean>
     < tx:advice  id ="txAdvice"  transaction-manager ="transactionManager" >
         < tx:attributes >
             < tx:method  name ="*"  propagation ="REQUIRED"   />
         </ tx:attributes >
     </ tx:advice >
    
     < aop:config >
         < aop:pointcut  id ="interceptorPointCuts"
            expression ="execution(* com.bluesky.spring.dao.*.*(..))"   />
         < aop:advisor  advice-ref ="txAdvice"
            pointcut-ref ="interceptorPointCuts"   />         
     </ aop:config >   

(2)通过@Transaction注解的方式来声明事务(推荐)

    @Transactional
    public List<SysUser> findByLogin(String username, String password) {
        return this.sysUserRepository.findByLogin(username, password);
    }

三、@Transaction注解的原理:

例如:我调用service的findByLogin这个方法

1、在项目启动时,spring会创建JpaTransactionManager实例( springboot-autoconfiguration包下)

还有事务拦截器(动态代理层面的,可不是springmvc的那个)

2、同时初始化spring ioc容器时会为我的service类创建动态代理类,通过下面的类

3、当service类的方法被调用时,会先调用代理类JdkDynamicAopProxy的invoke方法

反射调用ReflectiveMethodInvocation的proceed方法

再调用事务拦截器的invoke方法

再调用TransactionInterceptor的抽象父类的TransactionAspectSupport的invokeWithinTransaction方法

还是invokeWithinTransaction方法,里面看到了熟悉的编程式事务中的execute方法,还有JapTransactionManager、TransactionCallback等类

说明声名式事务底层也是使用的编程式事务

截图中:invocation.proceedWithInvocation()方法,其实就是调用service的findByLogin这个方法

至此事务流程完毕

四、@Transaction注解失效的场景:

提示:spring建议不要在接口或接口方法山使用@Transactional注解,因为只有基于接口的代理才会生效。

(1)不支持事务场景:

  1. 数据库MyIsam引擎不支持事务。mysql5.5.5之后默认是InnoDB引擎,才支持事务。
  2. @Transactional(propagation = Propagation.NOT_SUPPORTED)以非事务方式运行。当然就不会有事务。

(2)配置层面不注意:

  1. 启动类没加@EnableTransactionManagement注解。(springboot-autoconfigure的DataSourceTransactionManagerAutoConfiguration类中添加了注解)
  2. 事务类没有加@Service注解。也就是没有被spring管理(没有被动态代理)。
  3. 数据源没有配置事务管理器。(PlatformTransactionManager接口的实现类,如JpaTransactionManager,datasourceTransactionManager)

(3)编码层面不注意:

  1. 只能在public方法上使用注解。如果要用在非 public 方法上,可以开启 AspectJ 代理模式
  2. 异常没有上抛。ry-catch捕获住了,应该上抛给代理类。上层代理类捕获到异常后进行回滚。
  3. @Transactional(rollbackFor = Exception.class)异常类型错误。注意:默认回滚RuntimeException异常
  4. 自身调用问题。只有从外部调用service事务方法,才会走动态代理,才可以使用事务。调用本类的其他方法不会走动态代理

四、@Transaction注解读写事务和只读事务:

默认是读写事务,加上@Transactional(readOnly = true)后变成只读事务。

(1)只读事务有什么作用?

  1. orm框架提升性能。例如:hibernate省去了检测和同步对象持久化的更新(不执行flush操作)。
  2. 不允许DML语句,否则报错。
  3. 适合报表或统计。可以在同一事务中查询多次查询,当其他事务同时修改数据,也不会影响到查询结果

(2)hibernate框架是如何对只读事务做优化的?

HibernateJpaDialect类是关键,它的beginTransaction方法是开始事务操作

(3)mysql如何开启只读事务?

set transaction read only;

开启只读事务此时间点,之后所有的查询都是此时间点的数据(其他事务所有的提交都不会改变查询结果),直到commit或rollBack或执行ddl语句。

set transaction read only;只能是只读事务的第一条语句,并且只能出现一次。之后的语句只能出现select、select into、open、fetch、close、lock table、commit、rollback等语句,

其他的都不能出现例如:update、或select * from table for update(添加排他锁)等。

猜你喜欢

转载自blog.csdn.net/sumengnan/article/details/112639564