目錄
一、Resource
Resource能被ResourceManager管理並且能夠關聯globalTransaction。
public interface Resource {
// 主從的datasource應該有相同的groupId
String getResourceGroupId();
// 當前datasource或者tccResourceId的resourceId
String getResourceId();
// 獲取resource的類型, 有AT、TCC兩種
BranchType getBranchType();
}
二、TCCResource
TCCResource實現Resource接口,爲用戶提供複雜業務sql自定義手動模式
public class TCCResource implements Resource {
private String resourceGroupId = "DEFAULT";
private String appName;
private String actionName;
private Object targetBean;
private Method prepareMethod;
private String commitMethodName;
private Method commitMethod;
private String rollbackMethodName;
private Method rollbackMethod;
@Override
public String getResourceGroupId() {
return resourceGroupId;
}
@Override
public String getResourceId() {
return actionName;
}
@Override
public BranchType getBranchType() {
return BranchType.TCC;
}
}
三、DataSourceProxy
DataSourceProxy實現Resource接口,BranchType爲AT自動模式。它繼承AbstractDataSourceProxy代理類,所有的DataSource相關的方法調用傳入的targetDataSource代理類的方法,除了創建connection方法爲創建ConnectionProxy代理類。對象初始化時獲取連接的jdbcUrl作爲resourceId,並註冊至DefaultResourceManager進行管理。同時還提供獲取原始連接不被代理的getPlainConnection方法。
public class DataSourceProxy extends AbstractDataSourceProxy implements Resource {
private String resourceGroupId;
private static final String DEFAULT_RESOURCE_GROUP_ID = "DEFAULT";
private String jdbcUrl;
private String dbType;
public DataSourceProxy(DataSource targetDataSource, String resourceGroupId) {
super(targetDataSource);
init(targetDataSource, resourceGroupId);
}
private void init(DataSource dataSource, String resourceGroupId) {
this.resourceGroupId = resourceGroupId;
try (Connection connection = dataSource.getConnection()) {
jdbcUrl = connection.getMetaData().getURL();
dbType = JdbcUtils.getDbType(jdbcUrl, null);
} catch (SQLException e) {
throw new IllegalStateException("can not init dataSource", e);
}
DefaultResourceManager.get().registerResource(this);
}
public Connection getPlainConnection() throws SQLException {
return targetDataSource.getConnection();
}
public String getDbType() {
return dbType;
}
@Override
public ConnectionProxy getConnection() throws SQLException {
Connection targetConnection = targetDataSource.getConnection();
return new ConnectionProxy(this, targetConnection);
}
@Override
public ConnectionProxy getConnection(String username, String password) throws SQLException {
Connection targetConnection = targetDataSource.getConnection(username, password);
return new ConnectionProxy(this, targetConnection);
}
@Override
public String getResourceGroupId() {
return resourceGroupId;
}
@Override
public String getResourceId() {
if (jdbcUrl.contains("?")) {
return jdbcUrl.substring(0, jdbcUrl.indexOf("?"));
} else {
return jdbcUrl;
}
}
@Override
public BranchType getBranchType() {
return BranchType.AT;
}
}
四、ConnectionProxy
ConnectionProxy繼承AbstractConnectionProxy代理類,創建Statement對象方法爲創建StatementProxy類,創建PreparedStatement對象方法爲創建PreparedStatementProxy代理類。持有ConnectionContext對象,保存當前connection的相關context數據。主要分析它的commit,rollback和setAutoCommit方法。
public class ConnectionProxy extends AbstractConnectionProxy {
private ConnectionContext context = new ConnectionContext();
private static final int DEFAULT_REPORT_RETRY_COUNT = 5;
public ConnectionProxy(DataSourceProxy dataSourceProxy, Connection targetConnection) {
super(dataSourceProxy, targetConnection);
}
public ConnectionContext getContext() {
return context;
}
public void bind(String xid) {
context.bind(xid);
}
public void setGlobalLockRequire(boolean isLock) {
context.setGlobalLockRequire(isLock);
}
public boolean isGlobalLockRequire() {
return context.isGlobalLockRequire();
}
// 檢查lockKeys是否鎖住query
public void checkLock(String lockKeys) throws SQLException {
// Just check lock without requiring lock by now.
try {
boolean lockable = DefaultResourceManager.get().lockQuery(BranchType.AT, getDataSourceProxy().getResourceId(), context.getXid(), lockKeys);
if (!lockable) {
throw new LockConflictException();
}
} catch (TransactionException e) {
recognizeLockKeyConflictException(e);
}
}
public void appendUndoLog(SQLUndoLog sqlUndoLog) {
context.appendUndoItem(sqlUndoLog);
}
public void appendLockKey(String lockKey) {
context.appendLockKey(lockKey);
}
// connection事務提交
public void commit() throws SQLException {
// 如果開啓了全球事務
if (context.inGlobalTransaction()) {
// 處理全球事務提交邏輯
processGlobalTransactionCommit();
} else if (context.isGlobalLockRequire()) {
// 如果設置了全球鎖,處理全球鎖提交邏輯
processLocalCommitWithGlobalLocks();
} else {
// 默認代理連接直接提交
targetConnection.commit();
}
}
// 處理全球鎖提交邏輯
private void processLocalCommitWithGlobalLocks() throws SQLException {
// 檢查鎖先
checkLock(context.buildLockKeys());
try {
targetConnection.commit();
} catch (Throwable ex) {
throw new SQLException(ex);
}
context.reset();
}
// 處理全球事務提交邏輯
private void processGlobalTransactionCommit() throws SQLException {
try {
// 註冊
register();
} catch (TransactionException e) {
recognizeLockKeyConflictException(e);
}
try {
if (context.hasUndoLog()) {
// 如果有UndoLogs,則調用UndoLogManager異步刪除UndoLogs
UndoLogManager.flushUndoLogs(this);
}
// 代理連接提交
targetConnection.commit();
} catch (Throwable ex) {
// 提交報錯,報告第一步提交失敗錯誤,讓其他分支數據回滾
report(false);
if (ex instanceof SQLException) {
throw new SQLException(ex);
}
}
// 報告成功
report(true);
context.reset();
}
// 分支註冊,獲取branchId
private void register() throws TransactionException {
Long branchId = DefaultResourceManager.get().branchRegister(BranchType.AT, getDataSourceProxy().getResourceId(),
null, context.getXid(), null, context.buildLockKeys());
context.setBranchId(branchId);
}
// connection事務回滾調用
public void rollback() throws SQLException {
// 數據直接回滾
targetConnection.rollback();
// 如果開啓了全球事務,且註冊了當前分支至當前全球事務管理中
// 提交報錯,報告第一步提交失敗錯誤,讓其他分支數據回滾
if (context.inGlobalTransaction()) {
if (context.isBranchRegistered()) {
report(false);
}
}
context.reset();
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
// change autocommit from false to true, we should commit() first according to JDBC spec.
if ((autoCommit) && !getAutoCommit()) {
commit();
}
targetConnection.setAutoCommit(autoCommit);
}
// 報告當前分支事務是否成功,讓TC發起其他分支回滾或提交請求,重試次數默認爲5次
private void report(boolean commitDone) throws SQLException {
int retry = REPORT_RETRY_COUNT;
while (retry > 0) {
try {
DefaultResourceManager.get().branchReport(BranchType.AT, context.getXid(), context.getBranchId(),
(commitDone ? BranchStatus.PhaseOne_Done : BranchStatus.PhaseOne_Failed), null);
return;
} catch (Throwable ex) {
LOGGER.error("Failed to report [" + context.getBranchId() + "/" + context.getXid() + "] commit done ["
+ commitDone + "] Retry Countdown: " + retry);
retry--;
if (retry == 0) {
throw new SQLException("Failed to report branch status " + commitDone, ex);
}
}
}
}
}
五、ConnectionContext
ConnectionContext持有當前連接數據對象,xid當前全球事務唯一id,沒有全球事務爲null,branchId當前事務連接分支,受全球事務管控。isGlobalLockRequire鎖,lockKeysBuffer當前操作的表和主鍵字符串拼接而成的字符串集合。SQLUndoLog當前業務處理生成的待回滾日誌。
public class ConnectionContext {
private String xid;
private Long branchId;
private boolean isGlobalLockRequire;
//table and primary key should not be duplicated
private Set<String> lockKeysBuffer = new HashSet<>();
private List<SQLUndoLog> sqlUndoItemsBuffer = new ArrayList<>();
boolean isGlobalLockRequire() {
return isGlobalLockRequire;
}
void setGlobalLockRequire(boolean isGlobalLockRequire) {
this.isGlobalLockRequire = isGlobalLockRequire;
}
void appendLockKey(String lockKey) {
lockKeysBuffer.add(lockKey);
}
void appendUndoItem(SQLUndoLog sqlUndoLog) {
sqlUndoItemsBuffer.add(sqlUndoLog);
}
public boolean inGlobalTransaction() {
return xid != null;
}
public boolean isBranchRegistered() {
return branchId != null;
}
void bind(String xid) {
if (xid == null) {
throw new IllegalArgumentException("xid should not be null");
}
if (!inGlobalTransaction()) {
setXid(xid);
} else {
if (!this.xid.equals(xid)) {
throw new ShouldNeverHappenException();
}
}
}
public boolean hasUndoLog() {
return sqlUndoItemsBuffer.size() > 0;
}
public String getXid() {
return xid;
}
void setXid(String xid) {
this.xid = xid;
}
public Long getBranchId() {
return branchId;
}
void setBranchId(Long branchId) {
this.branchId = branchId;
}
void reset(){
this.reset(null);
}
void reset(String xid) {
this.xid = xid;
branchId = null;
this.isGlobalLockRequire = false;
lockKeysBuffer.clear();
sqlUndoItemsBuffer.clear();
}
public String buildLockKeys() {
if (lockKeysBuffer.isEmpty()) {
return null;
}
StringBuilder appender = new StringBuilder();
Iterator<String> iterable = lockKeysBuffer.iterator();
while (iterable.hasNext()) {
appender.append(iterable.next());
if (iterable.hasNext()) {
appender.append(";");
}
}
return appender.toString();
}
public List<SQLUndoLog> getUndoItems() {
return sqlUndoItemsBuffer;
}
}
六、StatementProxy
StatementProxy繼承AbstractStatementProxy類,泛型繼承Statement,可以爲PreparedStatement或者爲CallableStatement,目前只提供了PreparedStatement的實現,未提供CallableStatement調用存儲過程的實現。AbstractStatementProxy代理statement的方法,只有當executeBatch方法如果是開啓了全球事務,則不支持。StatementProxy的executeQuery,executeUpdate和execute執行sql方法,都是調用ExecuteTemplate的execute模板方法,對sql進行解析,添加日誌處理等工作。StatementCallback回調方法爲原生statement的對應方法。
public class StatementProxy<T extends Statement> extends AbstractStatementProxy<T> {
public StatementProxy(AbstractConnectionProxy connectionWrapper, T targetStatement, String targetSQL)
throws SQLException {
super(connectionWrapper, targetStatement, targetSQL);
}
public StatementProxy(AbstractConnectionProxy connectionWrapper, T targetStatement) throws SQLException {
this(connectionWrapper, targetStatement, null);
}
@Override
public ConnectionProxy getConnectionProxy() {
return (ConnectionProxy) super.getConnectionProxy();
}
@Override
public ResultSet executeQuery(String sql) throws SQLException {
this.targetSQL = sql;
return ExecuteTemplate.execute(this, new StatementCallback<ResultSet, T>() {
@Override
public ResultSet execute(Statement statement, Object... args) throws SQLException {
return statement.executeQuery((String) args[0]);
}
}, sql);
}
@Override
public int executeUpdate(String sql) throws SQLException {
this.targetSQL = sql;
return ExecuteTemplate.execute(this, new StatementCallback<Integer, T>() {
@Override
public Integer execute(Statement statement, Object... args) throws SQLException {
return statement.executeUpdate((String) args[0]);
}
}, sql);
}
@Override
public boolean execute(String sql) throws SQLException {
this.targetSQL = sql;
return ExecuteTemplate.execute(this, new StatementCallback<Boolean, T>() {
@Override
public Boolean execute(T statement, Object... args) throws SQLException {
return statement.execute((String) args[0]);
}
}, sql);
}
@Override
public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
this.targetSQL = sql;
return ExecuteTemplate.execute(this, new StatementCallback<Integer, T>() {
@Override
public Integer execute(T statement, Object... args) throws SQLException {
return statement.executeUpdate((String) args[0],(int)args[1]);
}
}, sql,autoGeneratedKeys);
}
@Override
public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
this.targetSQL = sql;
return ExecuteTemplate.execute(this, new StatementCallback<Integer, T>() {
@Override
public Integer execute(T statement, Object... args) throws SQLException {
return statement.executeUpdate((String) args[0],(int [])args[1]);
}
}, sql,columnIndexes);
}
@Override
public int executeUpdate(String sql, String[] columnNames) throws SQLException {
this.targetSQL = sql;
return ExecuteTemplate.execute(this, new StatementCallback<Integer, T>() {
@Override
public Integer execute(T statement, Object... args) throws SQLException {
return statement.executeUpdate((String) args[0],(String[])args[1]);
}
}, sql,columnNames);
}
@Override
public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
this.targetSQL = sql;
return ExecuteTemplate.execute(this, new StatementCallback<Boolean, T>() {
@Override
public Boolean execute(T statement, Object... args) throws SQLException {
return statement.execute((String) args[0],(int)args[1]);
}
}, sql,autoGeneratedKeys);
}
@Override
public boolean execute(String sql, int[] columnIndexes) throws SQLException {
this.targetSQL = sql;
return ExecuteTemplate.execute(this, new StatementCallback<Boolean, T>() {
@Override
public Boolean execute(T statement, Object... args) throws SQLException {
return statement.execute((String) args[0],(int[])args[1]);
}
}, sql,columnIndexes);
}
@Override
public boolean execute(String sql, String[] columnNames) throws SQLException {
this.targetSQL = sql;
return ExecuteTemplate.execute(this, new StatementCallback<Boolean, T>() {
@Override
public Boolean execute(T statement, Object... args) throws SQLException {
return statement.execute((String) args[0],(String[])args[1]);
}
}, sql,columnNames);
}
}
七、PreparedStatementProxy
PreparedStatementProxy繼承AbstractPreparedStatementProxy,AbstractPreparedStatementProxy繼承StatementProxy<PreparedStatement>類,實現PreparedStatement和ParametersHolder接口。ParametersHolder持有當前PreparedStatement設置的參數,爲模板方法的sql解析和日誌生成提供方便。執行execute,executeQuery和executeUpdate同樣調用模板方法。
public class PreparedStatementProxy extends AbstractPreparedStatementProxy
implements PreparedStatement, ParametersHolder {
@Override
public ArrayList<Object>[] getParameters() {
return parameters;
}
private void init() throws SQLException {
int paramCount = targetStatement.getParameterMetaData().getParameterCount();
this.parameters = new ArrayList[paramCount];
for (int i = 0; i < paramCount; i++) {
parameters[i] = new ArrayList<>();
}
}
public PreparedStatementProxy(AbstractConnectionProxy connectionProxy, PreparedStatement targetStatement,
String targetSQL) throws SQLException {
super(connectionProxy, targetStatement, targetSQL);
init();
}
@Override
public boolean execute() throws SQLException {
return ExecuteTemplate.execute(this, new StatementCallback<Boolean, PreparedStatement>() {
@Override
public Boolean execute(PreparedStatement statement, Object... args) throws SQLException {
return statement.execute();
}
});
}
@Override
public ResultSet executeQuery() throws SQLException {
return ExecuteTemplate.execute(this, new StatementCallback<ResultSet, PreparedStatement>() {
@Override
public ResultSet execute(PreparedStatement statement, Object... args) throws SQLException {
return statement.executeQuery();
}
});
}
@Override
public int executeUpdate() throws SQLException {
return ExecuteTemplate.execute(this, new StatementCallback<Integer, PreparedStatement>() {
@Override
public Integer execute(PreparedStatement statement, Object... args) throws SQLException {
return statement.executeUpdate();
}
});
}
}
八、ExecuteTemplate
ExecuteTemplate爲具體statement的execute,executeQuery和executeUpdate執行提供模板方法。
public class ExecuteTemplate {
public static <T, S extends Statement> T execute(StatementProxy<S> statementProxy,
StatementCallback<T, S> statementCallback,
Object... args) throws SQLException {
return execute(null, statementProxy, statementCallback, args);
}
public static <T, S extends Statement> T execute(SQLRecognizer sqlRecognizer,
StatementProxy<S> statementProxy,
StatementCallback<T, S> statementCallback,
Object... args) throws SQLException {
// 如果當前context沒有開啓全球事務,而且沒有全球鎖,則回調targetStatement執行原生方法
if (!RootContext.inGlobalTransaction() && !RootContext.requireGlobalLock()) {
// Just work as original statement
return statementCallback.execute(statementProxy.getTargetStatement(), args);
}
// 通過SQLVisitorFactory找到sqlRecognizer
if (sqlRecognizer == null) {
sqlRecognizer = SQLVisitorFactory.get(
statementProxy.getTargetSQL(),
statementProxy.getConnectionProxy().getDbType());
}
Executor<T> executor = null;
if (sqlRecognizer == null) {
executor = new PlainExecutor<T, S>(statementProxy, statementCallback);
} else {
// 根據不同的sql類型獲取不同的Executor執行器。
switch (sqlRecognizer.getSQLType()) {
case INSERT:
executor = new InsertExecutor<T, S>(statementProxy, statementCallback, sqlRecognizer);
break;
case UPDATE:
executor = new UpdateExecutor<T, S>(statementProxy, statementCallback, sqlRecognizer);
break;
case DELETE:
executor = new DeleteExecutor<T, S>(statementProxy, statementCallback, sqlRecognizer);
break;
case SELECT_FOR_UPDATE:
executor = new SelectForUpdateExecutor<T, S>(statementProxy, statementCallback, sqlRecognizer);
break;
default:
executor = new PlainExecutor<T, S>(statementProxy, statementCallback);
break;
}
}
T rs = null;
try {
// 調用執行器執行返回結果
rs = executor.execute(args);
} catch (Throwable ex) {
if (!(ex instanceof SQLException)) {
// Turn other exception into SQLException
ex = new SQLException(ex);
}
throw (SQLException)ex;
}
return rs;
}
}