盘点 JPA 基于 Spring 的事务管理

总文档 :文章目录

Github : github.com/black-ant

一 . 前言

这一篇讲事务管理相关的流程 , 以 SpringDataJPA 为例.

文章的目的 :

  • 梳理 Spring Transaction 的主流程
  • 说明 Spring Transaction 的核心原理
  • 流程中的参数传递

一句话原理 :

  • 通过 SQL START TRANSACTION; + COMMIT; 实现数据库级的事务管理
  • 通过 代理的方式进行整体的管控 , 通过代理类开启 START TRANSACTION
  • 通过 Mysql rollback 事务回滚

主流程 :

  • TransactionManager
  • JpaTransactionManager
  • PlatformTransactionManager

二 . 事务处理流程

事务管理的核心是对象 TransactionManager , 我们来看一下他的家族体系 :

PlatformTransactionManager.png

大概可以看到 , 主要的实现类有 :

1
2
3
4
5
6
7
8
java复制代码C- DataSourceTransactionManager
C- JdoTransactionManager
C- JpaTransactionManager
C- HibernateTransactionManager
C- JtaTransactionManager
C- OC4JjtaTransactionManager
C- WebSphereUowTransactionManager
C- WebLogicJtaTransactionManager

2.1 事务的拦截入口

事务的起点是通过 Interceptor 进行拦截的 , 其代理方式也是通过 AOP 实现的 ,其入口类为 CglibAopProxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java复制代码C01- TransactionInterceptor
E- TransactionAspectSupport -> PS:001
M01_01- invoke(MethodInvocation invocation)


// M01_01
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// 向TransactionAttributeSource传递目标类和方法
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

// 此处调用 invokeWithinTransaction 正式处理
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}

PS:001 TransactionAspectSupport 的作用

类的作用 : 事务基础类 , 子类负责以正确的顺序调用该类中的方法

特点 : 基于 策略模式 设计

2.2 流程的拦截

  • Step 1 : 属性准备
    • TransactionAttributeSource : TransactionInterceptor用于元数据检索的策略接口
    • TransactionAttribute : 该接口将rollbackOn规范添加到TransactionDefinition。
    • PlatformTransactionManager : 事务管理平台
    • joinpointIdentification
  • Step 2 : 分为 2 种类型处理
    • 存在用于在事务中执行给定回调的方法
    • 不存在回调方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
java复制代码C02- TransactionAspectSupport
M02_01- invokeWithinTransaction(Method method, Class<?> targetClass, InvocationCallback invocation)
P- method : 当前原始方法
P- targetClass : 当前原始类
P- invocation : 代理对象
// 1- 属性准备
- getTransactionAttributeSource() : 获取 TransactionAttributeSource
- tas.getTransactionAttribute(method, targetClass) : 获取 TransactionAttribute
- determineTransactionManager(txAttr) : 获取 PlatformTransactionManager
- methodIdentification(method, targetClass, txAttr) :
// 类型 A : -------------
A2- 主调操作
- createTransactionIfNecessary(tm, txAttr, joinpointIdentification)
?- 如果需要,根据给定的TransactionAttribute创建一个事务 -> M02_02
- invocation.proceedWithInvocation() : 核心接口 -> PS:M02_01_01
?- 进行目标调用的简单回调接口 , 具体的拦截器/方面由它们的调用机制决定
A3- catch 操作
- completeTransactionAfterThrowing(txInfo, ex)
A4- finally 操作
- cleanupTransactionInfo(txInfo) -> PS:M02_04_01
A5- 提交事务
- commitTransactionAfterReturning(txInfo) -> PS:M02_05_01
// 类型 B :------------------------
B2- CallbackPreferringPlatformTransactionManager.execute
B3- prepareTransactionInfo(tm, txAttr, joinpointIdentification, status) : 构建 TransactionInfo
B4- invocation.proceedWithInvocation()
M02_02- createTransactionIfNecessary(PlatformTransactionManager tm,TransactionAttribute txAttr, String joinpointIdentification)
1- tm.getTransaction(txAttr) : 获取 TransactionStatus -> M03_01
2- prepareTransactionInfo(tm, txAttr, joinpointIdentification, status)
?- 处理
M02_03- prepareTransactionInfo
1- new TransactionInfo(tm, txAttr, joinpointIdentification) : 构建一个 TransactionInfo
2- txInfo.newTransactionStatus(status) : 如果不兼容的tx已经存在,事务管理器将标记一个错误
3- txInfo.bindToThread() : 即使没有在这里创建一个新的事务 , 也会将TransactionInfo绑定到线程
?- 这保证了即使这个方面没有创建任何事务,TransactionInfo堆栈也将被正确管理
M02_04- cleanupTransactionInfo : 重置TransactionInfo ThreadLocal
- txInfo.restoreThreadLocalStatus() -> PS:M02_04_01

