@Transactional注解管理事务和手动提交事务

1、@Transacational注解

(1)注解里面的属性:

  • propagation: 用于设置事务传播属性,该属性类型为Propagation枚举,默认是Propagation.REQUIRED
  • issolation: 用于设置事务的隔离级别,该属性类型为Isolation枚举,默认是Isolation.DEFAULT
  • readOnly: 用于设置该方法对数据库的操作是否是只读,该属性为boolean,默认为false
  • timeout: 用于设置本操作与数据库连接的超时时限,单位为秒,类型int,默认为-1,即没有时限
  • rollbackFor 指定需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
  • rollbackForClassName:指定需要回滚的异常类类名。类型为 String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
  • noRollbackFor:指定不需要回滚的异常类。类型为 Class[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。
  • noRollbackForClassName: 指定不需要回滚的异常类类名。类型为 - String[],默认值为空数组。当然,若只有一个异常类时,可以不使用数组。

需要注意的是,@Transactional 若用在方法上,只能用于 public 方法上。对于其他非 public 方法,如果加上了注解 @Transactional,虽然 Spring 不会报错,但不会将指定事务织入到该方法中。因为 Spring 会忽略掉所有非 public 方法上的 @Transaction 注解。
若 @Transaction 注解在类上,则表示该类上所有的方法均将在执行时织入事务。

(2)@Transactional注解失效情况

  • 没有被 Spring 容器管理
    如果此时把 @Service 注解注释掉,这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 管理了,事务自然就失效了。
// @Service
public class OrderServiceImpl implements OrderService {
    @Transactional
    public void updateOrder(Order order) {
        // update order
    }
}
  • 用在非public修饰的方法
    @Transactional是基于动态代理的,Spring的代理工厂在启动时会扫描所有的类和方法,并检查方法的修饰符是否为public,非public时不会获取@Transactional的属性信息,这时@Transactional的动态代理对象为空。
  • 多线程调用
    Spring实现事务的原理是通过ThreadLocal把数据库连接绑定到当前线程中,同一个事务中数据库操作使用同一个jdbc connection,新开启的线程获取不到当前jdbc connection
  • 自身调用自身方法
@Service
public class OrderServiceImpl implements OrderService {

    @Transactional
    public void update(Order order) {
        updateOrder(order);
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateOrder(Order order) {
        // update order
    }   
}

上面的不生效
解决的方案:
[1]注入BeanFactory容器,使用容器获取代理类,然后使用代理类调用事务方法
image
[2]使用@Autowired注解或者@Resource注解注入自己,然后使用注入的代理类去调用事务方法
image
[3]类上面添加注解@EnableAspectJAutoProxy(exposeProxy = true),然后在类中获取当前类的代理类,使用代理类调用事务方法。如果不添加注解的话,可能会出现错误
image

  • 异常被catch“吃了”导致@Transactional失效

  • @Transactional 注解属性 rollbackFor 异常类型设置错误,数据库不支持事务

2、手动启用事务

(1)使用手动事务的场景
1、异步处理业务时,此时业务的事务已经脱离正常的aop机制了,所以需要手动提交事务,来保持业务中多个事务的一致性。
2、在线程中我们也有可能需要事务,这个事务可以使用手动事务。
(2)使用

    @Autowired
    PlatformTransactionManager platformTransactionManager;
    @Test
    void testTransactional(){
        // 获取事务
        DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
        defaultTransactionDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus transaction = platformTransactionManager.getTransaction(defaultTransactionDefinition);

        // 手动事务
        User user = new User();
        user.setId(13L);
        user.setOpenId("123456");
        user.setUsername("ccgg");
        userRepository.save(user);
        // 事务提交
        platformTransactionManager.commit(transaction);
        try{
            int a = 1/0;
        }catch (Exception e){
            // 事务回滚
            platformTransactionManager.rollback(transaction);
        }
    }

注意:这里引入 PlatformTransactionManager 而不是DataSourceTransactionManager。springboot根据数据库jpa配置自动实现PlatformTransactionManager。数据库组件如果是spring-boot-starter-jdbc,框架会默认注入DataSourceTransactionManager

参考:https://blog.csdn.net/weixin_37862824/article/details/124333511

3、事务管理器

项目中数据库组件如果是spring-boot-starter-jdbc,框架会默认注入DataSourceTransactionManager
如果你添加的是spring-boot-starter-data-jpa依赖,框架会默认注入JpaTransactionManager实例。

JPA的事务管理器和JDBC的事务管理器都实现了PlatformTransactionManager接口

测试项目中使用的是什么类型的事务

//参数platformTransactionManager spring会默认帮我们住进来
@Bean
public Object testBean(PlatformTransactionManager platformTransactionManager){
    System.out.println(platformTransactionManager.getClass().getName());
    return new Object();
}

(1)自定义事务类型
设置Spring的事务类型是DataSourceTransactionManager

@Bean
public PlatformTransactionManager txManager(DataSource dataSource) {
    return new DataSourceTransactionManager(dataSource);//其中dataSource框架会自动为我们注入
}

在Spring容器中,我们手工注解@Bean 将被优先加载,框架不会重新实例化其他的PlatformTransactionManager实现类

@Configuration
public class Profiledemo implements TransactionManagementConfigurer {

	//注入事务管理器2
    @Resource(name="txManager2")
    private PlatformTransactionManager txManager2;

    //创建事务管理器2
    @Bean(name = "txManager2")
    public PlatformTransactionManager txManager2(EntityManagerFactory factory) {
        return new JpaTransactionManager(factory);
    }

    //创建事务管理器1
    @Bean(name = "txManager1")
    public PlatformTransactionManager txManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    //实现接口TransactionManagementConfigurer方法,其返回值代表在拥有多个事务管理器的情况下默认使用的事务管理器
    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return txManager2;
    }
}

Service使用中指定事务

public class Service{

	//使用value具体指定使用哪个事务管理器
	@Transactional(value="txManager1")
	@Override
	public void xxx() {
	    
	}

	// 在存在多个事务管理器的情况下,默认使用上面annotationDrivenTransactionManager()方法返回的事务管理器
	@Transactional
	public void xxx() {
	    
	}
}

注意:多个数据多个事务管理器,如果没有重写annotationDrivenTransactionManager方法,并且Transactional没有指定value,启动是会报错的

posted @ 2024-10-29 18:22  spiderMan1-1  阅读(1)  评论(0编辑  收藏  举报