1. 概述
SEATA 默认支持mysql,oracle,postgresql 等数据库,在软件国产化当下,支持国产数据库也是必须的。因此我们需要支持国产数据库。好在 seata 提供了比较好的扩展方式支持我们去扩展。扩展应该包括两块:
1.TC的扩展。
2.RM 客户端的扩展。
2. 扩展的整体思路
TC说的是seata-server,在源码中我们可以看到:
入口程序是Server 类。
2.1 构建数据源
private DataBaseTransactionStoreManager() {
logQueryLimit = CONFIG.getInt(ConfigurationKeys.STORE_DB_LOG_QUERY_LIMIT, DEFAULT_LOG_QUERY_LIMIT);
String datasourceType = CONFIG.getConfig(ConfigurationKeys.STORE_DB_DATASOURCE_TYPE);
//init dataSource
DataSource logStoreDataSource = EnhancedServiceLoader.load(DataSourceProvider.class, datasourceType).provide();
logStore = new LogStoreDataBaseDAO(logStoreDataSource);
}
加载数据源类:
@LoadLevel(name = "druid")
public class DruidDataSourceProvider extends AbstractDataSourceProvider {
@Override
public DataSource generate() {
DruidDataSource ds = new DruidDataSource();
ds.setDriverClassName(getDriverClassName());
ds.setDriverClassLoader(getDriverClassLoader());
ds.setUrl(getUrl());
ds.setUsername(getUser());
ds.setPassword(getPassword());
ds.setInitialSize(getMinConn());
ds.setMaxActive(getMaxConn());
ds.setMinIdle(getMinConn());
ds.setMaxWait(getMaxWait());
ds.setTimeBetweenEvictionRunsMillis(120000);
ds.setMinEvictableIdleTimeMillis(300000);
ds.setTestWhileIdle(true);
ds.setTestOnBorrow(false);
ds.setPoolPreparedStatements(true);
ds.setMaxPoolPreparedStatementPerConnectionSize(20);
ds.setValidationQuery(getValidationQuery(getDBType()));
ds.setDefaultAutoCommit(true);
return ds;
}
}
平台加载根据不同的数据类型加载 LogStoreSqls
对象。
public class LogStoreSqlsFactory {
private static Map<String, LogStoreSqls> LOG_STORE_SQLS_MAP = new ConcurrentHashMap<>();
/**
* get the log store sqls
* @param dbType the db type
* @return the LogStoreSqls
*/
public static LogStoreSqls getLogStoreSqls(String dbType) {
return CollectionUtils.computeIfAbsent(LOG_STORE_SQLS_MAP, dbType,
key -> EnhancedServiceLoader.load(LogStoreSqls.class, dbType.toLowerCase()));
}
}
通过数据库类型获取 LogStoreSqls 接口实现实例。
在平台中我们可以看到 有关LogStoreSqls 的实现。
我们增加一个kingbase 的实现。
@LoadLevel(name = "kingbase" )
public class KingBaseLogStoreSqls extends OracleLogStoreSqls {
}
因为kingbase 的语法和 ORACLE 差不多所以这里我们简单继承一下 OracleLogStoreSqls
,另外我们需要对META-INF.service 的 io.seata.core.store.db.sql.log.LogStoreSqls
进行编辑。
锁表类实现
LockStoreSql
@LoadLevel(name = "kingbase")
public class KingbaseLockStoreSql extends OracleLockStoreSql {
}
编辑文件 io.seata.core.store.db.sql.lock.LockStoreSql
通过这些改造,server 端已经可以开始支持kingbase 数据库了。
对kingbase 的支持,我们需要修改 nacos 对于数据库类型的相关配置。
参数 | 说明 |
---|---|
store.db.dbType | kingbase |
store.db.driverClassName | 驱动名称, |
store.db.url | jdbc:kingbase8://localhost:54321/SEATA |
store.db.driverClassName | com.kingbase8.Driver |
在 server 项目的pom.xml 中增加
<dependency>
<groupId>com.kingbase</groupId>
<artifactId>kingbase8</artifactId>
<version>8.2.0</version>
</dependency>
3. seata1.4.1 AT模式集成人大金仓(kingbase8)以及达梦数据库的支持
3.1 服务端的修改
(1)在core(seata-core)\src\main\resouces\META-INF.services 目录下的两个文件io.seata.core.store.db.sql.lock.LockStoreSql、io.seata.core.store.db.sql.log.LogStoreSqls 分别增加对金仓、达梦的支持。
(2)在core(seata-core)\src\main\java 目录下面增加io.seata.core.store.db.sql.lock.KingbaseLockStoreSql以及io.seata.core.store.db.sql.lock.DmLockStoreSql类,它们分别继承OracleLockStoreSql类。
(3)在core(seata-core)\src\main\java 目录下面增加io.seata.core.store.db.sql.log.KingBaseLogStoreSqls以及io.seata.core.store.db.sql.log.DmLogStoreSqls类,它们分别继承OracleLogStoreSqls类。
(4)修改core(seata-core)\src\main\java 目录下面的io.seata.core.constants.DBType类,增加对人大金仓以及达梦数据库的支持。
(5)修改core(seata-core)\src\main\java 目录下面的io.seata.core.store.db.AbstractDataSourceProvider类,增加对人大金仓以及达梦数据库的支持。
(6)core(seata-core)\src\main\java目录下面的io.seata.core.constants.ClientTableColumnsName类,将其里面的UNDO_LOG_CONTEXT字段的值修改为【context_】,跟数据库的表undo_log的字段context_保持一致。
3.2 客户端的修改
(1)在rm-datasource(seata-rm-datasource)\src\main\resouces\META-INF.services 目录下的5个文件io.seata.rm.datasource.exec.InsertExecutor、io.seata.rm.datasource.sql.struct.TableMetaCache、io.seata.rm.datasource.undo.KeywordChecker、io.seata.rm.datasource.undo.UndoExecutorHolder、io.seata.rm.datasource.undo.UndoLogManager 分别增加对金仓、达梦的支持
(2)InsertExecutor的扩展,在rm-datasource(seata-rm-datasource)\src\main\java 目录下面模仿io.seata.rm.datasource.exec.oracle.OracleInsertExecutor,分别增加io.seata.rm.datasource.exec.kingbase.KingbaseInsertExecutor类,io.seata.rm.datasource.exec.dm.DmInsertExecutor类。
(3)TableMetaCache的扩展,在rm-datasource(seata-rm-datasource)\src\main\java 目录下面模仿io.seata.rm.datasource.sql.struct.cache.OracleTableMetaCache,分别增加io.seata.rm.datasource.sql.struct.cache.KingbaseTableMetaCache类,io.seata.rm.datasource.sql.struct.cache.DmTableMetaCache类。
(4)KeywordChecker的扩展,在rm-datasource(seata-rm-datasource)\src\main\java 目录下面模仿io.seata.rm.datasource.sql.struct.cache.OracleTableMetaCache,分别增加io.seata.rm.datasource.sql.struct.cache.KingbaseTableMetaCache类,io.seata.rm.datasource.sql.struct.cache.DmTableMetaCache类。
(5)UndoExecutorHolder的扩展,在rm-datasource(seata-rm-datasource)\src\main\java 目录下面模仿io.seata.rm.datasource.undo.oracle.OracleUndoExecutorHolder,分别增加io.seata.rm.datasource.undo.kingbase.KingbaseUndoExecutorHolder类,io.seata.rm.datasource.undo.dm.DmUndoExecutorHolder类。
(6)UndoExecutorHolder的insert扩展,在rm-datasource(seata-rm-datasource)\src\main\java 目录下面模仿io.seata.rm.datasource.undo.oracle.OracleUndoInsertExecutor,分别增加io.seata.rm.datasource.undo.kingbase.KingbaseUndoInsertExecutor类,io.seata.rm.datasource.undo.dm.DmUndoInsertExecutor类。
(7)UndoExecutorHolder的update扩展,在rm-datasource(seata-rm-datasource)\src\main\java 目录下面模仿io.seata.rm.datasource.undo.oracle.OracleUndoUpdateExecutor,分别增加io.seata.rm.datasource.undo.kingbase.KingbaseUndoUpdateExecutor类,io.seata.rm.datasource.undo.dm.DmUndoUpdateExecutor类。
(8)UndoExecutorHolder的delete扩展,在rm-datasource(seata-rm-datasource)\src\main\java 目录下面模仿io.seata.rm.datasource.undo.oracle.OracleUndoDeleteExecutor,分别增加io.seata.rm.datasource.undo.kingbase.KingbaseUndoDeleteExecutor类,io.seata.rm.datasource.undo.dm.DmUndoDeleteExecutor类。
(9)UndoLogManager的扩展,在rm-datasource(seata-rm-datasource)\src\main\java 目录下面模仿io.seata.rm.datasource.undo.oracle.OracleUndoLogManager,分别增加io.seata.rm.datasource.undo.kingbase.KingbaseUndoLogManager类,io.seata.rm.datasource.undo.dm.DmUndoLogManager类。
(10)在sqlparser(seata-sqlparser)的子模块seata-sqlparser-druid\src\main\resouces\META-INF.services 目录下的io.seata.sqlparser.druid.SQLOperateRecognizerHolder 增加对金仓、达梦的支持
(11)SQLOperateRecognizerHolder的扩展,在sqlparser(seata-sqlparser)的子模块seata-sqlparser-druid\src\main\java 目录下面模仿io.seata.sqlparser.druid.oracle.OracleOperateRecognizerHolder,分别增加io.seata.sqlparser.druid.kingbase.KingbaseOperateRecognizerHolder类,io.seata.sqlparser.druid.dm.DmOperateRecognizerHolder类。
(12)SQLOperateRecognizerHolder的base扩展,在sqlparser(seata-sqlparser)的子模块seata-sqlparser-druid\src\main\java 目录下面模仿io.seata.sqlparser.druid.oracle.BaseOracleRecognizer,分别增加io.seata.sqlparser.druid.kingbase.BaseKingbaseRecognizer类,io.seata.sqlparser.druid.dm.BaseDmRecognizer类。
(13)SQLOperateRecognizerHolder的insert扩展,在sqlparser(seata-sqlparser)的子模块seata-sqlparser-druid\src\main\java 目录下面模仿io.seata.sqlparser.druid.oracle.OracleInsertRecognizer,分别增加io.seata.sqlparser.druid.kingbase.KingbaseInsertRecognizer类,io.seata.sqlparser.druid.dm.DmInsertRecognizer类。
(14)SQLOperateRecognizerHolder的update扩展,在sqlparser(seata-sqlparser)的子模块seata-sqlparser-druid\src\main\java 目录下面模仿io.seata.sqlparser.druid.oracle.OracleUpdateRecognizer,分别增加io.seata.sqlparser.druid.kingbase.KingbaseUpdateRecognizer类,io.seata.sqlparser.druid.dm.DmUpdateRecognizer类。
(15)SQLOperateRecognizerHolder的delete扩展,在sqlparser(seata-sqlparser)的子模块seata-sqlparser-druid\src\main\java 目录下面模仿io.seata.sqlparser.druid.oracle.OracleDeleteRecognizer,分别增加io.seata.sqlparser.druid.kingbase.KingbaseDeleteRecognizer类,io.seata.sqlparser.druid.dm.DmDeleteRecognizer类。
(16)SQLOperateRecognizerHolder的selectForUpdate扩展,在sqlparser(seata-sqlparser)的子模块seata-sqlparser-druid\src\main\java 目录下面模仿io.seata.sqlparser.druid.oracle.OracleSelectForUpdateRecognizer,分别增加io.seata.sqlparser.druid.kingbase.KingbaseSelectForUpdateRecognizer类,io.seata.sqlparser.druid.dm.DmSelectForUpdateRecognizer类。
4. 对微服务端jar包进行修改
在微服务端,我们使用的是打包后的 seat-all 的数据。
5. 其它扩展
5.1 对变更表结构(如增加字段)的扩展支持
seata中表结构会缓存一段时间,变更了表结构缓存还没有失效。导致不一致报错。默认配置下,表结构缓存为10多分钟能够恢复正常。
因此,需对源码进行修改。在rm-datasource(seata-rm-datasource)\src\main\java 目录下,对io.seata.rm.datasource.sql.struct.TableRecords类进行修改,如下图所示:
对buildRecords方法里面的相关代码进行微调,columnName以及dataType不再从缓存的表结构TableMeta获取,改从ResultSetMetaData中获取。
5.2 对多表关联更新的扩展支持
seata默认不支持多表关联更新,需对源码进行修改。
在rm-datasource(seata-rm-datasource)\src\main\java 目录下,对io.seata.rm.datasource.exec.UpdateExecutor类进行修改,如下图所示:
5.2.1 mysql数据库
在sqlparser(seata-sqlparser)的子模块seata-sqlparser-druid\src\main\java 目录下,对io.seata.sqlparser.druid.mysql.MySQLUpdateRecognizer类进行修改,如下图所示:
5.2.2 oracle、kingbase(人大金仓)以及达梦数据库
(1)在sqlparser(seata-sqlparser)的子模块seata-sqlparser-druid\src\main\java 目录下,对io.seata.sqlparser.druid.oracle.OracleUpdateRecognizer类进行修改,如下图所示:
(2)在sqlparser(seata-sqlparser)的子模块seata-sqlparser-druid\src\main\java 目录下,对io.seata.rm.datasource.undo.oracle.keyword.OracleKeywordChecker类进行修改,如下图所示:
5.2.3 多表关联更新SQL语句样例
(1)mysql数据库:
普通多表连接
update PRODUCT a,PRODUCT_DETAIL b set a.NAME_='测试seata', a.TOTAL_=b.ID_ where a.ID_=b.PRODUCT_ID_ and b.USER_NAME_='李四'
join table
update ORDERS a join ORDERS_DETAIL b on a.PRODUCT_ID_=b.ORDERS_ID_ set a.TOTAL_= 1998, a.ID_=12 where b.USER_NAME_='张三'
(2)oracle、kingbase(人大金仓)以及达梦数据库:
update PRODUCT a set a.NAME_='测试seata', a.TOTAL_=(select b.Id_ from PRODUCT_DETAIL b where a.ID_=b.PRODUCT_ID_ and b.USER_NAME_='李四'
where exists (select 1 from PRODUCT_DETAIL b where a.ID_=b.PRODUCT_ID_ and b.USER_NAME_='李四')