SpringBoot@Transactional对Activiti6不起效

目录

  • 推荐公众号
  • 问题场景
  • 正文
  • 解决

推荐公众号

有彩蛋哦!!!(或者公众号内点击网赚获取彩蛋)
程序员探索之路

问题场景

首先已经按照官方推荐方式将Activiti与Spring事务整合在了一起,今天调整表结构之后开启流程抛出异常,业务代码回滚了;但是Activiti的数据没有回滚,回想起来这次做的流程比较简单没有出现异常,Acitiviti配置有之前做个流程引擎的搭建的,自己没有测过事务这块。

正文

刚开始是怀疑Acitivi事务并没有和Spring整合在一起,看了下官方配置没有毛病。然后debug开启流程源码,发现是直接提交到数据库中的,我看了下配置文件配置的auto-commit的确为false,百思不得其解。在翻日志的时候发现一个
AutoCommit 模式设置为“true”时,无法调用回滚操作
这个和配置文件中的不一致,悄悄翻看HikariDataSource源码(我公司用HikariDataSource号称性能最好的数据库连接池)。

调用的构造器
public HikariDataSource()
   {
      super();
      fastPathPool = null;
   }
/**
	默认构造器
    * Default constructor
    */
   public HikariConfig()
   {
   	 
      dataSourceProperties = new Properties();
      healthCheckProperties = new Properties();

	  默认值
      minIdle = -1;
      maxPoolSize = -1;
      maxLifetime = MAX_LIFETIME;
      connectionTimeout = CONNECTION_TIMEOUT;
      validationTimeout = VALIDATION_TIMEOUT;
      idleTimeout = IDLE_TIMEOUT;
      initializationFailTimeout = 1;
      设置为了true
      isAutoCommit = true;

	  获取JVM系统变量
      String systemProp = System.getProperty("hikaricp.configurationFile");
      if (systemProp != null) {
         loadProperties(systemProp);
      }
   }
   到这里只知道默认isAutoCommit为true,我们都知道数据库连接池维护的是一个线程池,到目前为止没有看到线程池信息,线程池的创建时在获取连接时进行的

 public Connection getConnection() throws SQLException
   {
      if (isClosed()) {
         throw new SQLException("HikariDataSource " + this + " has been closed.");
      }

      if (fastPathPool != null) {
         return fastPathPool.getConnection();
      }

      // See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_Java
      HikariPool result = pool;
      if (result == null) {
         synchronized (this) {
            result = pool;
            如果为null创建连接池
            if (result == null) {
               validate();
               LOGGER.info("{} - Starting...", getPoolName());
               try {
                  pool = result = new HikariPool(this);
                  this.seal();
               }
               catch (PoolInitializationException pie) {
                  if (pie.getCause() instanceof SQLException) {
                     throw (SQLException) pie.getCause();
                  }
                  else {
                     throw pie;
                  }
               }
               LOGGER.info("{} - Start completed.", getPoolName());
            }
         }
      }

      return result.getConnection();
   }
创建连接池
public HikariPool(final HikariConfig config)
   {
   	设置连接池信息
      super(config);

      this.connectionBag = new ConcurrentBag<>(this);
      this.suspendResumeLock = config.isAllowPoolSuspension() ? new SuspendResumeLock() : SuspendResumeLock.FAUX_LOCK;

      this.houseKeepingExecutorService = initializeHouseKeepingExecutorService();

      checkFailFast();

      if (config.getMetricsTrackerFactory() != null) {
         setMetricsTrackerFactory(config.getMetricsTrackerFactory());
      }
      else {
         setMetricRegistry(config.getMetricRegistry());
      }

      setHealthCheckRegistry(config.getHealthCheckRegistry());

      registerMBeans(this);

      ThreadFactory threadFactory = config.getThreadFactory();

      LinkedBlockingQueue<Runnable> addConnectionQueue = new LinkedBlockingQueue<>(config.getMaximumPoolSize());
      this.addConnectionQueue = unmodifiableCollection(addConnectionQueue);
      this.addConnectionExecutor = createThreadPoolExecutor(addConnectionQueue, poolName + " connection adder", threadFactory, new ThreadPoolExecutor.DiscardPolicy());
      this.closeConnectionExecutor = createThreadPoolExecutor(config.getMaximumPoolSize(), poolName + " connection closer", threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());

      this.leakTaskFactory = new ProxyLeakTaskFactory(config.getLeakDetectionThreshold(), houseKeepingExecutorService);

      this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(new HouseKeeper(), 100L, HOUSEKEEPING_PERIOD_MS, MILLISECONDS);

      if (Boolean.getBoolean("com.zaxxer.hikari.blockUntilFilled") && config.getInitializationFailTimeout() > 1) {
         final long startTime = currentTime();
         while (elapsedMillis(startTime) < config.getInitializationFailTimeout() && getTotalConnections() < config.getMinimumIdle()) {
            quietlySleep(MILLISECONDS.toMillis(100));
         }
      }
   }
