使用 sqlx 将 n:m 关系映射到 Vec

pas*_*ket 5 rust rust-sqlx

我有 2 个表(工作场所和工人),具有:m 关系。我的目标是拥有一个workplace包含Vec所有相关workers. 我想使用 sqlx 来做到这一点。使用柴油对我来说不是一个选择。

这是我在数据库方面想到的:

CREATE TABLE workplaces (
    id BIGSERIAL PRIMARY KEY,
    place TEXT NOT NULL
);

CREATE TABLE workers (
    id BIGSERIAL PRIMARY KEY,
    name TEXT NOT NULL
);

CREATE TABLE workplaces_workers (
    workplace_id BIGINT NOT NULL REFERENCES workplaces (id) ON DELETE CASCADE,
    workers_id BIGINT NOT NULL REFERENCES workers (id) ON DELETE CASCADE,
    PRIMARY KEY (workplace_id, workers_id)
);
Run Code Online (Sandbox Code Playgroud)

到目前为止,这是我的 Rust 代码:

pub struct Workplace {
    pub id: i64,
    pub place: String,
    pub workers: Option<Vec<Worker>>
}

pub struct Worker {
    pub id: i64,
    pub name: String
}
Run Code Online (Sandbox Code Playgroud)

我尝试使用sqlx::query_as宏,但我无法找到映射的方法Vec<Worker>

pub async fn find_workplace(pool: &PgPool, id: &i64) -> Result<Option<Workplace>> {
  return Ok(query_as!(Workplace, "SELECT * FROM workplaces wp 
      JOIN workplaces_workers wpws ON wp.id = wpws.workplace_id
      JOIN workers ws ON wpws.workers_id = ws.id 
      WHERE wp.id = $1", id).fetch_optional(pool).await?)
}
Run Code Online (Sandbox Code Playgroud)

小智 2

据我所知,SQLX 不是一个 ORM\xe2\x84\xa2,除了将平面结构与表进行类型匹配之外,没有为从查询构造域类型提供更多便利。\n这是好消息,也是坏消息 - 我们可以拥有我们想要的架构和我们想要的域类型,并对两者拥有完全控制权。但当然,我们需要自己弄清楚如何从一种转向另一种。可以这么说,要想吃到蛋糕,我们就必须烘焙。幸运的是,烘焙并不难。

\n

以下是适用于您的架构、结构和查询的可行解决方案。请记住,这并不是迄今为止唯一或“最佳”的解决方案:我们可以有一个表示 rust 中定义的查询结果的中间类型,并用于query_as!对其进行静态类型检查,然后impl Into<Workplace>对我们的中间类型进行静态类型检查。另一种方法是impl Into<Option<Workplace>> on Vec<PgRow>直接。这取决于您的用例以及您最终想要公开的用于数据持久性的 API。

\n
use sqlx::Row;\n\n#[derive(Debug)]\npub struct Worker {\n    pub id: i64,\n    pub name: String,\n}\n\n#[derive(Debug, sqlx::FromRow)]\npub struct Workplace {\n    pub id: i64,\n    pub place: String,\n    //no need for Option, Vec can accommodate for zero workers\n    pub workers: Vec<Worker>,\n}\n\nimpl Workplace {\n    pub async fn find_by_id(id: i64, pool: &sqlx::PgPool) -> Option<Self> {\n        let rows = sqlx::query(\n            "SELECT * FROM workplaces wp \n        JOIN workplaces_workers wpws ON wp.id = wpws.workplace_id\n        JOIN workers ws ON wpws.workers_id = ws.id \n        WHERE wp.id = $1",\n        )\n        .bind(id)\n        .fetch_all(pool)\n        .await\n        .unwrap_or_default(); \n        if rows.is_empty() {\n            return None;\n        }\n        let place = rows[0].get("place");\n        let mut workers: Vec<Worker> = Vec::with_capacity(rows.len());\n        for row in rows {\n            workers.push(Worker {\n                id: row.get("id"),\n                name: row.get("name"),\n            });\n        }\n        Some(Workplace { id, place, workers })\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n