news 2026/10/17 13:04:24

达梦数据库表结构读取:JDBC结合系统视图实现数据字典自动化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
达梦数据库表结构读取:JDBC结合系统视图实现数据字典自动化

前阵子接了个数据迁移的活儿,需要把一套业务系统的上百张表结构完整摸清楚,还要生成能归档的数据字典。一开始用达梦自带的数据库管理工具倒也能导出,可表一多、频率一高,工具手动操作那套根本扛不住,导出来的格式也五花八门,没法自动做环境之间的结构对比。后来我干脆写了一套通用代码,直接基于达梦数据库(DM8)的JDBC驱动和系统视图把表结构读出来,表、字段、类型、精度、注释、主键、默认值全部解析成结构化数据,再按需输出成文档或者用于代码生成。

这套东西跑通之后省了不少事,中间也踩了不少坑,比如注释读不到、模式大小写不匹配、系统表混进来等问题。这篇文章把实现思路、完整步骤以及排查过程都记录下来,给准备做同样事情的同学一个可以直接参考的方案,不管你是要做数据字典、生成实体类,还是做多环境表结构对比,应该都用得上。

1. 项目背景与整体设计思路

1.1 为什么不用现成工具,要自己写代码

很多人第一反应是,达梦不是自带管理工具吗,右键表生成SQL脚本不就行了?确实可以,而且达梦也支持模式导出、表结构脚本导出这些功能。但真实项目里有两个问题绕不过去。

第一个是自动化问题。数据字典不是导一次就完事,开发环境、测试环境会频繁变更表结构,文档需要定期同步。还有人会拿两个环境的表结构做差异对比,找一个字段新增或长度变化。这些场景都要求程序能定时、批量去调用,管理工具这种手动操作没法纳入流水线。

第二个是定制化输出问题。我要的不是建表SQL,而是结构化的元数据:字段名、字段类型、长度、精度、小数位、是否可空、默认值、注释、主键顺序。建表SQL虽然信息完整,但还得做二次解析,文本解析非常容易出错。而JDBC的元数据接口和数据字典视图能直接以结构化表格返回,拿到的就是干净的字段清单。

所以结论很明确:与其写一堆SQL解析脚本,不如直接用标准接口去读元数据。这套代码不绑定某个具体导出场景,后续要生成实体类、生成Markdown文档、做结构对比,都复用同一份读取结果。

1.2 通用代码的定位:一次读取,多库兼容

标题里强调“通用代码”,意思不是只针对达梦写死一套逻辑,而是上层用统一的数据模型收口,数据库差异只隔离在读取层。

具体来说,我设计的时候把表结构抽象成这么两样东西:

  • TableInfo:表名、模式、类型(表/视图)、表注释、主键列清单、字段列表
  • ColumnInfo:字段名、字段序号、类型、长度、精度、小数位、是否可空、默认值、注释、是否自增

后面所有功能都围绕这两个模型来写。这样换数据库时,只需要在读取层做适配:MySQL有INFORMATION_SCHEMA,Oracle和达梦有ALL_TAB_COLUMNS这组视图,PostgreSQL有pg_catalog。只要能把这些不同来源的数据填进同一个模型,上层逻辑完全不用改动。

这也是为什么我建议不要把类型做成固定枚举。数据库之间的类型差异太大了,VARCHAR2、VARCHAR、TEXT、LONG VARCHAR、NVARCHAR2,硬枚举会把代码锁死,用字符串原样保存才是最稳的。

1.3 技术方案选型:标准JDBC接口还是系统视图

实际操作中有两条路可以拿到达梦的表结构:

一条是JDBC标准接口DatabaseMetaData,里面的getTables、getColumns、getPrimaryKeys就是专门干这个的,理论上所有数据库通用。另一条是直接查达梦的数据字典视图,比如ALL_TABLES、ALL_TAB_COLUMNS、ALL_COL_COMMENTS、ALL_CONSTRAINTS。