// PS:M02_01_01 核心处理
// 该方法会执行最终的 state 方法 , 主要流程为

C- RepositoryComposition # invoke(Method method, Object... args)
C- RepositoryFactorySupport # doInvoke
C- SimpleJpaRepository # save
C- Loader # loadEntity
C- Loader # doQuery
C- ConnectionImpl # prepareStatement


// PS:M02_04_01 的作用
TODO : 这个属性的作用
protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
if (txInfo != null) {
txInfo.restoreThreadLocalStatus();
}
}

参数详情

这里看一看相关的参数详情

image.png

M02_01 源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
java复制代码    protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {

// Step 1 : 属性准备
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}

else {
final ThrowableHolder throwableHolder = new ThrowableHolder();

// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
});

// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
}
}

3.3 流程的处理

如何调用到该方法 :

  • TransactionAspectSupport # invokeWithinTransaction
  • TransactionAspectSupport # createTransactionIfNecessary
  • AbstractPlatformTransactionManager # getTransaction

M02_02 源代码 , 先来看一下第二步怎么到 getTransaction 的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
java复制代码
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

// 如果没有指定名称,则将方法标识应用为事务名称
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}

TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// txAttr 和 tm 均存在时 , 调用 AbstractPlatformTransactionManager
status = tm.getTransaction(txAttr);
} else {
// 仅打 log
}
}
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

可以看到 , 相关代码为 status = tm.getTransaction(txAttr)

TransactionManager 总共有2个 :

  • AbstractPlatformTransactionManager : 总的抽象事务管理器
  • JpaTransactionManager : 因为是基于 JPA , 此处是 JpaTransactionManager
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
java复制代码C03- AbstractPlatformTransactionManager
M03_01- getTransaction(TransactionDefinition definition)
1- doGetTransaction() : 调用子类事务管理 -> M04_01
2- newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources) -> PS:M03_01_01
3- doBegin(transaction, definition)
?- 根据给定的事务定义,使用语义开始一个新的事务 -> PS:M03_01_02
4- prepareSynchronization(status, definition)
M03_02- commit(TransactionStatus status)
1- processRollback(defStatus, false) : 处理回调操作 -> M03_03
2- processCommit(defStatus) : -> M03_04
M03_04- processCommit(defStatus) : 主要的 commit 流程
1- 前置处理
- prepareForCommit(status);
- triggerBeforeCommit(status);
- triggerBeforeCompletion(status);
2- 通过不同的 DefaultTransactionStatus 执行不同的逻辑
3- 在commit后触发回调,并将在那里抛出的异常传播给调用方,但事务仍被视为已提交
- triggerAfterCommit(status)
- triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED)
4- 完成后进行清理,必要时清除同步,并调用doCleanupAfterCompletion
- cleanupAfterCompletion(status)

// 调用子类事务管理 , 此处使用 JpaTransactionManager
C04- JpaTransactionManager
M04_01- doGetTransaction()
?- 该方法主要是对 JpaTransactionObject 的创建和处理
1- setSavepointAllowed
- TransactionSynchronizationManager.getResource : 获取 EntityManagerHolder
2- setEntityManagerHolder :
- TransactionSynchronizationManager.getResource(getDataSource()) : ConnectionHolder
3- setConnectionHolder : 包装JDBC的资源持有者
M04_02- doBegin(Object transaction, TransactionDefinition definition)
1- 获取 M04_01 创建的 JpaTransactionObject
2- txObject.getEntityManagerHolder().getEntityManager() : 获取 EntityManager
3- determineTimeout(definition) : 准备超时时间
4- getJpaDialect().beginTransaction : 委托给JpaDialect以开始实际的事务 -> PS:M04_02_05
5- txObject.getEntityManagerHolder().setTimeoutInSeconds(timeoutToUse) : 如果超时 , 设置超时时间
6-IF- 如果设置了JPA EntityManager的JDBC连接
- getJpaDialect().getJdbcConnection(em, definition.isReadOnly()) : 获取 ConnectionHandle
- 通过 ConnectionHandle 生成 ConnectionHolder
- TransactionSynchronizationManager.bindResource(getDataSource(), conHolder) :
- txObject.setConnectionHolder(conHolder)
7- txObject.getEntityManagerHolder().setSynchronizedWithTransaction(true) : 将资源标记为与事务同步


