Skip to main content

sz_orm_core/
relation_trait.rs

1//! RelationTrait — 类型安全的关联关系定义与 JOIN 链式 API
2//!
3//! 提供 `RelationKind` / `RelationDef` / `RelationTrait` 核心类型,
4//! 配合 `#[derive(Relation)]` 宏自动生成 `RelationTrait` 实现,
5//! 追平 SeaORM `User::find().join(Posts)` 链式关联查询体验。
6//!
7//! # 设计
8//!
9//! - `RelationDef` 使用 `&'static str` 零分配描述关联关系
10//! - `RelationTrait` 提供 `def()` / `all_relations()` 方法
11//! - `RelationKind::default_join_type()` 决定 JOIN 类型(HasOne/BelongsTo → INNER,HasMany/ManyToMany → LEFT)
12//!
13//! # 用法
14//!
15//! ```ignore
16//! use sz_orm_core::relation_trait::{RelationDef, RelationKind, RelationTrait};
17//!
18//! struct User;
19//!
20//! impl RelationTrait for User {
21//!     fn def(&self) -> &'static RelationDef { &RELATIONS[0] }
22//!     fn all_relations() -> &'static [RelationDef] { RELATIONS }
23//! }
24//!
25//! static RELATIONS: &[RelationDef] = &[
26//!     RelationDef::new("orders", "users", "orders", "id", "user_id", RelationKind::HasMany),
27//! ];
28//! ```
29
30/// 关联关系类型
31///
32/// 决定 JOIN 策略和数据加载方式:
33/// - `HasOne` / `BelongsTo` → INNER JOIN(一条关联记录)
34/// - `HasMany` / `ManyToMany` → LEFT JOIN(多条关联记录,双查询策略避免行膨胀)
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36pub enum RelationKind {
37    /// 一对一:当前实体拥有一个关联实体(如 User → Profile)
38    HasOne,
39    /// 一对多:当前实体拥有多个关联实体(如 User → Orders)
40    HasMany,
41    /// 多对一:当前实体属于一个父实体(如 Order → User)
42    BelongsTo,
43    /// 多对多:通过中间表关联(如 User ↔ Role,通过 user_roles)
44    ManyToMany,
45}
46
47impl RelationKind {
48    /// 返回该关系类型默认的 JOIN 类型
49    ///
50    /// - `HasOne` / `BelongsTo` → `JoinKind::Inner`(关联记录存在性要求)
51    /// - `HasMany` / `ManyToMany` → `JoinKind::Left`(允许零关联记录)
52    pub fn default_join_type(self) -> JoinKind {
53        match self {
54            RelationKind::HasOne | RelationKind::BelongsTo => JoinKind::Inner,
55            RelationKind::HasMany | RelationKind::ManyToMany => JoinKind::Left,
56        }
57    }
58}
59
60/// JOIN 类型(与 `join_dsl::JoinKind` 对齐,独立定义避免循环依赖)
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum JoinKind {
63    /// INNER JOIN
64    Inner,
65    /// LEFT [OUTER] JOIN
66    Left,
67}
68
69impl JoinKind {
70    /// 转换为 SQL 关键字
71    pub fn as_sql(self) -> &'static str {
72        match self {
73            JoinKind::Inner => "INNER JOIN",
74            JoinKind::Left => "LEFT JOIN",
75        }
76    }
77}
78
79/// 关联关系定义(零分配,编译期常量)
80///
81/// 描述两个实体间的关联关系,包含外键映射信息。
82/// 所有字段为 `&'static str`,运行时零分配。
83#[derive(Debug, Clone)]
84pub struct RelationDef {
85    /// 关联名称(如 "orders"、"profile")
86    pub name: &'static str,
87    /// 源实体表名(如 "users")
88    pub from_entity: &'static str,
89    /// 目标实体表名(如 "orders")
90    pub to_entity: &'static str,
91    /// 源实体键列名(通常为主键,如 "id")
92    pub from_key: &'static str,
93    /// 目标实体外键列名(如 "user_id")
94    pub to_key: &'static str,
95    /// 关联类型
96    pub kind: RelationKind,
97}
98
99impl RelationDef {
100    /// 创建关联关系定义
101    pub const fn new(
102        name: &'static str,
103        from_entity: &'static str,
104        to_entity: &'static str,
105        from_key: &'static str,
106        to_key: &'static str,
107        kind: RelationKind,
108    ) -> Self {
109        Self {
110            name,
111            from_entity,
112            to_entity,
113            from_key,
114            to_key,
115            kind,
116        }
117    }
118}
119
120/// 关联关系 trait — 由 `#[derive(Relation)]` 自动实现
121///
122/// 提供关联定义访问和批量关联查询能力。
123/// 实体类型实现此 trait 后,可通过 `QueryBuilder::join()` 链式构建 JOIN 查询。
124pub trait RelationTrait: Send + Sync {
125    /// 返回当前关联的定义
126    fn def(&self) -> &'static RelationDef;
127
128    /// 返回实体所有关联定义的静态切片
129    fn all_relations() -> &'static [RelationDef]
130    where
131        Self: Sized;
132
133    /// 按名称查找关联定义
134    fn relation_by_name(name: &str) -> Option<&'static RelationDef>
135    where
136        Self: Sized,
137    {
138        Self::all_relations().iter().find(|r| r.name == name)
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    static TEST_RELATIONS: &[RelationDef] = &[
147        RelationDef::new(
148            "orders",
149            "users",
150            "orders",
151            "id",
152            "user_id",
153            RelationKind::HasMany,
154        ),
155        RelationDef::new(
156            "profile",
157            "users",
158            "profiles",
159            "id",
160            "user_id",
161            RelationKind::HasOne,
162        ),
163        RelationDef::new(
164            "owner",
165            "orders",
166            "users",
167            "user_id",
168            "id",
169            RelationKind::BelongsTo,
170        ),
171        RelationDef::new(
172            "roles",
173            "users",
174            "roles",
175            "id",
176            "role_id",
177            RelationKind::ManyToMany,
178        ),
179    ];
180
181    struct User;
182
183    impl RelationTrait for User {
184        fn def(&self) -> &'static RelationDef {
185            &TEST_RELATIONS[0]
186        }
187        fn all_relations() -> &'static [RelationDef] {
188            TEST_RELATIONS
189        }
190    }
191
192    #[test]
193    fn test_relation_kind_default_join_type() {
194        assert_eq!(RelationKind::HasOne.default_join_type(), JoinKind::Inner);
195        assert_eq!(RelationKind::BelongsTo.default_join_type(), JoinKind::Inner);
196        assert_eq!(RelationKind::HasMany.default_join_type(), JoinKind::Left);
197        assert_eq!(RelationKind::ManyToMany.default_join_type(), JoinKind::Left);
198    }
199
200    #[test]
201    fn test_join_kind_as_sql() {
202        assert_eq!(JoinKind::Inner.as_sql(), "INNER JOIN");
203        assert_eq!(JoinKind::Left.as_sql(), "LEFT JOIN");
204    }
205
206    #[test]
207    fn test_relation_def_new() {
208        let def = RelationDef::new(
209            "orders",
210            "users",
211            "orders",
212            "id",
213            "user_id",
214            RelationKind::HasMany,
215        );
216        assert_eq!(def.name, "orders");
217        assert_eq!(def.from_entity, "users");
218        assert_eq!(def.to_entity, "orders");
219        assert_eq!(def.from_key, "id");
220        assert_eq!(def.to_key, "user_id");
221        assert_eq!(def.kind, RelationKind::HasMany);
222    }
223
224    #[test]
225    fn test_relation_trait_all_relations() {
226        let relations = User::all_relations();
227        assert_eq!(relations.len(), 4);
228        assert_eq!(relations[0].name, "orders");
229        assert_eq!(relations[1].name, "profile");
230        assert_eq!(relations[2].name, "owner");
231        assert_eq!(relations[3].name, "roles");
232    }
233
234    #[test]
235    fn test_relation_trait_relation_by_name() {
236        let found = User::relation_by_name("orders");
237        assert!(found.is_some());
238        assert_eq!(found.unwrap().to_entity, "orders");
239        assert_eq!(found.unwrap().kind, RelationKind::HasMany);
240
241        let not_found = User::relation_by_name("unknown");
242        assert!(not_found.is_none());
243    }
244
245    #[test]
246    fn test_relation_trait_def() {
247        let user = User;
248        let def = user.def();
249        assert_eq!(def.name, "orders");
250        assert_eq!(def.kind, RelationKind::HasMany);
251    }
252}