我一开始想偷懒,全用JDBC标准接口,毕竟代码最干净。结果实测发现,达梦的JDBC驱动对getTables和getColumns支持还可以,但有些列的返回值会“残缺”,最典型的就是列注释REMARKS经常读不到,主键信息也得看驱动版本。这直接导致我用JDBC拿回来的数据缺胳膊少腿。

然后我补查了系统视图,发现注释、约束、自增这些信息还是得靠数据字典视图才稳。两种方案的优缺点对比如下:

维度标准JDBC元数据接口数据字典视图
跨数据库通用性高低,依赖达梦Oracle兼容模式
表、字段、类型信息基本可用很完整
注释信息部分版本为空完整
主键与约束依赖驱动实现完整
自增列有IS_AUTOINCREMENT列但版本差异大部分版本可用系统视图补齐
代码简洁度高需要写SQL

所以说,完整方案不是二选一,而是“JDBC拿骨架 + 系统视图补细节”。先用JDBC把表清单和字段清单拉出来,再用系统视图把注释、主键、默认值这些细节填上。这样既保留了通用性,又能拿到完整信息。

2. 达梦数据库表结构信息藏在哪

2.1 核心数据字典视图:ALL_TABLES与ALL_TAB_COLUMNS

达梦数据库在Oracle兼容模式下,系统视图和Oracle非常像,这也是很多老Oracle开发能快速上手达梦的原因。读取表结构最核心的几张视图是ALL_TABLES和ALL_TAB_COLUMNS。

ALL_TABLES 里记录当前用户能访问的所有表,常用字段有OWNER(模式名)、TABLE_NAME(表名)、TABLESPACE_NAME(表空间)、STATUS(状态)。如果只想看某模式下的表,用OWNER过滤就行。

ALL_TAB_COLUMNS 是字段信息的主力视图,每条记录对应表里的一个字段,关键字段如下:

  • OWNER、TABLE_NAME:定位是哪张表的哪个字段
  • COLUMN_ID:字段序号,从1开始,排序就靠它
  • COLUMN_NAME:字段名
  • DATA_TYPE:字段类型,比如VARCHAR2、CHAR、DECIMAL、INTEGER、DATE、TIMESTAMP
  • DATA_LENGTH:字段长度,这个值在不同字符集下含义不一样,要注意
  • DATA_PRECISION、DATA_SCALE:精度和小数位,DECIMAL/NUMBER类型用得上
  • NULLABLE:是否允许为空,值为Y或N

实际查询的SQL是这样的:

SELECT TABLE_NAME, COLUMN_ID, COLUMN_NAME, DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE, NULLABLE FROM ALL_TAB_COLUMNS WHERE OWNER = UPPER('test') ORDER BY TABLE_NAME, COLUMN_ID;

这里有个非常容易踩的坑:DATA_LENGTH在数据库里通常存的是字节长度,不是字符长度。比如一个字段定义成VARCHAR2(100),如果数据库字符集是UTF-8,DATA_LENGTH显示的可能就是400;如果字符集是GBK,一个汉字占2个字节,显示可能在200左右浮动。所以专门做文档时,宁可先用DATA_LENGTH做展示,再人工校对,也别理所当然地以为它就是“字符数”。

2.2 表注释与列注释:ALL_TAB_COMMENTS和ALL_COL_COMMENTS

达梦的注释信息和字段信息是分开存放的,这一点和Oracle完全一致。表注释在ALL_TAB_COMMENTS里,列注释在ALL_COL_COMMENTS里。

-- 表注释 SELECT TABLE_NAME, COMMENTS FROM ALL_TAB_COMMENTS WHERE OWNER = UPPER('test'); -- 列注释 SELECT TABLE_NAME, COLUMN_NAME, COMMENTS FROM ALL_COL_COMMENTS WHERE OWNER = UPPER('test');

我之所以专门提这两张视图,是因为它们在实操中的地位太重要了。很多项目要求数据字典必须包含中文注释,但JDBC的getColumns返回的REMARKS字段在达梦驱动上经常是空的,最后只能靠ALL_COL_COMMENTS来补。

