共计 3666 个字符,预计需要花费 10 分钟才能阅读完成。
这两天在开发 ACGHub 社区平台时,遇到了一个诡异的问题 —— 数据库里明明白白有表,Sea ORM 愣是报错说表不存在。搞了半天才发现是个超级隐蔽的配置问题。把这次踩坑记录下来,以后自己或者其他小伙伴再碰到类似问题就能快速定位了。
问题描述
在我的 ACGHub 项目中,我需要实现一个获取板块列表的接口,之前用的是 sqlx 的原生 SQL 查询,现在我想把它改成使用 Sea ORM 的方式。数据结构很简单,就是查询 board
表的信息,同时关联 section
表获取分区信息。
我的代码大致是这样的:
pub async fn list_boards(
&self,
offset: i32,
limit: i32,
section_oxid: Option<String>,
) -> Result<ListBoardVo> {
// 构建基本查询
let mut query = board::Entity::find()
.join(JoinType::InnerJoin, board::Relation::Section.def())
.filter(board::Column::Status.eq(1))
.filter(section::Column::Status.eq(1));
// 处理可选的分区过滤
if let Some(oxid) = section_oxid {query = query.filter(section::Column::Oxid.eq(oxid));
}
// 添加排序和分页
let board_models = query
.order_by_desc(board::Column::Weight)
.offset(offset as u64)
.limit(limit as u64)
.all(&self.pool)
.await?;
// ... 后续处理代码
}
结果却收到了这样的错误:
Database error: Query Error: error returned from database: relation \"board\" does not exist
排查过程
第一步:确认表是否存在
首先,我直接在数据库中执行了查询,确认表是否真的存在:
SELECT tablename FROM pg_tables WHERE schemaname = 'public';
结果显示 board
表确实存在,而且在 public
schema 中。
第二步:验证原始 SQL 查询
我直接用原始 SQL 查询测试,看看是否能正常获取数据:
SELECT
b.id, b.name, b.description, b.oxid, b.icon,
b.post_count, b.today_post_count, b.follower_count, b.is_official, b.updated_at,
s.oxid as section_oxid, s.name as section_name, s.description as section_description, s.icon as section_icon
FROM
board b
INNER JOIN
section s ON b.section_id = s.oxid
WHERE
b.status = 1 AND s.status = 1
ORDER BY
b.weight DESC
LIMIT 10 OFFSET 0;
这个查询可以正常执行,返回了预期的结果。
第三步:检查 Sea ORM 实体定义
我仔细检查了 Sea ORM 的实体定义,确保表名和字段名都是正确的:
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "board")]
pub struct Model {#[sea_orm(primary_key)]
pub id: i64,
#[sea_orm(unique)]
pub name: String,
#[sea_orm(column_type = "Text")]
pub description: String,
#[sea_orm(unique)]
pub oxid: String,
pub icon: String,
pub section_id: String,
pub weight: i32,
pub status: i16,
pub is_official: bool,
pub post_count: i64,
pub today_post_count: i64,
pub week_post_count: i64,
pub month_post_count: i64,
pub follower_count: i64,
pub created_by: Option<String>,
pub created_at: DateTime,
pub updated_at: DateTime,
}
一切看起来都正确。
第四步:尝试各种可能的解决方案
我尝试了各种可能的解决方案,包括:
- 修改 Sea ORM 的关系定义
- 使用不同的查询方式
- 直接使用原始 SQL 查询代替 Sea ORM 的查询构建器
但是问题依然存在…
柳暗花明又一村
折腾了大半天后,我灵光一闪,想到是不是连接配置出了问题。检查了一下数据库连接配置,果然在这里发现了问题:
pub async fn create_db_pool_v2(&self) -> Result<DatabaseConnection> {let mut opt = ConnectOptions::new(&self.database_url);
opt.max_connections(100)
.min_connections(5)
.connect_timeout(Duration::from_secs(8))
.acquire_timeout(Duration::from_secs(8))
.idle_timeout(Duration::from_secs(8))
.max_lifetime(Duration::from_secs(8))
.sqlx_logging(true)
.sqlx_logging_level(log::LevelFilter::Info)
.set_schema_search_path("my_schema") // 这货就是罪魁祸首!;
Ok(Database::connect(opt).await?)
}
哦豁!原来是 .set_schema_search_path("my_schema")
这行惹的祸。我设置了一个名为“my_schema”的 schema 搜索路径,而我的表其实都在 PostgreSQL 的默认“public”schema 中。
把这行注释掉或者改成 .set_schema_search_path("public")
后,问题立马解决!
我后来还在 Rust 论坛找到了同样问题的讨论:https://users.rust-lang.org/t/postgres-sea-orm-relation-table-name-does-not-exist-at-character-64/103858,看来不止我一个人踩过这个坑。
Schema 搜索路径是个啥玩意儿?
这次问题的关键在于 PostgreSQL 的 schema 搜索路径机制。简单来说,schema 就像是表的命名空间或者文件夹,用来组织和管理表。
当你在 SQL 中引用一个表但没有明确指定 schema 时(比如直接写 SELECT * FROM users
而不是 SELECT * FROM public.users
),PostgreSQL 会按照一定的顺序在不同的 schema 中找这个表。这个顺序就是由搜索路径决定的。
如果你把搜索路径设成了“my_schema”,而你的表其实都在“public”schema 里,那 PostgreSQL 就只会在“my_schema”里找表,找不到就报错了,即使这个表明明白白地存在于“public”schema 中。
正确的设置应该是这样的:
// 如果表在 public schema 中(PostgreSQL 的默认 schema).set_schema_search_path("public")
// 如果你有多个 schema 需要查询
.set_schema_search_path("my_schema,public")
Sea ORM 实际上是把这个搜索路径的设置传给了底层的 SQLx 和 PostgreSQL 连接,所以一旦设置错了,整个查询机制就崩了。
几点血泪教训
这次排错的过程真是既痛苦又有趣,总结一下几点经验:
不要轻信错误提示!" 表不存在 " 不一定是真的不存在,而可能是找错了地方。
对付 PostgreSQL 的问题,schema 永远是值得怀疑的对象。
ORM 再好用,也挡不住配置出问题。有时候最简单的原生 SQL 反而能帮你快速定位问题。
如果怀疑是数据库连接配置问题,可以先用最简单的 SQL 查询测试一下,能省下不少排错时间。
还有就是,多看开源社区的讨论!你遇到的问题,大概率有人已经踩过坑了。
好了,希望这篇踩坑记录能帮到其他在深夜被 Sea ORM 问题折磨的同学们。如果你有任何想法或者更好的解决方案,欢迎在评论区交流,或者来 ACGHub 的开发者社区灌水~