// PS:M04_02_05 实际事务处理流程 : 主要是2句话
Object transactionData = getJpaDialect().beginTransaction(em,new JpaTransactionDefinition(definition, timeoutToUse, txObject.isNewEntityManagerHolder()));
txObject.setTransactionData(transactionData);


// 流程处理的参数情况关注类
C- NativeSession # execSQL
// 1
select orgentity0_.id as id1_0_0_, orgentity0_.org_name as org_name2_0_0_, orgentity0_.org_type as org_type3_0_0_ from org orgentity0_ where orgentity0_.id=?
select userentity0_.userid as userid1_1_0_, userentity0_.isactive as isactive2_1_0_, userentity0_.orgid as orgid3_1_0_, userentity0_.remark as remark4_1_0_, userentity0_.userlink as userlink5_1_0_, userentity0_.username as username6_1_0_, userentity0_.usertype as usertype7_1_0_ from user userentity0_ where userentity0_.userid=2177281

select orgentity0_.id as id1_0_0_, orgentity0_.org_name as org_name2_0_0_, orgentity0_.org_type as org_type3_0_0_ from org orgentity0_ where orgentity0_.id=5052474
select userentity0_.userid as userid1_1_0_, userentity0_.isactive as isactive2_1_0_, userentity0_.orgid as orgid3_1_0_, userentity0_.remark as remark4_1_0_, userentity0_.userlink as userlink5_1_0_, userentity0_.username as username6_1_0_, userentity0_.usertype as usertype7_1_0_ from user userentity0_ where userentity0_.userid=4412226

.......

PS:M03_01_01 newTransactionStatus

作用 : 用给定的参数创建一个TransactionStatus实例

1
2
3
4
5
6
java复制代码protected DefaultTransactionStatus newTransactionStatus(TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {

boolean actualNewSynchronization = newSynchronization &&!TransactionSynchronizationManager.isSynchronizationActive();
return new DefaultTransactionStatus(transaction, newTransaction, actualNewSynchronization,
definition.isReadOnly(), debug, suspendedResources);
}

S:M03_01_02 具体的流程
作用 : 根据给定的事务定义,使用语义开始一个新的事务 。 由于已经由抽象管理器处理过 , 所以不需要关心传播行为的应用。当事务管理器决定实际启动一个新事务时,将调用此方法。要么之前没有任何交易,要么之前的交易已经暂停。

S:M02_05_01 中干了什么 ?
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus())

可以看到 , 这里通过 TransactionManager 调用具体的 Commit -> AbstractPlatformTransactionManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
java复制代码C03- AbstractPlatformTransactionManager
M03_02- commit(TransactionStatus status)


// commit 主要逻辑
public final void commit(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalTransactionStateException(".....");
}

DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
processRollback(defStatus, false);
return;
}

if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
processRollback(defStatus, true);
return;
}

// 核心调用逻辑 , 提交 commit 处理
processCommit(defStatus);
}

3.5 异常的处理

异常的处理主要是回退的相关操作 , 该操作主要在 C03- AbstractPlatformTransactionManager

Rollback 的调用 :
观察发现 , Rollback 不仅仅在出现异常时调用 , 这里会有不同的场景 , 主要的有2种 type :

  • 事务代码请求回滚 : processRollback(defStatus, false)
  • 全局事务被标记为仅回滚但请求提交事务代码 : processRollback(defStatus, true)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