我在程序里的做法是,把这两张视图一次查出来,放进Map<表名+字段名, 注释>,再和JDBC拿到的字段模型做合并。千万别说一条一条查,几百张表可能几千个字段,性能完全不一样。

2.3 主键、唯一约束与IDENTITY自增列

主键是表结构里绕不开的东西,生成实体类、做数据对比、生成DDL都依赖它。达梦里的主键信息不像MySQL的INFORMATION_SCHEMA那样直接放一张表里,而是分布在ALL_CONSTRAINTS和ALL_CONS_COLUMNS两张视图里。

ALL_CONSTRAINTS存约束主信息,包括约束名、约束类型、所属表、状态。约束类型用CONSTRAINT_TYPE表示,P是主键,U是唯一约束,C是检查约束,R是外键。ALL_CONS_COLUMNS存约束涉及的具体字段,以及字段在约束中的顺序POSITION。

查主键的SQL如下:

SELECT a.CONSTRAINT_NAME, a.TABLE_NAME, b.COLUMN_NAME, b.POSITION FROM ALL_CONSTRAINTS a JOIN ALL_CONS_COLUMNS b ON a.CONSTRAINT_NAME = b.CONSTRAINT_NAME AND a.OWNER = b.OWNER WHERE a.OWNER = UPPER('test') AND a.CONSTRAINT_TYPE = 'P' ORDER BY a.TABLE_NAME, b.POSITION;

主键字段的顺序很重要,一定要按POSITION排序,不然生成实体类里复合主键的顺序就乱了。

至于IDENTITY自增列,达梦的不同版本表现差异比较明显。一部分场景下可以通过JDBC的getColumns结果集里的IS_AUTOINCREMENT列来判断,但这个字段的标准性存疑;另一部分场景则建议直接看建表DDL,或者用DBA级别的视图去确认。我的建议是,如果只是做数据字典,自增列不是关键信息,标注成可选扩展就行,别让它卡住主流程。

3. 实操过程:从零实现一套可用的读取器

3.1 环境准备:驱动、连接参数、本地依赖安装

在写代码之前,先把达梦JDBC驱动搞定。达梦8的JDBC驱动一般叫DmJdbcDriver18.jar,适用于JDK1.8及以上。如果你是连现成的达梦环境,直接找管理员要一份驱动包即可。如果本机安装了达梦数据库,驱动一般在安装目录下的drivers/jdbc文件夹里。

拿到jar之后,建议用maven命令安装到本地仓库,这样项目里直接引用坐标就行,不用维护一整个lib目录:

mvn install:install-file -Dfile=DmJdbcDriver18.jar -DgroupId=com.dameng -DartifactId=DmJdbcDriver18 -Dversion=8.1.2.192 -Dpackaging=jar

然后pom里加依赖:

<dependency> <groupId>com.dameng</groupId> <artifactId>DmJdbcDriver18</artifactId> <version>8.1.2.192</version> </dependency>

连接参数方面,达梦默认端口是5236,JDBC URL格式如下:

jdbc:dm://127.0.0.1:5236

很多人在这里会有一个误解:JDBC URL没带schema,所以连接成功之后就认为直接能查到表。实际上,你不指定schema也能连上数据库,但在读元数据时必须显式传schema参数,否则很多接口会默认按当前登录用户过滤,查出来的结果往往不是你想要的。

3.2 定义统一数据模型:TableInfo和ColumnInfo

我前面说过,所有数据库差异在读取层消化,上层统一用模型。两个Java类的设计如下:

