配置
springboot中事务配置类:TransactionAutoConfiguration
spring事务调用入口:
org.springframework.transaction.interceptor.TransactionInterceptor#invoke
=====>> org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
我们一般使用:DataSourceTransactionManager
介绍一下TransactionSynchronizationManager:主要记录线程当前正在运行的事务,记录是否有事务正在进行中(即开启事务状态)、当前事务名称、当前事务是否只读、当前事务的隔离级别、当前线程绑定了哪些数据源(这个数据源对应的正在运行的事务,可以参考:org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin)、注册一些钩子方法用于事务提交前后提交后执行
org.springframework.data.repository.core.support.TransactionalRepositoryProxyPostProcessor.AbstractFallbackTransactionAttributeSource#getTransactionAttribute 获取@Transactional注解配置的相关事务属性,进行设置,控制事务流转
TransactionInterceptor初始化
springboot中@EnableTransactionManagement 即可开启事务基础配置。
该注解通过TransactionManagementConfigurationSelector将AutoProxyRegistrar和ProxyTransactionManagementConfiguration的bean实例注册到bean容器。
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
/**
* {@inheritDoc}
* @return {@link ProxyTransactionManagementConfiguration} or
* {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and
* {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively
*/
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
}
其中ProxyTransactionManagementConfiguration里面初始化了:BeanFactoryTransactionAttributeSourceAdvisor、TransactionAttributeSource、TransactionInterceptor
开启事务
(这里有一个坑:手动管理事务的关键是con.setAutoCommit(false),JDBC事务默认是开启的,并且是自动提交,并不是什么beginTransaction方法,看源码的时候半天没找到什么beginTransaction,一查才知道):
org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin
/**
* This implementation sets the isolation level but ignores the timeout.
*/
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (txObject.getConnectionHolder() == null ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = this.dataSource.getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
//设置数据库隔离级别
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
//手动控制事务的关键是con.setAutoCommit(false),JDBC事务默认是开启的,并且是自动提交
//自动事务提交给关掉,手动开启事务
con.setAutoCommit(false);
}
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, this.dataSource);
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
上面方法走完,代表事务已经开启
一直困惑我的,spring是如何动态修改数据的隔离级别的,源码如下:org.springframework.jdbc.datasource.DataSourceUtils#prepareConnectionForTransaction
/**
* Prepare the given Connection with the given transaction semantics.
* @param con the Connection to prepare
* @param definition the transaction definition to apply
* @return the previous isolation level, if any
* @throws SQLException if thrown by JDBC methods
* @see #resetConnectionAfterTransaction
*/
public static Integer prepareConnectionForTransaction(Connection con, TransactionDefinition definition)
throws SQLException {
Assert.notNull(con, "No Connection specified");
// Set read-only flag.
if (definition != null && definition.isReadOnly()) {
try {
if (logger.isDebugEnabled()) {
logger.debug("Setting JDBC Connection [" + con + "] read-only");
}
con.setReadOnly(true);
}
catch (SQLException ex) {
Throwable exToCheck = ex;
while (exToCheck != null) {
if (exToCheck.getClass().getSimpleName().contains("Timeout")) {
// Assume it's a connection timeout that would otherwise get lost: e.g. from JDBC 4.0
throw ex;
}
exToCheck = exToCheck.getCause();
}
// "read-only not supported" SQLException -> ignore, it's just a hint anyway
logger.debug("Could not set JDBC Connection read-only", ex);
}
catch (RuntimeException ex) {
Throwable exToCheck = ex;
while (exToCheck != null) {
if (exToCheck.getClass().getSimpleName().contains("Timeout")) {
// Assume it's a connection timeout that would otherwise get lost: e.g. from Hibernate
throw ex;
}
exToCheck = exToCheck.getCause();
}
// "read-only not supported" UnsupportedOperationException -> ignore, it's just a hint anyway
logger.debug("Could not set JDBC Connection read-only", ex);
}
}
// Apply specific isolation level, if any.
//通过definition,设置数据库的隔离级别
Integer previousIsolationLevel = null;
if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
if (logger.isDebugEnabled()) {
logger.debug("Changing isolation level of JDBC Connection [" + con + "] to " +
definition.getIsolationLevel());
}
int currentIsolation = con.getTransactionIsolation();
if (currentIsolation != definition.getIsolationLevel()) {
previousIsolationLevel = currentIsolation;
//就在这里设置得
con.setTransactionIsolation(definition.getIsolationLevel());
}
}
return previousIsolationLevel;
}
提交事务
源码如下:org.springframework.transaction.support.AbstractPlatformTransactionManager#commit
最终在processCommit调用doCommit方法提交事务,前后都有一些钩子方法
**
* This implementation of commit handles participating in existing
* transactions and programmatic rollback requests.
* Delegates to {@code isRollbackOnly}, {@code doCommit}
* and {@code rollback}.
* @see org.springframework.transaction.TransactionStatus#isRollbackOnly()
* @see #doCommit
* @see #rollback
*/
@Override
public final void commit(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus);
return;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus);
// Throw UnexpectedRollbackException only at outermost transaction boundary
// or if explicitly asked to.
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
return;
}
processCommit(defStatus);
}
/**
* 这里有一个注意点: 当前事务必须是一个isNewTransaction为true时,才会去提交事务
* Process an actual commit.
* Rollback-only flags have already been checked and applied.
* @param status object representing the transaction
* @throws TransactionException in case of commit failure
*/
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
//以下三个钩子方法
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
boolean globalRollbackOnly = false;
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
status.releaseHeldSavepoint();
}
//这里会提交事务
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
doCommit(status);
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (globalRollbackOnly) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
}
catch (Error err) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, err);
throw err;
}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
//钩子方法
triggerAfterCommit(status);
}
finally {
//钩子方法
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
cleanupAfterCompletion(status);
}
}
手动控制spring事务
TransactionTemplate是一个spring事务辅助类,可以手动将一个方法放入事务,比如在工作中就可以将多个单独标有事务注解的方法写在一个代码块中,不用新写个service,再标上注解,这个时候就可以用这个手动加入事务。
该类的主要方法:
org.springframework.transaction.support.TransactionTemplate#execute
TransactionTemplate使用参考
public <T> T execute(final CommandConfig config, final Command<T> command) {
LOGGER.debug("Running command with propagation {}", config.getTransactionPropagation());
//这里直接new了一个TransactionTemplate,之所以new,是想每个调用进来的方法,有自己的ropagationBehavior
//实际上TransactionTemplate 默认就会初始化一个bean在容器里,但是用这个得话,所有方法的传播机制就是PROPAGATION_REQUIRED
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.setPropagationBehavior(getPropagation(config));
T result = transactionTemplate.execute(new TransactionCallback<T>() {
public T doInTransaction(TransactionStatus status) {
return next.execute(config, command);
}
});
return result;
}
private int getPropagation(CommandConfig config) {
switch (config.getTransactionPropagation()) {
case NOT_SUPPORTED:
return TransactionTemplate.PROPAGATION_NOT_SUPPORTED;
case REQUIRED:
return TransactionTemplate.PROPAGATION_REQUIRED;
case REQUIRES_NEW:
return TransactionTemplate.PROPAGATION_REQUIRES_NEW;
default:
throw new ActivitiIllegalArgumentException("Unsupported transaction propagation: " + config.getTransactionPropagation());
}
}