事务开启
通过注解@Transactional可以开启事务。
开启事务后,Spring代理时,会通过TransactionAspectSupport来注入,具体的方法和代码如下:
1
protected Object invokeWithinTransaction(Method method, Class targetClass, final InvocationCallback invocation)
2
throws Throwable {
3
4
// If the transaction attribute is null, the method is non-transactional.
5
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
6
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
7
final String joinpointIdentification = methodIdentification(method, targetClass);
8
9
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
10
// Standard transaction demarcation with getTransaction and commit/rollback calls.
11
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
12
Object retVal = null;
13
try {
14
// This is an around advice: Invoke the next interceptor in the chain.
15
// This will normally result in a target object being invoked.
16
retVal = invocation.proceedWithInvocation();
17
}
18
catch (Throwable ex) {
19
// target invocation exception
20
completeTransactionAfterThrowing(txInfo, ex);
21
throw ex;
22
}
23
finally {
24
cleanupTransactionInfo(txInfo);
25
}
26
commitTransactionAfterReturning(txInfo);
27
return retVal;
28
}
29
30
else {
31
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
32
try {
33
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
34
new TransactionCallback<Object>() {
35
public Object doInTransaction(TransactionStatus status) {
36
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
37
try {
38
return invocation.proceedWithInvocation();
39
}
40
catch (Throwable ex) {
41
if (txAttr.rollbackOn(ex)) {
42
// A RuntimeException: will lead to a rollback.
43
if (ex instanceof RuntimeException) {
44
throw (RuntimeException) ex;
45
}
46
else {
47
throw new ThrowableHolderException(ex);
48
}
49
}
50
else {
51
// A normal return value: will lead to a commit.
52
return new ThrowableHolder(ex);
53
}
54
}
55
finally {
56
cleanupTransactionInfo(txInfo);
57
}
58
}
59
});
60
61
// Check result: It might indicate a Throwable to rethrow.
62
if (result instanceof ThrowableHolder) {
63
throw ((ThrowableHolder) result).getThrowable();
64
}
65
else {
66
return result;
67
}
68
}
69
catch (ThrowableHolderException ex) {
70
throw ex.getCause();
71
}
72
}
73
}
最终通过createTransactionIfNecessary来创建事务。
1
protected TransactionInfo createTransactionIfNecessary(
2
PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
3
4
// If no name specified, apply method identification as transaction name.
5
if (txAttr != null && txAttr.getName() == null) {
6
txAttr = new DelegatingTransactionAttribute(txAttr) {
7
@Override
8
public String getName() {
9
return joinpointIdentification;
10
}
11
};
12
}
13
14
TransactionStatus status = null;
15
if (txAttr != null) {
16
if (tm != null) {
17
status = tm.getTransaction(txAttr);
18
}
19
else {
20
if (logger.isDebugEnabled()) {
21
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
22
"] because no transaction manager has been configured");
23
}
24
}
25
}
26
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
27
}
如果是corbar分库,则会根据每个shards进行开启事务。
corbarclient重写了Spring的事务管理,开启事务通过BestEffortMultiDataSourceTransactionManager来实现。
1
@Override
2
protected void doBegin(Object o, TransactionDefinition transactionDefinition) throws TransactionException {
3
List<TransactionStatus> statusList = (List<TransactionStatus>) o;
4
for (AbstractPlatformTransactionManager transactionManager : transactionManagers) {
5
statusList.add(transactionManager.getTransaction(transactionDefinition));
6
}
7
}
通过获取多个shard来实现事务。
1
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
2
if (status.isNewSynchronization()) {
3
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
4
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
5
(definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) ?
6
definition.getIsolationLevel() : null);
7
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
8
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
9
TransactionSynchronizationManager.initSynchronization();
10
}
11
}
最终TransactionAspectSupport prepareTransactionInfo。
1
protected TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm,
2
TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {
3
4
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
5
if (txAttr != null) {
6
// We need a transaction for this method
7
if (logger.isTraceEnabled()) {
8
logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
9
}
10
// The transaction manager will flag an error if an incompatible tx already exists
11
txInfo.newTransactionStatus(status);
12
}
13
else {
14
// The TransactionInfo.hasTransaction() method will return
15
// false. We created it only to preserve the integrity of
16
// the ThreadLocal stack maintained in this class.
17
if (logger.isTraceEnabled())
18
logger.trace("Don't need to create transaction for [" + joinpointIdentification +
19
"]: This method isn't transactional.");
20
}
21
22
// We always bind the TransactionInfo to the thread, even if we didn't create
23
// a new transaction here. This guarantees that the TransactionInfo stack
24
// will be managed correctly even if no transaction was created by this aspect.
25
txInfo.bindToThread();
26
return txInfo;
27
}
1
private void bindToThread() {
2
// Expose current TransactionStatus, preserving any existing TransactionStatus
3
// for restoration after this transaction is complete.
4
this.oldTransactionInfo = transactionInfoHolder.get();
5
transactionInfoHolder.set(this);
6
}
7
1
/**
2
* Holder to support the {@code currentTransactionStatus()} method,
3
* and to support communication between different cooperating advices
4
* (e.g. before and after advice) if the aspect involves more than a
5
* single method (as will be the case for around advice).
6
*/
7
private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
8
new NamedThreadLocal<TransactionInfo>("Current aspect-driven transaction");
需要注意此处的ThreadLocal,这里绑定了我们需要的事务,方便在当前before和after切面后进行处理。
事务提交
在TransactionAspectSupport invokeWithTransaction方法开启事务并执行完数据库操作后在finally语句中进行对事务的提交clean处理.
1
protected void cleanupTransactionInfo(TransactionInfo txInfo) {
2
if (txInfo != null) {
3
txInfo.restoreThreadLocalStatus();
4
}
5
}
1
private void restoreThreadLocalStatus() {
2
// Use stack to restore old transaction TransactionInfo.
3
// Will be null if none was set.
4
transactionInfoHolder.set(this.oldTransactionInfo);
5
}
最终事务的提交
1
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
2
if (txInfo != null && txInfo.hasTransaction()) {
3
if (logger.isTraceEnabled()) {
4
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
5
}
6
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
7
}
8
}
提交操作
1
public final void commit(TransactionStatus status) throws TransactionException {
2
if (status.isCompleted()) {
3
throw new IllegalTransactionStateException(
4
"Transaction is already completed - do not call commit or rollback more than once per transaction");
5
}
6
7
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
8
if (defStatus.isLocalRollbackOnly()) {
9
if (defStatus.isDebug()) {
10
logger.debug("Transactional code has requested rollback");
11
}
12
processRollback(defStatus);
13
return;
14
}
15
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
16
if (defStatus.isDebug()) {
17
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
18
}
19
processRollback(defStatus);
20
// Throw UnexpectedRollbackException only at outermost transaction boundary
21
// or if explicitly asked to.
22
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
23
throw new UnexpectedRollbackException(
24
"Transaction rolled back because it has been marked as rollback-only");
25
}
26
return;
27
}
28
29
processCommit(defStatus);
30
}
最终的提交交给corbar BestEffortMultiDataSourceTransactionManager
1
protected void doCommit(DefaultTransactionStatus defaultTransactionStatus) throws TransactionException {
2
MultipleCauseException ex = new MultipleCauseException();
3
List<TransactionStatus> statusList = (List<TransactionStatus>) defaultTransactionStatus.getTransaction();
4
for (int i = transactionManagers.size() - 1; i >= 0; i--) {
5
AbstractPlatformTransactionManager transactionManager = transactionManagers.get(i);
6
TransactionStatus status = statusList.get(i);
7
try {
8
transactionManager.commit(status);
9
} catch (TransactionException e) {
10
ex.add(e);
11
}
12
}
13
if (!ex.getCauses().isEmpty())
14
throw new HeuristicCompletionException(HeuristicCompletionException.STATE_UNKNOWN, ex);
15
16
}
事务回滚
事务执行中出现异常,需要回滚事务时,在TransactionAspectSupport中进行了catch操作。
1
try {
2
// This is an around advice: Invoke the next interceptor in the chain.
3
// This will normally result in a target object being invoked.
4
retVal = invocation.proceedWithInvocation();
5
}
6
catch (Throwable ex) {
7
// target invocation exception
8
completeTransactionAfterThrowing(txInfo, ex);
9
throw ex;
10
}
11
finally {
12
cleanupTransactionInfo(txInfo);
13
}
最终还是Corbar进行操作
1
@Override
2
protected void doRollback(DefaultTransactionStatus defaultTransactionStatus) throws TransactionException {
3
MultipleCauseException ex = new MultipleCauseException();
4
List<TransactionStatus> statusList = (List<TransactionStatus>) defaultTransactionStatus.getTransaction();
5
for (int i = transactionManagers.size() - 1; i >= 0; i--) {
6
AbstractPlatformTransactionManager transactionManager = transactionManagers.get(i);
7
TransactionStatus status = statusList.get(i);
8
try {
9
transactionManager.rollback(status);
10
} catch (TransactionException e) {
11
ex.add(e);
12
}
13
}
14
if (!ex.getCauses().isEmpty())
15
throw new UnexpectedRollbackException("one or more error on rolling back the transaction", ex);
16
}
问题1
发现在提交和回滚操作中,如果数据操作突然断网,corbar存在多个数据源的时候,会造成事务不提交的bug。
问题2
在BestEffortMultiDataSourceTransactionManager的 dobegin方法中分别开启事务。
@Override
protected void doBegin(Object o, TransactionDefinition transactionDefinition) throws TransactionException {
List<TransactionStatus> statusList = (List<TransactionStatus>) o;
for (AbstractPlatformTransactionManager transactionManager : transactionManagers) {
statusList.add(transactionManager.getTransaction(transactionDefinition));
}
}
假设存在两个managers, A B,循环开启事务时, A正常开启事务,B开启事务时异常,此时抛出异常,不存在对A事务回滚的地方,会导致A库存在一条未提交事务而导致连接泄露问题。