java复制代码// 事务的回退主流程
C03- AbstractPlatformTransactionManager
M03_03- processRollback(DefaultTransactionStatus status, boolean unexpected) : 处理实际回退, 检查已完成标志
// Type 1 : 正常执行情况下的 Rollback
1- triggerBeforeCompletion(status)
2- triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK)
// Type 2 : 调用 doCallback 回滚的情况
1- triggerBeforeCompletion(status)
2- doRollback(status)
3- triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK)
M03_05- triggerBeforeCompletion(status)
1- TransactionSynchronizationUtils.triggerBeforeCompletion()
M03_06- triggerAfterCompletion(status)
M03_07- doRollback(status)
?- 这是一个需要子类实现的接口 , 以 JPA 为例 , 这里使用的 JpaTransactionManager

C04- JpaTransactionManager
F04_01- EntityManagerFactory entityManagerFactory;
M04_03- doRollback
// Step 1 : 属性获取 -> PS:M04_03_01
1- status.getTransaction() : 获取 JpaTransactionObject
// Step 2 : 执行 EntityTransaction
2- txObject.getEntityManagerHolder().getEntityManager().getTransaction() -> PS:M04_03_02
3- tx.rollback() -> PS:M04_03_03
- 主要调用对象
// Step 3 : finally 强制处理
4- txObject.getEntityManagerHolder().getEntityManager().clear()


C05- TransactionImpl
M05_01- rollback
- TransactionStatus status = getStatus() : 获取 TransactionStatus --> PS:M05_01_01
- status.canRollback() : 是否可以回退 , 不可则抛出异常 --> PS:M05_01_02
- internalGetTransactionDriverControl().rollback() : 执行回退 --> PS:M05_01_03

// 发起命令 callback

PS:M04_03_01 参数详情

JpaTransactionObject 参数详情

这里可以看到 , 每一个操作都会形成一个 EntityInsertAction

JPA_Transaction_object.jpg

PS:M05_01_03 回退逻辑详情

核心类为 JdbcResourceLocalTransactionCoordinatorImpl , 回退有如下调用栈 (中间有部分省略) :

setAutoCommit 环节

  • JpaTransactionManager # doBegin
  • HibernateJpaDialect # beginTransaction
  • TransactionImpl # begin
  • JdbcResourceLocalTransactionCoordinatorImpl # begin
  • HikariProxyConnection # setAutoCommit
  • ConnectionImpl # setAutoCommit
  • NativeProtocol # sendQueryString

SQL 执行环节

  • HikariProxyPreparedStatement # executeQuery
  • NativeSession # execSQL
  • NativeProtocol # sendQueryString

PS :最终执行语句均为 :

  • NativeProtocol # sendQueryString
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
java复制代码C11- JdbcResourceLocalTransactionCoordinatorImpl
M- rollback()

C12- AbstractLogicalConnectionImplementor
M- rollback()
- getConnectionForTransactionManagement().rollback()
?- 调用 ProxyConnection
- status = TransactionStatus.ROLLED_BACK

C13- ProxyConnection
M13_01- rollback()

// M13_01 rollback() 源代码
public void rollback() throws SQLException{
delegate.rollback();
isCommitStateDirty = false;
lastAccess = currentTime();
}

// 这里就涉及到 com.mysql.cj.jdbc
C14- ConnectionImpl
M14_01- rollback()
- rollbackNoChecks()

private void rollbackNoChecks() throws SQLException {
synchronized (getConnectionMutex()) {
if (this.useLocalTransactionState.getValue()) {
if (!this.session.getServerSession().inTransactionOnServer()) {
return; // effectively a no-op
}
}
// 回退核心逻辑
this.session.execSQL(null, "rollback", -1, null, false, this.nullStatementResultSetFactory, this.database, null, false);
}
}


// 最终处理
C- NativeProtocol # sendQueryString
return sendQueryPacket(callingQuery, sendPacket, maxRows, streamResults, catalog, cachedMetadata, getProfilerEventHandlerInstanceFunction,
resultSetFactory);

sendPacket -> rollback

// PS : 这里就涉及到 MySQL 的事务了
MySQL的 ROLLBACK 命令用来回退(撤销)MySQL语句

START TRANSACTION or BEGIN 启动新事务.
COMMIT 提交当前事务,使其更改永久化.
ROLLBACK 回滚当前事务,取消其更改
SET 自动提交禁用或启用当前会话的默认自动提交模式。

