SpringBoot 使用事务注解 @Transactional 遇到的问题

SpringBoot 使用事务注解 @Transactional 遇到的问题

使用事务注意的地方:
1:开启事务,使用注解 @EnableTransactionManagement 开启事务
2:注意表的引擎:MyISAM 不能使用事务,要用 InnoDB 引擎
3:事务触发的异常默认是运行时异常【具体看源代码】:

/**
	 * Defines zero (0) or more exception {@link Class classes}, which must be
	 * subclasses of {@link Throwable}, indicating which exception types must cause
	 * a transaction rollback.
	 * 

By default, a transaction will be rolling back on {@link RuntimeException} * and {@link Error} but not on checked exceptions (business exceptions). See * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)} * for a detailed explanation. *

This is the preferred way to construct a rollback rule (in contrast to * {@link #rollbackForClassName}), matching the exception class and its subclasses. *

Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}. * @see #rollbackForClassName * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable) */ Class<? extends Throwable>[] rollbackFor() default {};

因此当异常不是运行时异常时,需要指定具体异常,如:sql异常不是运行时异常,那么就需要指定异常,指定方法 rollbackFor :

@Transactional(rollbackFor = {Exception.class})

4:在使用事务的时候还需要注意事务的合理使用,会不会造成内存溢出,如,在开启事务的时候加了非DB写操作,或者 DB操作在循环内,前者:一旦非DB写操作出现比较慢,或者流量比较大,就会出现大事务的问题。由于事务的一直不提交,就会导致数据库连接被占用。这个时候你可能会问,我扩大点数据库连接不就行了吗,100个不行就上1000个,在上篇文章已经讲过数据库连接池大小依然会影响我们数据库的性能,所以,数据库连接并不是想扩多少扩多少。后者:当数据达几十万上百万时,它会一直不提交直到循环结束:

例1:

    @Transactional
    public int createOrder(Order order){
        orderDbStorage.save(order);
        orderItemDbStorage.save(order.getItems());
        sendRpc();
        sendMessage();
        return order.getId();
    }

例2:

    @Transactional
    public void createOrder(Order order){
    	for(int i =0;i<99999;i++){
	        orderDbStorage.save(order);
	        orderItemDbStorage.save(order.getItems());
	        return order.getId();
        }
    }

因此,第一种解决办法是把非DB操作,单独拿出来,第二种同理,把循环内的DB操作封装成一个方法,在该方法上使用事务。
参考链接
5:当存在多数据源时,事务会不生效,因为它不会自动匹配。
解决办法:
第一个数据源的事务管理器配置类 :

import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = "com.frond.dao.datacenter" , sqlSessionTemplateRef = "datacenterSqlSessionTemplate")
public class DatacenterDataSourceConfiguration {

    @Bean(name = "datacenterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.druid.datacenter")
    public DataSource dataSource(){
        return new DruidDataSource();
    }

    @Bean(name = "datacenterTransactionManager")
    public DataSourceTransactionManager transactionManager(@Qualifier("datacenterDataSource") DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

}

第二个数据源的事务管理器配置类 :

import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = "com.frond.dao.approval" , sqlSessionTemplateRef = "approvalSqlSessionTemplate")
public class ApprovalDataSourceConfiguration {

    @Bean(name = "approvalDataSource")
	@Primary
    @ConfigurationProperties(prefix = "spring.datasource.druid.approval")
    public DataSource dataSource(){
        return new DruidDataSource();
    }

    @Bean(name = "approvalTransactionManager")
	@Primary
    public DataSourceTransactionManager transactionManager(@Qualifier("approvalDataSource") DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }

}

注入事务管理器的bean时,默认是根据类型去注入,如果该类型有多个Bean,不通过bean的名字去注入,默认是会注入被@Primary标识的bean的,所以在这个项目中,当在业务层方法添加@Transactional注解时,默认是调用了approvalTransactionManager这个bean,但是如果方法中调用的是第一个数据源对应的Dao层方法时,直接添加@Transactional是不能实现事务管理的

三、解决办法
需要在使用@Transactional注解时指定使用的事务管理器的bean的名字

@Transactional(rollbackFor = Exception.class, transactionManager = "datacenterTransactionManager")

参考链接

你可能感兴趣的:(JAVA,事务,java)