有彩蛋哦!!!(或者公众号内点击网赚获取彩蛋)
首先已经按照官方推荐方式将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());
}
}