默认情况下,MySQL 运行时启用了自动提交模式。这意味着,如果事务中没有其他语句,那么每个语句都是原子的,就好像它被 START TRANSACTION 和 COMMIT 包围了一样。不能使用 ROLLBACK 撤消该效果; 但是,如果在语句执行期间发生错误,将回滚该语句。

// 若要对一系列语句隐式禁用自动提交模式,请使用 START TRANSACTION 语句:

START TRANSACTION;
SELECT @A:=SUM(salary) FROM table1 WHERE type=1;
UPDATE table2 SET summary=@A WHERE type=1;
COMMIT;

// 具体的可以参考官方文档 : https://dev.mysql.com/doc/refman/5.7/en/commit.html

总结

整体大概是把 JPA Transaction 的流程过了一遍 , 不过一直有一点怀疑 , 现阶段看出来的就是通过 SQL 其本身的事务管理来做的 , 不确定是否有其他的环节控制或者通过业务的方式处理 .

附录

Mysql 事务回滚机制

1
java复制代码官方文档 @ https://dev.mysql.com/doc/refman/8.0/en/innodb-autocommit-commit-rollback.html

先来看一下 , 官方提供的案例 :

在 InnoDB 中,所有的用户活动都发生在一个事务中。如果启用了自动提交模式,则每个 SQL 语句自己形成一个事务。

注意 , 在 MyMyISAM 中 ,这种处理是失效的!!

1
2
3
4
SQL复制代码CREATE TABLE customer (a INT, b CHAR (20), INDEX (a));

# 这里可以看到 , 此时运行可以直接插入
INSERT INTO customer VALUES (10, 'Heikki');

类型一 : 多语句事务提交

  • 如果这里不执行 commit , 当前操作是不会提交的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SQL复制代码
# Step 1 :开启事务和自动提交
# 启用自动提交的会话可以通过以显式的 START TRANSACTION 或 BEGIN 语句开始并以 COMMIT 或 ROLLBACK 语句结束来执行多语句事务。
START TRANSACTION;


# Step 2 : 插入一条记录 , 在自动提交的前提下 , 一个 SQL 就是一个事务
INSERT INTO customer VALUES (10, 'Heikki');


# Step 3 : 提交 (提交之后 , 数据才正式添加)
# 如果一个禁用自动提交的会话在没有显式提交最终事务的情况下结束,MySQL 将回滚该事务。
# COMMIT 意味着当前事务中所做的更改是永久性的,并且对其他会话可见
COMMIT;

类型二 :回退操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SQL复制代码# Step 1 : 设置非自动提交
# 如果在 setautocommit = 0的会话中禁用自动提交模式,则会话始终打开一个事务。COMMIT 或 ROLLBACK 语句结束当前事务并启动新事务。
SET autocommit=0;

# Step 2 : 插入所有的数据
INSERT INTO customer VALUES (15, 'John');
INSERT INTO customer VALUES (20, 'Paul');
DELETE FROM customer WHERE b = 'Heikki';

# Step 3 : 回滚事务
ROLLBACK;

# 可以看到 , DELETE Heikki 没有执行 , 第一条 插入成功
SELECT * FROM customer;

对于本案例的事务处理

1
2
3
4
5
6
7
8
9
10
11
12
sql复制代码START TRANSACTION;
SET autocommit=0;
# Step 2 : 插入一条记录 , 在自动提交的前提下 , 一个 SQL 就是一个事务
INSERT INTO org VALUES (10,'111', 'Heikki');
INSERT INTO org VALUES (20,'222','Paul');
#DELETE FROM org WHERE id = 'Heikki';

# Step 3 : 回滚事务
ROLLBACK;

# 可以看到 , DELETE Heikki 没有执行 , 第一条 插入成功
SELECT * FROM org;

观察到的本案例的 SQL 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
sql复制代码# 项目启动时
SET autocommit=1

# Step 1 : 进入方法
SET autocommit=0