PoolBase(final HikariConfig config)
   {
      this.config = config;

      this.networkTimeout = UNINITIALIZED;
      this.catalog = config.getCatalog();
      this.schema = config.getSchema();
      this.isReadOnly = config.isReadOnly();
      可以看到还是config中的值
      this.isAutoCommit = config.isAutoCommit();
      this.transactionIsolation = UtilityElf.getTransactionIsolation(config.getTransactionIsolation());

      this.isQueryTimeoutSupported = UNINITIALIZED;
      this.isNetworkTimeoutSupported = UNINITIALIZED;
      this.isUseJdbc4Validation = config.getConnectionTestQuery() == null;
      this.isIsolateInternalQueries = config.isIsolateInternalQueries();

      this.poolName = config.getPoolName();
      this.connectionTimeout = config.getConnectionTimeout();
      this.validationTimeout = config.getValidationTimeout();
      this.lastConnectionFailure = new AtomicReference<>();

      initializeDataSource();
   }
   到这里发现配置文件中auto-commit配置了没有用,因为它根本就不读,那么jdbcurl和
   username,password怎么读的呢,是初始化database时读的,由于现在我们项目有两个数
   据源,所以重写了平时业务需要的DataSource和JdbcTemplete
   @Bean
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource(DataSourceProperties properties) {
        return DataSourceBuilder.create(properties.getClassLoader())
            .type(HikariDataSource.class)
            .url(properties.determineUrl())
            .username(properties.determineUsername())
            .password(properties.determinePassword())
            .build();
    }

    @Bean
    @Primary
    public JdbcTemplate jdbcTemplate(DataSourceProperties properties) {
        return new JdbcTemplate(dataSource(properties));
    }
**为什么auto-commit默认为true**
问了大牛之后这样说:auto-commit设置为true是为了保护那些不受事务管理的sql,如果有需要将获取到的连接设置为false,执行之后设置为true。Spring的事物管理就是这样设置的

解决

public class SpringManagedTransaction implements Transaction {

    private static final Logger LOGGER = LoggerFactory.getLogger(SpringManagedTransaction.class);

    private final DataSource dataSource;

    private Connection connection;

    private boolean isConnectionTransactional;

    private boolean autoCommit;

    public SpringManagedTransaction(DataSource dataSource) {
        notNull(dataSource, "No DataSource specified");
        this.dataSource = dataSource;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Connection getConnection() throws SQLException {
        if (this.connection == null) {
            openConnection();
        }
        return this.connection;
    }

    /**
     * Gets a connection from Spring transaction manager and discovers if this {@code Transaction} should manage
     * connection or let it to Spring.
     * 

* It also reads autocommit setting because when using Spring Transaction MyBatis thinks that autocommit is always * false and will always call commit/rollback so we need to no-op that calls. */ private void openConnection() throws SQLException { this.connection = DataSourceUtils.getConnection(this.dataSource); this.connection.setAutoCommit(false); // this.autoCommit = false; this.isConnectionTransactional = DataSourceUtils.isConnectionTransactional(this.connection, this.dataSource); LOGGER.debug( "JDBC Connection [" + this.connection + "] 将" + (this.isConnectionTransactional ? " " : " not ") + "会被Spring管理"); } /** * {@inheritDoc} */ @Override public void commit() throws SQLException { if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) { LOGGER.debug( "提交jdbc连接 [" + this.connection + "]"); this.connection.commit(); } } /** * {@inheritDoc} */ @Override public void rollback() throws SQLException { if (this.connection != null && !this.isConnectionTransactional && !this.autoCommit) { LOGGER.debug("回滚jdbc连接 [" + this.connection + "]"); this.connection.rollback(); } } /** * {@inheritDoc} */ @Override public void close() { LOGGER.debug("关闭jdbc连接 [" + this.connection + "]"); this.connection.setAutoCommit(true); DataSourceUtils.releaseConnection(this.connection, this.dataSource); } /** * {@inheritDoc} */ @Override public Integer getTimeout() { ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); if (holder != null && holder.hasTimeout()) { return holder.getTimeToLiveInSeconds(); } return null; } } public class SpringManagedTransactionFactory implements TransactionFactory { /** * {@inheritDoc} */ @Override public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) { return new SpringManagedTransaction(dataSource); } /** * {@inheritDoc} */ @Override public Transaction newTransaction(Connection conn) { throw new UnsupportedOperationException("New Spring transactions require a DataSource"); } /** * {@inheritDoc} */ @Override public void setProperties(Properties props) { // not needed in this version } } @Configuration public class ActivitiConfig extends AbstractProcessEngineAutoConfiguration implements ProcessEngineConfigurationConfigurer { @Autowired private ActivitiUserTaskListener activitiUserTaskListener; @Override public void configure(SpringProcessEngineConfiguration processEngineConfiguration) { Map<String, List<ActivitiEventListener>> eventListeners = new HashMap<>(); eventListeners.put(ActivitiEventType.TASK_CREATED + "," + ActivitiEventType.TASK_COMPLETED, Arrays.asList(activitiUserTaskListener)); processEngineConfiguration.setTypedEventListeners(eventListeners); processEngineConfiguration.setTransactionFactory(new SpringManagedTransactionFactory()); } }

你可能感兴趣的:(activiti)