TableStructureManager.java 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. package com.bfkj.unidia.DataBaseUtils;
  2. import com.bfkj.unidia.Result;
  3. import com.github.benmanes.caffeine.cache.Cache;
  4. import com.github.benmanes.caffeine.cache.Caffeine;
  5. import com.zaxxer.hikari.HikariDataSource;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.stereotype.Component;
  10. import org.springframework.stereotype.Service;
  11. import java.math.BigDecimal;
  12. import java.sql.*;
  13. import java.time.LocalDate;
  14. import java.time.LocalDateTime;
  15. import java.util.*;
  16. import java.util.concurrent.*;
  17. import java.util.concurrent.locks.ReentrantLock;
  18. import static com.bfkj.unidia.cacheUtils.CacheUtil.buildCaffeineCache;
  19. /**
  20. * 表结构管理器,用于获取和缓存数据库表结构信息(多数据库兼容版)
  21. */
  22. @Service
  23. public class TableStructureManager {
  24. private static final Logger logger = LoggerFactory.getLogger(TableStructureManager.class);
  25. // 表结构缓存:数据源标识 -> 表名 -> 表结构信息
  26. private final Cache<String, ConcurrentMap<String, TableSchema>> schemaCache = buildCaffeineCache();
  27. // 数据库连接池管理器
  28. private final ConnectionPoolManager poolManager;
  29. // 数据库工具类
  30. private final DbUtils dbUtils;
  31. //数据命令执行工具(在此主要用于创建表和更新表结构)
  32. private final JdbcExecutor jdbcExecutor;
  33. @Autowired
  34. public TableStructureManager(ConnectionPoolManager poolManager,DbUtils dbUtils,
  35. JdbcExecutor jdbcExecutor){
  36. this.poolManager = poolManager;
  37. this.dbUtils = dbUtils;
  38. this.jdbcExecutor = jdbcExecutor;
  39. }
  40. // 表级锁管理器:保证同一时间只能有一个线程操作一张表
  41. private final ConcurrentMap<String, ReentrantLock> tableLocks = new ConcurrentHashMap<>();
  42. /**
  43. * 获取指定表的锁对象(如果不存在则创建)
  44. */
  45. public ReentrantLock getTableLock(String tableName) {
  46. return tableLocks.computeIfAbsent(tableName, k -> new ReentrantLock());
  47. }
  48. // 定义 ResultSet 列名常量
  49. private static final String COLUMN_NAME = "COLUMN_NAME";
  50. private static final String TYPE_NAME = "TYPE_NAME";
  51. private static final String DATA_TYPE = "DATA_TYPE";
  52. private static final String COLUMN_SIZE = "COLUMN_SIZE";
  53. private static final String NULLABLE = "NULLABLE";
  54. private static final String REMARKS = "REMARKS";
  55. private static final Set<String> ORACLE_TYPES = Set.of("oracle", "dm", "kingbase", "oceanbase");
  56. private static final Set<String> POSTGRESQL_TYPES = Set.of("postgresql", "gaussdb", "opengauss");
  57. private static final Set<String> SQLSERVER_TYPES = Set.of("sqlserver");
  58. private static final Set<String> SQLITE_TYPES = Set.of("sqlite");
  59. private static final Set<String> GOLDEN_DB_TYPES = Set.of("goldendb");
  60. /**
  61. * 获取表结构信息(线程安全、高性能)
  62. * @param dbConfig 数据库配置
  63. * @param tableName 表名
  64. * @return 表结构结果
  65. */
  66. public Result<TableSchema> getTableStructure(Map<String, String> dbConfig, String tableName) {
  67. Result<Map<String, String>> validation = dbUtils.validateConnection(dbConfig);
  68. if (!validation.isSuccess()) {
  69. return Result.fail(validation.getError());
  70. }
  71. Map<String, String> validationData = validation.getData();
  72. String dbKey = validationData.get("dbKey");
  73. String dbType = validationData.get("dbType");
  74. return getTableStructure(dbKey,dbType, dbConfig,tableName);
  75. }
  76. /**
  77. * 获取表结构信息(线程安全、高性能)
  78. *
  79. * @param dbConfig 数据库配置
  80. * @param tableName 表名
  81. * @return 表结构结果
  82. */
  83. public Result<TableSchema> getTableStructure(String dbKey,String dbType, Map<String, String> dbConfig, String tableName) {
  84. try {
  85. // 第一次尝试从缓存中获取
  86. ConcurrentMap<String, TableSchema> tableSchemas = schemaCache.getIfPresent(dbKey);
  87. if (tableSchemas != null) {
  88. TableSchema schema = tableSchemas.get(tableName);
  89. if (schema != null) {
  90. return Result.success(schema);
  91. }
  92. }
  93. // 缓存未命中,同步加载并更新缓存
  94. synchronized (this) {
  95. // 再次检查缓存(双重检查锁)
  96. tableSchemas = schemaCache.getIfPresent(dbKey);
  97. if (tableSchemas == null) {
  98. tableSchemas = new ConcurrentHashMap<>();
  99. schemaCache.put(dbKey, tableSchemas);
  100. }
  101. TableSchema schema = tableSchemas.get(tableName);
  102. if (schema != null) {
  103. return Result.success(schema);
  104. }
  105. // 获取数据源
  106. Result<HikariDataSource> dataSourceResult = poolManager.getDataSource(dbConfig);
  107. if (!dataSourceResult.isSuccess()) {
  108. return Result.fail(dataSourceResult.getError());
  109. }
  110. HikariDataSource dataSource = dataSourceResult.getData();
  111. // 真正加载
  112. Result<TableSchema> result = loadTableStructure(dataSource, tableName, dbType);
  113. if (result.isSuccess()) {
  114. tableSchemas.put(tableName, result.getData());
  115. }
  116. return result;
  117. }
  118. } catch (Exception e) {
  119. logger.error("获取表结构失败,dbKey: {}, tableName: {}", dbKey, tableName, e);
  120. return Result.fail("获取表结构失败: 系统错误");
  121. }
  122. }
  123. /**
  124. * 实际获取表结构的核心方法
  125. *
  126. * @param dataSource 数据库配置参数集合,包含连接池所需所有配置项
  127. * @param tableName 需要获取结构的数据库表名称
  128. * @return 包含表结构信息的Result对象,失败时返回错误信息
  129. */
  130. private Result<TableSchema> loadTableStructure(HikariDataSource dataSource, String tableName, String dbType) {
  131. try (Connection connection = dataSource.getConnection()) {
  132. DatabaseMetaData metaData = connection.getMetaData();
  133. String catalog = getCatalog(dbType, connection);
  134. String schema = getSchema(dbType, connection);
  135. try (ResultSet columns = metaData.getColumns(catalog, schema, tableName, "%")) {
  136. LinkedHashMap<String, ColumnInfo> columnMap = new LinkedHashMap<>();
  137. while (columns.next()) {
  138. String columnName = columns.getString(COLUMN_NAME);
  139. String columnType = columns.getString(TYPE_NAME);
  140. int dataType = columns.getInt(DATA_TYPE);
  141. int columnSize = columns.getInt(COLUMN_SIZE);
  142. boolean nullable = columns.getBoolean(NULLABLE);
  143. String remarks = columns.getString(REMARKS);
  144. columnMap.put(columnName, createColumnInfo(columnName, columnType, dataType, columnSize, nullable, remarks));
  145. }
  146. return Result.success(new TableSchema(tableName, columnMap));
  147. } catch (SQLException e) {
  148. // 记录详细日志(示例)
  149. logger.error("获取表结构失败: {}", e.getMessage(), e);
  150. return Result.fail("获取表结构失败: ", e);
  151. }
  152. } catch (Exception e) {
  153. // 记录非 SQL 异常日志(示例)
  154. logger.error("非预期异常: {}", e.getMessage(), e);
  155. return Result.fail("获取表结构失败: ", e);
  156. }
  157. }
  158. public List<String> getColumns(Map<String, String> dbConfig,String tableName){
  159. Result<TableSchema> tableSchemaResult = getTableStructure(dbConfig, tableName);
  160. if(!tableSchemaResult.isSuccess()){
  161. return null;
  162. }
  163. return tableSchemaResult.getData().getColumns().keySet().stream().toList();
  164. }
  165. // 提取为独立方法
  166. private ColumnInfo createColumnInfo(String columnName, String columnType, int dataType, int columnSize, boolean nullable, String remarks) {
  167. return new ColumnInfo(columnName, columnType, dataType, columnSize, nullable, remarks);
  168. }
  169. private static final Set<String> SUPPORTED_DB_TYPES = Set.of(
  170. "mysql", "polardb", "tidb", "goldendb", "tdsql",
  171. "sqlserver", "kingbase", "gaussdb", "opengauss", "gbase"
  172. );
  173. private String getCatalog(String dbType, Connection connection) {
  174. if (dbType == null) {
  175. return null;
  176. }
  177. try {
  178. String catalog = SUPPORTED_DB_TYPES.contains(dbType) ? connection.getCatalog() : null;
  179. if ("oceanbase".equalsIgnoreCase(dbType)) {
  180. try (Statement stmt = connection.createStatement();
  181. ResultSet rs = stmt.executeQuery("SELECT tenant_name FROM oceanbase.__all_virtual_tenant LIMIT 1")) {
  182. if (rs.next()) {
  183. catalog = rs.getString(1);
  184. } else {
  185. logger.warn("OceanBase查询成功但未找到租户信息,使用默认租户sys");
  186. catalog = "sys";
  187. }
  188. } catch (SQLException e) {
  189. logger.warn("OceanBase查询异常,使用默认租户sys", e);
  190. catalog = "sys";
  191. }
  192. }
  193. return catalog;
  194. } catch (SQLException e) {
  195. return null;
  196. }
  197. }
  198. private static final Set<String> PUBLIC_SCHEMA_TYPES = Set.of("kingbase", "gaussdb", "opengauss", "gbase");
  199. private String getSchema(String dbType, Connection connection) {
  200. if (dbType == null || connection == null) {
  201. // 可选:抛出IllegalArgumentException以强制约束输入
  202. return null;
  203. }
  204. try {
  205. if (dbType.equals("sqlserver")) {
  206. return "dbo";
  207. } else if ("oracle".equals(dbType) || "dm".equals(dbType)) {
  208. return connection.getMetaData().getUserName().toUpperCase();
  209. } else if (PUBLIC_SCHEMA_TYPES.contains(dbType)) {
  210. return "public";
  211. } else if (dbType.equals("oceanbase")) {
  212. return connection.getCatalog();
  213. } else {
  214. // 可选:记录未知dbType以辅助调试
  215. return null;
  216. }
  217. } catch (SQLException e) {
  218. // 建议记录日志,避免静默失败
  219. logger.info("Failed to get schema: " + e.getMessage());
  220. return null;
  221. }
  222. }
  223. /**
  224. * 创建表(如果不存在)并自动添加缺失字段
  225. */
  226. public Result<Integer> createTableIfNotExists(Map<String, String> dbConfig,
  227. String tableName,
  228. Map<String, Object> executeParam) {
  229. Result<TableSchema> schemaResult = getTableStructure(dbConfig, tableName);
  230. if (schemaResult.isSuccess() && !schemaResult.getData().getColumns().isEmpty()) {
  231. // 表已存在,尝试添加缺失字段
  232. return addMissingColumns(dbConfig, tableName, executeParam);
  233. }
  234. // 表不存在,创建新表
  235. Result<Integer> createResult = createTable(dbConfig, tableName, executeParam);
  236. if (createResult.isSuccess()) {
  237. return createResult;
  238. }
  239. // 创建失败可能是并发创建导致,检查是否存在后重试
  240. Result<TableSchema> retrySchema = getTableStructure(dbConfig, tableName);
  241. if (retrySchema.isSuccess() && !retrySchema.getData().getColumns().isEmpty()) {
  242. return addMissingColumns(dbConfig, tableName, executeParam);
  243. }
  244. return createResult;
  245. }
  246. public Result<Integer> createTable(Map<String, String> dbConfig,
  247. String tableName,
  248. Map<String, Object> executeParam) {
  249. ReentrantLock lock = getTableLock(tableName);
  250. lock.lock();
  251. try {
  252. Result<Map<String, String>> validateResult = dbUtils.validateConnection(dbConfig);
  253. if (!validateResult.isSuccess()) {
  254. return Result.fail(validateResult.getError());
  255. }
  256. String dbType = validateResult.getData().get("dbType");
  257. // 构建字段列表
  258. List<String> columns = buildColumnDefinitions(executeParam, dbType,tableName);
  259. // 构建并执行建表语句
  260. String sqlTemplate = getCreateTableTemplate(dbType);
  261. String sql = String.format(sqlTemplate, tableName, String.join(", ", columns));
  262. Result<Integer> result = jdbcExecutor.executeDDL(dbConfig, sql);
  263. if (result.isSuccess()) {
  264. clearTableCache(dbConfig, tableName);
  265. }
  266. return result;
  267. } catch (Exception e) {
  268. logger.error("创建表失败", e);
  269. return Result.fail("创建表失败: " + e.getMessage());
  270. } finally {
  271. lock.unlock();
  272. }
  273. }
  274. /**
  275. * 添加缺失字段(适用于表已存在的情况)
  276. */
  277. public Result<Integer> addMissingColumns(Map<String, String> dbConfig,
  278. String tableName,
  279. Map<String, Object> executeParam) {
  280. Result<TableSchema> schemaResult = getTableStructure(dbConfig, tableName);
  281. if (!schemaResult.isSuccess()) {
  282. return Result.fail(schemaResult.getError());
  283. }
  284. // 筛选新字段
  285. Map<String, Object> newColumns = filterNewColumns(executeParam, schemaResult.getData());
  286. if (newColumns.isEmpty()) {
  287. return Result.fail("所有字段已存在");
  288. }
  289. // 添加字段
  290. return addColumns(dbConfig, tableName, newColumns);
  291. }
  292. /**
  293. * 构建字段定义列表
  294. */
  295. private List<String> buildColumnDefinitions(Map<String, Object> executeParam,
  296. String dbType,String tableName) {
  297. List<String> columns = new ArrayList<>();
  298. for (Map.Entry<String, Object> entry : executeParam.entrySet()) {
  299. String colName = entry.getKey();
  300. String colType = convertJavaTypeToSqlType(entry.getValue().getClass(), dbType);
  301. // 自动添加主键定义
  302. if (isPrimaryKey(colName, tableName)) {
  303. colType += " PRIMARY KEY ";
  304. }
  305. columns.add(colName + " " + colType);
  306. }
  307. return columns;
  308. }
  309. private String getCreateTableTemplate(String dbType) {
  310. return switch (dbType) {
  311. case "sqlserver" ->
  312. "IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'%s') AND type in (N'U')) CREATE TABLE %s (%s)";
  313. case "oracle" ->
  314. "BEGIN EXECUTE IMMEDIATE 'CREATE TABLE %s (%s)'; EXCEPTION WHEN OTHERS THEN IF SQLCODE != -955 THEN RAISE; END IF; END;";
  315. case "dm" ->
  316. "DECLARE e_table_not_exist EXCEPTION; PRAGMA EXCEPTION_INIT(e_table_not_exist, -20071); BEGIN EXECUTE IMMEDIATE 'CREATE TABLE %s (%s)'; EXCEPTION WHEN e_table_not_exist THEN NULL; END;";
  317. default -> "CREATE TABLE IF NOT EXISTS %s (%s)";
  318. };
  319. }
  320. private boolean isPrimaryKey(String colName, String tableName) {
  321. return colName.equalsIgnoreCase(tableName + "_code") ||
  322. colName.equalsIgnoreCase("_id") ||
  323. colName.equalsIgnoreCase("id");
  324. }
  325. /**
  326. * 添加多个字段(支持批量执行以提升性能)
  327. */
  328. public Result<Integer> addColumns(Map<String, String> dbConfig, String tableName, Map<String, Object> columns) {
  329. ReentrantLock lock = getTableLock(tableName);
  330. lock.lock();
  331. try {
  332. // 2. 获取当前表结构
  333. Result<TableStructureManager.TableSchema> schemaResult = getTableStructure(dbConfig, tableName);
  334. if (!schemaResult.isSuccess()) {
  335. return Result.fail(schemaResult.getError());
  336. }
  337. // 3. 筛选新字段
  338. Map<String, Object> newColumns = filterNewColumns(columns, schemaResult.getData());
  339. if (newColumns.isEmpty()) {
  340. return Result.fail("所有字段已存在");
  341. }
  342. String dbType = dbUtils.getDatabaseType(dbConfig.get("url"));
  343. // 4. 构建ALTER语句
  344. List<String> alterStatements = buildAlterStatements(dbType, tableName, newColumns);
  345. // 5. 根据数据库类型选择执行方式
  346. int totalAffectedRows;
  347. if (Set.of("mysql","mariadb","polardb","tidb","gaussdb","sqlite").contains(dbType)) {
  348. String combinedSql = combineAlterStatements(tableName, alterStatements, dbType);
  349. Result<Integer> result = jdbcExecutor.executeDDL(dbConfig, combinedSql);
  350. if (!result.isSuccess()) {
  351. return result;
  352. }
  353. totalAffectedRows = result.getData();
  354. } else {
  355. Result<Integer> singleResult = executeSingleAlters(dbConfig, alterStatements);
  356. if (singleResult.isSuccess()) {
  357. totalAffectedRows = singleResult.getData();
  358. } else {
  359. return Result.fail(singleResult.getError());
  360. }
  361. }
  362. // 6. 清除缓存以便下次重新加载表结构
  363. clearTableCache(dbConfig, tableName);
  364. return Result.success(totalAffectedRows);
  365. } catch (Exception e) {
  366. logger.error("添加字段失败", e);
  367. return Result.fail("添加字段失败: " + e.getMessage());
  368. }finally {
  369. lock.unlock();
  370. }
  371. }
  372. /**
  373. * 筛选出不存在的新字段
  374. */
  375. public Map<String, Object> filterNewColumns(
  376. Map<String, Object> columns,
  377. TableStructureManager.TableSchema schema) {
  378. Map<String, Object> newColumns = new HashMap<>();
  379. for (Map.Entry<String, Object> entry : columns.entrySet()) {
  380. String columnName = entry.getKey();
  381. if (!schema.getColumns().containsKey(columnName.toLowerCase())) {
  382. newColumns.put(columnName, entry.getValue());
  383. } else {
  384. logger.warn("字段 [{}] 已存在,跳过添加", columnName);
  385. }
  386. }
  387. return newColumns;
  388. }
  389. /**
  390. * 构建 ALTER TABLE ADD COLUMN 语句列表
  391. */
  392. private List<String> buildAlterStatements(String dbType, String tableName, Map<String, Object> columns) {
  393. List<String> statements = new ArrayList<>();
  394. for (Map.Entry<String, Object> entry : columns.entrySet()) {
  395. String quotedTableName = dbUtils.quoteIdentifier(tableName, dbType);
  396. String quotedColumnName = dbUtils.quoteIdentifier(entry.getKey(), dbType);
  397. String sql = "ALTER TABLE " + quotedTableName + " ADD COLUMN " + quotedColumnName + " " + entry.getValue();
  398. statements.add(sql);
  399. }
  400. return statements;
  401. }
  402. /**
  403. * 合并多条 ALTER TABLE 语句为一条 SQL
  404. */
  405. private String combineAlterStatements(String tableName, List<String> alterStatements, String dbType) {
  406. StringBuilder combined = new StringBuilder();
  407. String quotedTableName = dbUtils.quoteIdentifier(tableName, dbType);
  408. combined.append("ALTER TABLE ").append(quotedTableName).append(" ");
  409. // 提取每个语句中的 ADD COLUMN 子句
  410. List<String> addColumnClauses = new ArrayList<>();
  411. for (String stmt : alterStatements) {
  412. String clause = stmt.substring(stmt.indexOf("ADD COLUMN"));
  413. addColumnClauses.add(clause);
  414. }
  415. combined.append(String.join(", ", addColumnClauses));
  416. return combined.toString();
  417. }
  418. /**
  419. * 逐条执行 ADD COLUMN 操作
  420. */
  421. private Result<Integer> executeSingleAlters(Map<String, String> dbConfig, List<String> alterStatements) {
  422. int totalAffectedRows = 0;
  423. for (String sql : alterStatements) {
  424. Result<Integer> result = jdbcExecutor.executeDDL(dbConfig, sql);
  425. if (!result.isSuccess()) {
  426. logger.error("执行DDL失败: " + result.getError());
  427. //return Result.fail(result.getError()).getData(); // 返回错误结果
  428. continue;
  429. }
  430. totalAffectedRows += result.getData();
  431. }
  432. if(totalAffectedRows == 0){
  433. return Result.fail("所有字段已存在");
  434. }
  435. return Result.success(totalAffectedRows);
  436. }
  437. /**
  438. * 清除特定表的缓存
  439. */
  440. public void clearTableCache(Map<String, String> dbConfig, String tableName) {
  441. try {
  442. String cacheKey = dbUtils.generateCacheKey(dbConfig, null);
  443. // 第一次尝试从缓存中获取
  444. ConcurrentMap<String, TableSchema> tableSchemas = schemaCache.getIfPresent(cacheKey);
  445. if (tableSchemas != null) {
  446. tableSchemas.remove(tableName);
  447. }
  448. // 缓存未命中,同步加载并更新缓存
  449. synchronized (this) {
  450. // 再次检查缓存(双重检查锁)
  451. tableSchemas = schemaCache.getIfPresent(cacheKey);
  452. if (tableSchemas != null) {
  453. tableSchemas.remove(tableName);
  454. }
  455. }
  456. } catch (Exception e) {
  457. logger.error("清除表结构失败", e);
  458. }
  459. }
  460. /**
  461. * 将Java类型转换为数据库支持的字段类型
  462. *
  463. * @param javaType Java类型(如String.class, Integer.class等)
  464. * @param dbType 数据库连接URL(用于识别数据库类型)
  465. * @return 数据库字段类型字符串(如VARCHAR, INTEGER等)
  466. * @throws UnsupportedOperationException 不支持的Java类型或数据库类型
  467. */
  468. public String convertJavaTypeToSqlType(Class<?> javaType, String dbType) {
  469. if (javaType == null || dbType == null) {
  470. throw new IllegalArgumentException("参数不能为空");
  471. }
  472. if (javaType == String.class) {
  473. if (ORACLE_TYPES.contains(dbType)) return "VARCHAR2(255)";
  474. if (SQLITE_TYPES.contains(dbType)) return "TEXT";
  475. if (SQLSERVER_TYPES.contains(dbType)) return "NVARCHAR(MAX)";
  476. if (GOLDEN_DB_TYPES.contains(dbType)) return "VARCHAR(4096)";
  477. return "VARCHAR(255)";
  478. } else if (javaType == Integer.class || javaType == int.class) {
  479. if (ORACLE_TYPES.contains(dbType)) return "NUMBER(11)";
  480. if (POSTGRESQL_TYPES.contains(dbType) || SQLITE_TYPES.contains(dbType)) return "INTEGER";
  481. return "INT";
  482. } else if (javaType == Long.class || javaType == long.class) {
  483. if (ORACLE_TYPES.contains(dbType)) return "NUMBER(19)";
  484. return "BIGINT";
  485. } else if (javaType == Double.class || javaType == double.class) {
  486. if (ORACLE_TYPES.contains(dbType)) return "NUMBER(38,10)";
  487. if (SQLSERVER_TYPES.contains(dbType)) return "DECIMAL(38, 10)";
  488. if (SQLITE_TYPES.contains(dbType)) return "REAL";
  489. return "DECIMAL";
  490. } else if (javaType == LocalDate.class) {
  491. return "DATE";
  492. } else if (javaType == LocalDateTime.class) {
  493. if (Set.of("oracle", "dm", "kingbase", "oceanbase",
  494. "postgresql", "gaussdb", "opengauss",
  495. "polardb", "tidb").contains(dbType)) {
  496. return "TIMESTAMP";
  497. } else if (SQLSERVER_TYPES.contains(dbType)) {
  498. return "DATETIME2";
  499. }
  500. return "DATETIME";
  501. } else if (javaType == BigDecimal.class) {
  502. if (ORACLE_TYPES.contains(dbType)) return "NUMBER(38,10)";
  503. if (SQLSERVER_TYPES.contains(dbType)) return "DECIMAL(38, 10)";
  504. if (SQLITE_TYPES.contains(dbType)) return "REAL";
  505. return "DECIMAL";
  506. } else if (javaType == byte[].class) {
  507. if (ORACLE_TYPES.contains(dbType) || SQLITE_TYPES.contains(dbType)) {
  508. return "BLOB";
  509. } else if (POSTGRESQL_TYPES.contains(dbType)) {
  510. return "BYTEA";
  511. } else if (SQLSERVER_TYPES.contains(dbType)) {
  512. return "VARBINARY(MAX)";
  513. }
  514. return "LONGBLOB";
  515. }
  516. throw new UnsupportedOperationException("不支持的Java类型: " + javaType.getName());
  517. }
  518. /**
  519. * 表结构封装类
  520. */
  521. public record TableSchema(String name, Map<String, ColumnInfo> columns) {
  522. public Map<String, ColumnInfo> getColumns() {
  523. return Collections.unmodifiableMap(columns);
  524. }
  525. }
  526. /**
  527. * 列信息封装类
  528. */
  529. public record ColumnInfo(String name, String type,
  530. int dataType, int size,
  531. boolean nullable, String remarks) {
  532. }
  533. }