# Step 2 : 执行 SQL
select userentity0_.userid as userid1_1_0_, userentity0_.isactive as isactive2_1_0_, userentity0_.orgid as orgid3_1_0_, userentity0_.remark as remark4_1_0_, userentity0_.userlink as userlink5_1_0_, userentity0_.username as username6_1_0_, userentity0_.usertype as usertype7_1_0_ from user userentity0_ where userentity0_.userid=3138559
select orgentity0_.id as id1_0_0_, orgentity0_.org_name as org_name2_0_0_, orgentity0_.org_type as org_type3_0_0_ from org orgentity0_ where orgentity0_.id=7614913tity0_.userlink as userlink5_1_0_, userentity0_.username as username6_1_0_, userentity0_.usertype as usertype7_1_0_ from user userentity0_ where userentity0_.userid=3138559

// Step 3 : 插入操作
insert into user .....
insert into org ........

commit
SET autocommit=1


// 如果出现异常 , 会调用 rollback
// PS : 若要保留自动提交 (SET autocommit=1),请以 start transaction 开始每个事务,并以 COMMIT 或 ROLLBACK 结束

PS : 这里可以 Debug C- NativeProtocol # sendQueryString 拿到

M04_01 : JpaTransactionManager # doGetTransaction()源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
java复制代码protected Object doGetTransaction() {
JpaTransactionObject txObject = new JpaTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
// JPA中资源持有者的封装
EntityManagerHolder emHolder = (EntityManagerHolder)
// 管理每个线程的资源和事务同步的中央委托
TransactionSynchronizationManager.getResource(obtainEntityManagerFactory());
if (emHolder != null) {
txObject.setEntityManagerHolder(emHolder, false);
}

if (getDataSource() != null) {
// ConnectionHolder :包装JDBC的资源持有者
// 对于特定的javax.sql. datasource,将这个类的实例绑定到线程。
// 注意:这是一个SPI类,不打算被应用程序使用。
ConnectionHolder conHolder = (ConnectionHolder)
TransactionSynchronizationManager.getResource(getDataSource());
txObject.setConnectionHolder(conHolder);
}

return txObject;
}

M04_02 : JpaTransactionManager # doBegin()源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
java复制代码protected void doBegin(Object transaction, TransactionDefinition definition) {

// JPA事务对象,表示EntityManagerHolder。JpaTransactionManager用作事务对象。
JpaTransactionObject txObject = (JpaTransactionObject) transaction;

if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
throw new IllegalTransactionStateException(....);
}

try {
if (!txObject.hasEntityManagerHolder() ||txObject.getEntityManagerHolder().isSynchronizedWithTransaction()) {
// 实体类管理器
EntityManager newEm = createEntityManagerForTransaction();
txObject.setEntityManagerHolder(new EntityManagerHolder(newEm), true);
}

EntityManager em = txObject.getEntityManagerHolder().getEntityManager();
final int timeoutToUse = determineTimeout(definition);
// 开始一个事务
Object transactionData = getJpaDialect().beginTransaction(em,new JpaTransactionDefinition(definition, timeoutToUse, txObject.isNewEntityManagerHolder()));
// 为当前事务对象标明事务属性
txObject.setTransactionData(transactionData);

if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) {
// 控制超时时间
txObject.getEntityManagerHolder().setTimeoutInSeconds(timeoutToUse);
}

if (getDataSource() != null) {
// 连接管理 , 执行相关的连接操作
ConnectionHandle conHandle = getJpaDialect().getJdbcConnection(em, definition.isReadOnly());
if (conHandle != null) {
ConnectionHolder conHolder = new ConnectionHolder(conHandle);
if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) {
conHolder.setTimeoutInSeconds(timeoutToUse);
}
// 这里绑定了资源 , 把一个数据库数据绑定为了映射对象
// TODO : 这里应该就是三态变化的核心 , 以后看看
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
txObject.setConnectionHolder(conHolder);
} else {
//log.....
}
}

if (txObject.isNewEntityManagerHolder()) {
TransactionSynchronizationManager.bindResource(obtainEntityManagerFactory(), txObject.getEntityManagerHolder());
}
// 将资源标记为与事务同步
txObject.getEntityManagerHolder().setSynchronizedWithTransaction(true);
} catch (TransactionException ex) {
closeEntityManagerAfterFailedBegin(txObject);
throw ex;
} catch (Throwable ex) {
closeEntityManagerAfterFailedBegin(txObject);
throw new CannotCreateTransactionException("Could not open JPA EntityManager for transaction", ex);
}
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%