public class TableInfo { private String schema; // 模式名 private String tableName; // 表名 private String tableType; // TABLE / VIEW private String comment; // 表注释 private List<ColumnInfo> columns; // 字段列表 private List<String> primaryKeyColumns; // 主键字段,按顺序 } public class ColumnInfo { private String columnName; // 字段名 private int position; // 序号 private String dataType; // 原始类型名,如 VARCHAR2 private int length; // 长度 private Integer precision; // 精度 private Integer scale; // 小数位 private boolean nullable; // 是否可空 private String defaultValue; // 默认值 private String comment; // 字段注释 private boolean autoIncrement; // 是否自增 }

我用Integer而不是int来表示精度和小数位,因为不是所有类型都有这两个概念,比如DATE类型读出来可能为null。用包装类型能避免很多判断上的麻烦。

3.3 基于JDBC DatabaseMetaData的通用读取实现

先看主流程,我直接读取所有表和字段:

public List<TableInfo> readByJdbcMetaData(Connection conn, String schema) throws SQLException { List<TableInfo> tables = new ArrayList<>(); DatabaseMetaData meta = conn.getMetaData(); try (ResultSet rs = meta.getTables(null, schema, "%", new String[]{"TABLE", "VIEW"})) { while (rs.next()) { TableInfo table = new TableInfo(); table.setSchema(rs.getString("TABLE_SCHEM")); table.setTableName(rs.getString("TABLE_NAME")); table.setTableType(rs.getString("TABLE_TYPE")); List<ColumnInfo> columns = new ArrayList<>(); try (ResultSet cols = meta.getColumns(null, schema, table.getTableName(), "%")) { while (cols.next()) { ColumnInfo col = new ColumnInfo(); col.setColumnName(cols.getString("COLUMN_NAME")); col.setPosition(cols.getInt("ORDINAL_POSITION")); col.setDataType(cols.getString("TYPE_NAME")); col.setLength(cols.getInt("COLUMN_SIZE")); col.setPrecision(cols.getInt("COLUMN_SIZE")); col.setScale(cols.getInt("DECIMAL_DIGITS")); col.setNullable(cols.getInt("NULLABLE") != DatabaseMetaData.columnNoNulls); col.setDefaultValue(cols.getString("COLUMN_DEF")); columns.add(col); } } table.setColumns(columns); tables.add(table); } } return tables; }

这里有几个细节值得说:

  • getTables的第一个参数是catalog,达梦驱动基本无视它,传null就行,真正起作用的是第二个参数schema。
  • schema必须是实际模式名,而且通常是大写。如果输错了,结果集为空,但不会报异常,排查起来很费劲。
  • COLUMN_SIZE对于VARCHAR2是字符长度,对于NUMBER则是精度,在达梦驱动下表现不太一致,所以我在填充length和precision时都先取COLUMN_SIZE,具体语义再根据类型微调。

这套逻辑最大的好处是不依赖任何数据库特有的SQL,MySQL、Oracle、PostgreSQL理论上都能用。局限性也很明显:注释经常拿不到,默认值也可能为空。这就是我为什么还要再走一遍系统视图。

3.4 基于达梦系统视图的增强读取实现

系统视图部分,核心是补全三块信息。第一是表注释:

public Map<String, String> readTableComments(Connection conn, String schema) throws SQLException { Map<String, String> map = new HashMap<>(); String sql = "SELECT TABLE_NAME, COMMENTS FROM ALL_TAB_COMMENTS WHERE OWNER = ?"; try (PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, schema.toUpperCase()); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { map.put(rs.getString("TABLE_NAME"), rs.getString("COMMENTS")); } } } return map; }

第二是列注释,SQL和表注释类似,只是视图换成ALL_COL_COMMENTS,条件多一个字段名。第三是主键信息,用前面写的ALL_CONSTRAINTS和ALL_CONS_COLUMNS关联查询。

我习惯把注释Map的key拼成“表名|字段名”,避免不同表有相同字段名导致覆盖:

map.put(rs.getString("TABLE_NAME") + "|" + rs.getString("COLUMN_NAME"), rs.getString("COMMENTS"));

最后再写一个汇总方法,把JDBC读出来的骨架和增强信息合并:

