1. 问题重现:为什么我的表明明存在,人大金仓却说字段不存在?
最近在帮一个项目做数据库迁移,从MySQL换到人大金仓,结果应用一启动就炸了。报错信息非常直接:“字段‘user_name’不存在”。我当时就懵了,第一反应是:建表脚本是不是漏了?赶紧连上数据库,用客户端工具执行了一遍 SELECT * FROM sys_user;,嘿,数据刷刷地就出来了,字段一个不少。
这就奇怪了,同一个SQL,在数据库工具里能跑,通过我们Java应用的JDBC连接跑就报错。这感觉就像你对着一个人喊名字,他明明就在你面前,却死活不答应,非得你走到他跟前拍一下肩膀才行。这种“薛定谔的表”问题,在数据库迁移里特别磨人。
我花了点时间深入排查,终于揪出了“元凶”。原来,人大金仓数据库(KingbaseES)内部有一个系统模式(Schema),名字叫 sys_catalog。这个模式里,存放着数据库管理自身需要的各种系统表、视图和函数。巧了,这里面也有一张表,名字就叫 sys_user。
这下问题就清晰了。当我们的应用程序执行 SELECT * FROM sys_user 时,数据库需要决定到底去哪个模式(Schema)里找这张表。如果没明确告诉它,它就会按照一个默认的搜索路径(search_path)去挨个找。很可能,在这个默认路径里,sys_catalog 这个系统模式的优先级,排在了我们自己创建的应用模式(比如 public 或 myschema)前面。结果就是,数据库一头钻进了系统表里,发现系统表 sys_user 的结构和我们应用表的结构完全不一样,自然就报“字段不存在”了。
所以,核心矛盾点在于 “同名表在多个模式下的查询优先级冲突”。这不是代码写错了,也不是表没建好,而是数据库的“寻路规则”需要调整。下面,我就把自己实测有效的两种解决方案,掰开揉碎了讲给你听。
2. 解决方案一:从源头入手,修改JDBC连接串
这是我最先尝试,也是我认为对应用最“无侵入”的一种方法。思路很简单:既然数据库不知道该先去哪个模式找表,那我们就在建立数据库连接的时候,直接把“寻路指南”告诉它。
这个“寻路指南”,就是通过JDBC连接字符串(URL)里的参数来设置的。通常,我们连接人大金仓的URL长这样:
jdbc:kingbase8://localhost:54321/my_database
或者,为了指定默认的模式,我们会加上 currentSchema 参数:
jdbc:kingbase8://localhost:54321/my_database?currentSchema=my_app_schema
最开始,我以为加了 currentSchema=my_app_schema 就万事大吉了,数据库应该会乖乖先去 my_app_schema 里找表。但实际测试发现,光指定一个模式是不够的。数据库的搜索行为是:先看 currentSchema,如果没找到,它还是会回头去搜索路径里包含的其他模式(包括 sys_catalog)里继续找。
因此,正确的姿势是,通过 currentSchema 参数,显式地定义出一个完整的、有序的模式搜索列表,并且必须把系统模式 sys_catalog 放在最后。修改后的连接字符串如下:
jdbc:kingbase8://localhost:54321/my_database?currentSchema=my_app_schema,sys_catalog
请注意这里的写法:currentSchema 的值不再是单个模式名,而是用英文逗号分隔的多个模式名。这个列表的顺序就是查询时的优先级顺序。
我来详细解释一下这个连接串生效后,数据库的行为:
- 应用发起查询
SELECT * FROM sys_user。 - JDBC驱动会


406

被折叠的 条评论
为什么被折叠?



