Fescar - RM InsertExecutor介紹
開篇
這篇文章的目的是講解RM Executor模組當中一些通用的方法,這些方法在各個Executor的父類當中實現的,各個子類Executor模組都會複用,因此抽取出來統一的進行講解。
個人是認為抽取通用的內容放在一篇文章講解完後可以針對每類Executor講解特有的功能,這樣能夠有更好的理解。這篇文章講解Executor的實現類InsertExecutor。
類依賴圖
說明:
- 著重講解InsertExecutor實現類。
InsertExecutor方法介紹
public class InsertExecutor<T, S extends Statement> extends AbstractDMLBaseExecutor<T, S> {
protected TableRecords beforeImage() throws SQLException {
return TableRecords.empty(getTableMeta());
}
}
public class TableRecords {
public static TableRecords empty(TableMeta tableMeta) {
return new TableRecords(tableMeta) {
@Override
public int size() {
return 0;
}
@Override
public List<Field> pkRows() {
return new ArrayList<>();
}
@Override
public void add(Row row) {
throw new UnsupportedOperationException("xxx");
}
@Override
public TableMeta getTableMeta() {
throw new UnsupportedOperationException("xxx");
}
};
}
}
說明:
- Insert操作的執行前映象顧名思義是空的,所以beforeImage()方法返回空記錄。
- TableRecords.empty返回的是一個沒有記錄的TableRecords物件。
public class InsertExecutor<T, S extends Statement> extends AbstractDMLBaseExecutor<T, S> {
protected TableRecords afterImage(TableRecords beforeImage) throws SQLException {
SQLInsertRecognizer recogizier = (SQLInsertRecognizer)sqlRecognizer;
List<String> insertColumns = recogizier.getInsertColumns();
TableMeta tmeta = getTableMeta();
TableRecords afterImage = null;
if (tmeta.containsPK(insertColumns)) {
// 處理插入資料包含主鍵的情況
List<Object> pkValues = null;
String pk = tmeta.getPkName();
for (int paramIdx = 0; paramIdx < insertColumns.size(); paramIdx++) {
if (insertColumns.get(paramIdx).equalsIgnoreCase(pk)) {
// 處理PreparedStatement型別,從引數列表中提取
if (statementProxy instanceof PreparedStatementProxy) {
pkValues = ((PreparedStatementProxy) statementProxy).getParamsByIndex(paramIdx);
} else {
// 處理Statement型別,直接從SQL語句中提取
List<List<Object>> insertRows = recogizier.getInsertRows();
pkValues = new ArrayList<>(insertRows.size());
for (List<Object> row : insertRows) {
pkValues.add(row.get(paramIdx));
}
}
//主鍵只有一個
break;
}
}
if (pkValues == null) {
throw new ShouldNeverHappenException();
}
afterImage = getTableRecords(pkValues);
} else {
// 處理主鍵自動生成的場景,也就是不包含主鍵的情況。
Map<String, ColumnMeta> pkMetaMap = getTableMeta().getPrimaryKeyMap();
if (pkMetaMap.size() != 1) {
throw new NotSupportYetException();
}
// 獲取primary key的後設資料資訊
ColumnMeta pkMeta = pkMetaMap.values().iterator().next();
if (!pkMeta.isAutoincrement()) {
throw new ShouldNeverHappenException();
}
// 獲取generatedKeys()獲取生成的主鍵
ResultSet genKeys = null;
try {
genKeys = statementProxy.getTargetStatement().getGeneratedKeys();
} catch (SQLException e) {
if ("S1009".equalsIgnoreCase(e.getSQLState())) {
genKeys = statementProxy.getTargetStatement().executeQuery("SELECT LAST_INSERT_ID()");
} else {
throw e;
}
}
List<Object> pkValues = new ArrayList<>();
while (genKeys.next()) {
Object v = genKeys.getObject(1);
pkValues.add(v);
}
// 生成執行後映象
afterImage = getTableRecords(pkValues);
}
if (afterImage == null) {
throw new SQLException("Failed to build after-image for insert");
}
return afterImage;
}
}
說明:
- afterImage準備執行後的映象,針對Insert操作就是新增的記錄。
- afterImage處理分為兩種場景,分為插入記錄包含主鍵欄位和不包含主鍵欄位。
- 記錄包含主鍵欄位場景下,區分preparedStatement和Statement兩種場景,前者去傳入引數中獲取,後者從執行的SQL當中解析。
- 不包含主鍵欄位場景下,通過獲取getGeneratedKeys()返回的值獲取。
- 獲取主鍵的值以後pkValues,然後通過getTableRecords()生成執行後映象。
public class InsertExecutor<T, S extends Statement> extends AbstractDMLBaseExecutor<T, S> {
private TableRecords getTableRecords(List<Object> pkValues) throws SQLException {
TableRecords afterImage;
String pk = getTableMeta().getPkName();
StringBuffer selectSQLAppender = new StringBuffer("SELECT * FROM " + getTableMeta().getTableName() + " WHERE ");
for (int i = 1; i <= pkValues.size(); i++) {
selectSQLAppender.append(pk + "=?");
if (i < pkValues.size()) {
selectSQLAppender.append(" OR ");
}
}
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = statementProxy.getConnection().prepareStatement(selectSQLAppender.toString());
for (int i = 1; i <= pkValues.size(); i++) {
ps.setObject(i, pkValues.get(i - 1));
}
rs = ps.executeQuery();
afterImage = TableRecords.buildRecords(getTableMeta(), rs);
} finally {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
}
return afterImage;
}
}
說明:
- getTableRecords通過針對主鍵資訊拼接SELECT的SQL語句拼接成根據主鍵查詢欄位資訊的SQL。
- 通過buildRecords()方法針對查詢結果ResultSet進行處理。
- 生成執行後映象資訊在buildRecords()內部實現。
public class TableRecords {
public static TableRecords buildRecords(TableMeta tmeta, ResultSet resultSet) throws SQLException {
TableRecords records = new TableRecords(tmeta);
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
int columnCount = resultSetMetaData.getColumnCount();
while (resultSet.next()) {
List<Field> fields = new ArrayList<>(columnCount);
for (int i = 1; i <= columnCount; i++) {
String colName = resultSetMetaData.getColumnName(i);
ColumnMeta col = tmeta.getColumnMeta(colName);
Field field = new Field();
field.setName(col.getColumnName());
if (tmeta.getPkName().equals(field.getName())) {
field.setKeyType(KeyType.PrimaryKey);
}
field.setType(col.getDataType());
field.setValue(resultSet.getObject(i));
fields.add(field);
}
Row row = new Row();
row.setFields(fields);
records.add(row);
}
return records;
}
}
說明:
- buildRecords內部遍歷查詢的結果憑藉Row物件。
- 插入的資料可能有多條,外層while迴圈獲取所有的行數,內層for迴圈遍歷所有的列數。
期待
InsertExecutor生成執行前後映象的過程分析完後,會接著分析DeleteExecutor。額外的收貨就是分析完對於SQL或者說對阿里開源的druid有了一些深入研究的興趣。
相關文章
- Fescar - RM 全域性事務提交回滾流程
- 分散式事務中介軟體 Fescar—RM 模組原始碼解讀分散式原始碼
- 第1章 Oracle資料庫簡介-RMOracle資料庫
- 介紹
- Linux 系統的常用命令之 rm ,rm -rf , rm -f 以及rm 命令的其他引數命令Linux
- Proxy介紹
- Reflect介紹
- Azkaban介紹
- 模式介紹模式
- ZooKeeper介紹
- css介紹CSS
- PostgreSQLHooK介紹SQLHook
- DuelJS 介紹JS
- Docker介紹Docker
- StarRocks 介紹
- JCache 介紹
- zigbee 介紹
- GO 介紹Go
- RPC介紹RPC
- springcloud介紹SpringGCCloud
- CSRedisCore 介紹Redis
- AJAX 介紹
- php介紹PHP
- Pyzmq介紹MQ
- uniswap介紹
- LDAP 介紹LDA
- rustyline 介紹Rust
- SpringBoot介紹Spring Boot
- JSON 介紹JSON
- BitMap介紹
- Yocto 介紹
- 自我介紹
- git介紹Git
- FontFamily介紹
- Dubbo介紹
- Duktape 介紹
- jsoncpp 介紹JSON
- 公文介紹