public List<TableInfo> readTableStructure(Connection conn, String schema) throws SQLException { // 1. JDBC骨架 List<TableInfo> tables = readByJdbcMetaData(conn, schema); // 2. 补充注释 Map<String, String> tableComments = readTableComments(conn, schema); Map<String, String> columnComments = readColumnComments(conn, schema); Map<String, List<String>> primaryKeys = readPrimaryKeys(conn, schema); // 3. 合并到模型 for (TableInfo table : tables) { table.setComment(tableComments.get(table.getTableName())); for (ColumnInfo col : table.getColumns()) { String key = table.getTableName() + "|" + col.getColumnName(); col.setComment(columnComments.get(key)); } table.setPrimaryKeyColumns(primaryKeys.get(table.getTableName())); } return tables; }

合并顺序值得注意:JDBC只是骨架,注释和主键一律以系统视图为准。这样写的好处是,将来要兼容MySQL,只需要换掉系统视图那部分实现,JDBC骨架部分可以原样复用。

3.5 按模式过滤:只读指定用户下的业务表

在真实数据库里,达梦会默认带一批系统模式,比如SYSDBA、SYSAUDITOR、SYSJOB,还有大量SYS开头的东西。如果不过滤,读出来的列表会非常杂乱。最稳妥的方式是白名单,明确只处理业务用户对应的模式。

SELECT TABLE_NAME FROM ALL_TABLES WHERE OWNER = UPPER(?) AND TABLE_NAME NOT LIKE 'SYS%' ORDER BY TABLE_NAME;

如果数据库里模式特别多,也可以通过查询先列出所有OWNER,人工确认一遍再写进配置:

SELECT DISTINCT OWNER FROM ALL_TABLES ORDER BY OWNER;

这一步看起来不起眼,但在最终输出数据字典时非常关键,否则文档里混进系统表,使用方很容易误读。

3.6 最终输出:生成Markdown数据字典文档

读取完结构化模型后,输出只是体力活。我常输出两种格式,一种Markdown,一种Excel,这里演示Markdown:

public String toMarkdown(TableInfo table) { StringBuilder sb = new StringBuilder(); sb.append("# 表 ").append(table.getTableName()).append("\n\n"); sb.append("> 注释:").append(nullToEmpty(table.getComment())).append("\n\n"); sb.append("| 序号 | 字段名 | 类型 | 长度 | 精度 | 小数 | 可空 | 默认值 | 注释 |\n"); sb.append("| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n"); for (ColumnInfo col : table.getColumns()) { sb.append("| ") .append(col.getPosition()).append(" | ") .append(col.getColumnName()).append(" | ") .append(col.getDataType()).append(" | ") .append(col.getLength()).append(" | ") .append(nullToEmpty(col.getPrecision())).append(" | ") .append(nullToEmpty(col.getScale())).append(" | ") .append(col.isNullable() ? "Y" : "N").append(" | ") .append(nullToEmpty(col.getDefaultValue())).append(" | ") .append(nullToEmpty(col.getComment())).append(" |\n"); } if (table.getPrimaryKeyColumns() != null && !table.getPrimaryKeyColumns().isEmpty()) { sb.append("\n**主键:** ").append(String.join(", ", table.getPrimaryKeyColumns())).append("\n"); } return sb.toString(); }

生成出来的数据字典可以直接放到项目Wiki或者文档管理系统里,格式清爽,别人看起来也直观。

4. 常见问题与排查技巧实录

4.1 查不到任何表:模式名大小写和Catalog传参问题

这是我在达梦上遇到最多的情况。代码跑完,List是空的,也不报错。一开始一脸懵,后来意识到问题出在大小写。

达梦在不加双引号的情况下,创建的表和模式会默认存为大写。如果你在JDBC调用时传了小写的schema,结果集自然为空。有些开发环境里,模式名是带引号创建的小写模式,这就更麻烦了。

排查方法很简单,先在数据库客户端执行。

SELECT DISTINCT OWNER FROM ALL_TABLES;

看看实际存在的模式名长什么样,再决定代码里统一UPPER还是原样传入。另外,getTables的schema参数传null也不是不行,但那样会把所有能访问的模式都扫一遍,包含系统模式,输出会很乱。

4.2 列注释读不到:JDBC驱动对REMARKS支持不完整

我在测试环境用达梦的DmJdbcDriver18跑getColumns,盯着结果集里的REMARKS,几次返回都是null。一开始以为是表本身没写注释,后来发现用管理工具看注释是有的,这才确定是驱动的问题。

所以别再纠结JDBC能不能返回注释了,直接放弃REMARKS这条路,改查ALL_COL_COMMENTS。这里也顺带提醒:查ALL_COL_COMMENTS时,OWNER同样要大小写一致,最好先UPPER。

如果连ALL_COL_COMMENTS也查不到注释,先确认用户是否有权限访问这张视图,以及建表的时候是否真的执行了COMMENT ON语句。这两条都排除掉以后,一般就查得到。

4.3 字段类型显示为LONG而不是VARCHAR2

做数据字典时会发现,某些字段在JDBC里返回的类型名是LONG或LONG VARCHAR,尤其在建表时用了超长文本字段,或者达梦自己做了类型映射的情况下。

比如达梦里常见的CLOB类型,或者某些驱动版本对VARCHAR2的变长处理,传回来就是LONG VARCHAR。这不代表建表语句里真的写了LONG,而是JDBC驱动的类型映射结果。

我的处理方法是,展示阶段优先使用ALL_TAB_COLUMNS里的DATA_TYPE,而不是JDBC的TYPE_NAME。JDBC的类型只用来做Java类型映射,展示文档还是以数据字典视图为准。如果你一定要在JDBC层面处理,可以写一个类型归一化的方法,把LONG VARCHAR映射回VARCHAR2,但这属于驱动差异的补丁,通用性不高。

4.4 系统表混进来:过滤不干净

达梦默认的模式可不止一个,除了当前用户自己的模式,还有一堆系统相关模式。如果你直接查ALL_TABLES且不过滤OWNER,列表里会出现大量SYS开头的东西,其中很多是达梦内部的元数据表。

光过滤OWNER LIKE 'SYS%'还不够,因为某些配套工具会创建自己的模式,名字不一定以SYS开头。所以我建议用白名单:

AND OWNER NOT LIKE 'SYS%' AND OWNER NOT IN ('SYSDBA', 'SYSAUDITOR', 'SYSJOB')

再严格一点的做法是,代码里配置一个可用的业务模式清单,比如只有TEST、APP两个模式,其他地方写死只读这些模式。这样就算以后有人新建了奇怪名字的模式,也不会混进数据字典。

4.5 驱动版本差异:DmJdbcDriver17和18行为不一致

同一个项目里,我先后接触过两套达梦环境,一套用的驱动老一点,一套用的DmJdbcDriver18。结果发现,老驱动在getColumns返回的列数量和执行效率上都有明显差异,注解信息缺得更多。

后来我统一用DmJdbcDriver18,问题少了很多。所以建议,新项目直接用18版驱动,如果碰到老环境,宁可想办法升级驱动也不要花大量时间在兼容老驱动程序上。驱动jar包最好在项目里固定版本,不要随便换,因为不同版本对元数据结果集的列名支持可能不一样,代码里硬编码的getString("REMARKS")在某个版本里可能直接抛异常。

5. 进阶扩展:表结构对比与实体类生成

5.1 基于结构化模型做表结构差异对比

读取表结构不单纯为了出文档,很多时候是为了多环境对比。有了TableInfo和ColumnInfo的模型,对比逻辑反而简单了,关键就是构建唯一键索引。

我对比两个库时,先把两个环境的表结构都读成Map<表名, TableInfo>,然后按键遍历,逐层比较:先对表清单,找新增表、删除表;再对同一个表的字段清单,按字段名找新增字段、删除字段;再检查字段的类型、长度、精度、可空、注释是否有变化。

伪代码思路如下:

public void compareTables(Map<String, TableInfo> source, Map<String, TableInfo> target) { for (String tableName : source.keySet()) { if (!target.containsKey(tableName)) { System.out.println("源库有表,目标库缺失:" + tableName); } else { compareColumns(source.get(tableName), target.get(tableName)); } } // 反过来找目标库多出来的表 }

这个功能在数据库版本升级、分库分表前评估、数据迁移验证场景下特别实用。实际跑下来,几十张表的差异一眼就能看全,比人工对着管理工具点来点去高效太多。

5.2 生成Java实体类的基本思路

既然拿到了字段类型,生成实体类就是顺理成章的事。核心是类型映射表,提前把达梦的字段类型和Java类型对应好:

  • VARCHAR、VARCHAR2、CHAR、TEXT -> String
  • INTEGER、INT -> Integer
  • BIGINT -> Long
  • DECIMAL、NUMERIC、NUMBER -> BigDecimal
  • DATE、TIMESTAMP -> LocalDateTime(或者java.util.Date)
  • FLOAT、DOUBLE -> Double

然后根据ColumnInfo拼出字段定义和getter、setter,注释直接填字段的中文注释。这里有一点要注意:达梦字段名大小写不敏感时通常显示为大写,但Java命名规范希望是驼峰。写代码时需要自己处理字段名转驼峰,比如把USER_NAME转成userName。如果数据库建表时用了带引号的小写字段,那Java字段名直接用原字段名也行,别硬转。

这个扩展属于“一次读取,多处复用”的典型落地。我实际跑下来,生成实体类生成的准确率还不错,但表名和类名的映射规则还是得人工确认,尤其是多单词表名,惯例并不完全统一。

我自己在多个异构库之间切换测试之后,最大的感受是:别指望JDBC一次把所有信息拿全,也别把系统视图当成唯一方案,二者结合才是最优解。实现时不用追求一上来就做完整架构,先针对达梦这一个库把“完整读取一张表结构”跑通,再慢慢把通用模型抽出来,后面扩展自然顺滑。这段代码目前已经在我的几个周边项目里复用,后续如果再遇到国产数据库适配,这套结构还能继续演进出更多可能性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 13:03:52

Docker Swarm编排实战:从集群搭建到生产级应用

1. 为什么要学 Swarm&#xff1a;掉进编排这个大坑之前&#xff0c;先想明白先说一个我自己的经历。两三年前我用 Docker 管项目&#xff0c;还是典型的“一台机器打天下”思路&#xff1a;docker-compose up -d一把梭&#xff0c;容器想开几个开几个&#xff0c;数据库、后端、…

作者头像 李华
网站建设 2026/10/17 13:03:22

ApeRAG开源系统实战:五种索引+MCP,用TaoToken统一Key跑通RAG应用开发

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/17 13:03:17

InnoDB底层原理与MySQL日志机制:从WAL到崩溃恢复的实战解析

做后端这些年&#xff0c;MySQL是绕不开的坎&#xff0c;而InnoDB底层原理和MySQL日志机制&#xff0c;又是坎中坎。很多人增删改查玩得飞起&#xff0c;一聊到事务隔离、崩溃恢复、主从复制就露怯。这篇我想把这两块内容串起来讲透&#xff1a;从一条UPDATE语句如何在InnoDB内…

作者头像 李华
网站建设 2026/10/17 13:01:00

投机解码实战:单卡27B模型从14到159 tok/s调优全记录

从 14 tok/s 到 159 tok/s&#xff0c;这组数字不是换卡换出来的&#xff0c;也不是量化压出来的&#xff0c;而是同一台机器、同一个 27B 模型、同一份测试集上&#xff0c;通过投机解码调优得到的实测变化。最初看到这个结果我也很意外&#xff0c;毕竟自回归解码一直被显存带…

作者头像 李华
网站建设 2026/10/17 13:00:02

AutoClaw:把OpenClaw智能体框架装进本地的部署利器

最近不少同行在聊AutoClaw&#xff08;澳龙&#xff09;这个东西。我第一反应是&#xff0c;这又是一款套壳AI工具吧&#xff1f;但被一个本地项目里OpenClaw的环境依赖折腾到怀疑人生之后&#xff0c;我重新认真试了一遍。AutoClaw解决的是一个非常实际的痛点&#xff1a;把Op…

作者头像 李华