简单理解下其事务传播机制的体现以及生效原理。
1. mysql 和 jdbc 提供的保存点
在看事务源码的过程中发现有个savepoint 的一操作,简单研究下其使用。
1. mysql 提供的savepoint
start TRANSACTION; SELECT * FROM `message` insert into message(title) values ("tx1"); SAVEPOINT point_tx1; insert into message(title) values ("tx2"); SAVEPOINT point_tx3; insert into message(title) values ("tx3"); rollback to point_tx1; // 删除保存点(commit 之后也会删除回滚点) release SAVEPOINT point_tx1; // 同名会覆盖 SAVEPOINT point_tx1; // commit 也会删除点 commit;
2. JDBC提供的savepoint
import java.sql.*; public class Client { public static void main(String[] args) throws ClassNotFoundException, SQLException { try { Class.forName("com.mysql.jdbc.Driver"); Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/media?useSSL=false", "root", "123456"); con.setAutoCommit(false); Savepoint savepoint = null; PreparedStatement preparedStatement = con.prepareStatement("insert into message(title) values (\"tx1\")"); preparedStatement.execute(); savepoint = con.setSavepoint("point_tx1"); PreparedStatement preparedStatement2 = con.prepareStatement("insert into message(title) values (\"tx2\")"); preparedStatement2.execute(); // 回滚到指定保存点然后commit, 第二条数据会回滚 con.rollback(savepoint); con.commit(); } catch (Exception e) { e.printStackTrace(); } } }
2. 事务传播机制以及对dao 的影响
这里简单研究下事务AOP是如何影响到dao 的,也可以理解为如何传递connection 的; 接着研究下事务默认的传播行为。
1. 以mybatis 为例子分析其事务如何影响到dao
根据之前的操作大体可以理解到。事务相关实际是改变Connection 相关信息,也就是设置自动提交为false, 然后手动commit。 接下来简单研究下,事务在AOP层面修改为手动提交之后,dao 是如何拿到相对应的Connection 对象的。
1. 事务AOP修改Connection 对象相关属性
(1) org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction 标志事务AOP开始执行,调用createTransactionIfNecessary 获取一个TransactionInfo 对象, 这个对象包含事务管理器、事务属性、连接点、以及上一个事务信息, TransactionInfo 源码如下: (oldTransactionInfo 是一个重要的对象,可以理解为是执行权交给上一个事务的重要对象)
/** * Opaque object used to hold transaction information. Subclasses * must pass it back to methods on this class, but not see its internals. */ protected static final class TransactionInfo { @Nullable private final PlatformTransactionManager transactionManager; @Nullable private final TransactionAttribute transactionAttribute; private final String joinpointIdentification; @Nullable private TransactionStatus transactionStatus; @Nullable private TransactionInfo oldTransactionInfo; public TransactionInfo(@Nullable PlatformTransactionManager transactionManager, @Nullable TransactionAttribute transactionAttribute, String joinpointIdentification) { this.transactionManager = transactionManager; this.transactionAttribute = transactionAttribute; this.joinpointIdentification = joinpointIdentification; } public PlatformTransactionManager getTransactionManager() { Assert.state(this.transactionManager != null, "No PlatformTransactionManager set"); return this.transactionManager; } @Nullable public TransactionAttribute getTransactionAttribute() { return this.transactionAttribute; } /** * Return a String representation of this joinpoint (usually a Method call) * for use in logging. */ public String getJoinpointIdentification() { return this.joinpointIdentification; } public void newTransactionStatus(@Nullable TransactionStatus status) { this.transactionStatus = status; } @Nullable public TransactionStatus getTransactionStatus() { return this.transactionStatus; } /** * Return whether a transaction was created by this aspect, * or whether we just have a placeholder to keep ThreadLocal stack integrity. */ public boolean hasTransaction() { return (this.transactionStatus != null); } private void bindToThread() { // Expose current TransactionStatus, preserving any existing TransactionStatus // for restoration after this transaction is complete. this.oldTransactionInfo = transactionInfoHolder.get(); transactionInfoHolder.set(this); } private void restoreThreadLocalStatus() { // Use stack to restore old transaction TransactionInfo. // Will be null if none was set. transactionInfoHolder.set(this.oldTransactionInfo); } @Override public String toString() { return (this.transactionAttribute != null ? this.transactionAttribute.toString() : "No transaction"); } }