关系
关系允许以某种方式将两个实体连接起来。这通常在数据库中通过外键的概念来实现。Deepkit ORM 在所有官方数据库适配器中都支持关系。
关系使用 Reference
装饰器进行标注。通常一个关系还会有一个反向关系,用 BackReference
类型标注,但只有在需要在数据库查询中使用反向关系时才需要。反向引用仅为虚拟的。
一对多
存储引用的实体通常称为 owning side
,即 owns
该引用的一方。下面的代码展示了 User
与 Post
之间的一对多关系的两个实体。这意味着一个 User
可以拥有多个 Post
。实体 post
拥有 post->user
关联。在数据库中,此时会有一个字段 Post. "author"
,其中包含 User
的主键。
import { SQLiteDatabaseAdapter } from '@deepkit/sqlite'; import { entity, PrimaryKey, AutoIncrement, Reference } from '@deepkit/type'; import { Database } from '@deepkit/orm'; @entity.collection('users') class User { id: number & PrimaryKey & AutoIncrement = 0; created: Date = new Date; constructor(public username: string) { } } @entity.collection('posts') class Post { id: number & PrimaryKey & AutoIncrement = 0; created: Date = new Date; constructor( public author: User & Reference, public title: string ) { } } const database = new Database( new SQLiteDatabaseAdapter(':memory:'), [User, Post] ); await database.migrate(); const user1 = new User('User1'); const post1 = new Post(user1, 'My first blog post'); const post2 = new Post(user1, 'My second blog post'); await database.persist(user1, post1, post2);
默认情况下,查询不会选取引用。详见数据库联接。
多对一
一个引用通常会有一个反向引用(多对一)。它只是一个虚拟引用,因为它并不会在数据库中体现。反向引用使用 BackReference
标注,主要用于反射与查询联接。如果在 User
上添加到 Post
的 BackReference
,就可以在 User
的查询中直接联接 Post
。
@entity.name('user').collection('users') class User { id: number & PrimaryKey & AutoIncrement = 0; created: Date = new Date; posts?: Post[] & BackReference; constructor(public username: string) { } }
//[ { username: 'User1', posts: [ [Post], [Post] ] } ] const users = await database.query(User) .select('username', 'posts') .joinWith('posts') .find();
多对多
多对多关系允许将多个记录与多个其他记录关联起来。例如,它可用于用户与组的场景。一个用户可以不在任何组、在一个组或多个组中。相应地,一个组可以包含 0 个、一个或多个用户。
多对多关系通常使用一个中间表实体来实现。该中间实体包含指向另外两个实体的实际拥有端引用,而这两个实体则对该中间实体设置反向引用。
@entity.name('user') class User { id: number & PrimaryKey & AutoIncrement = 0; created: Date = new Date; groups?: Group[] & BackReference<{via: typeof UserGroup}>; constructor(public username: string) { } } @entity.name('group') class Group { id: number & PrimaryKey & AutoIncrement = 0; users?: User[] & BackReference<{via: typeof UserGroup}>; constructor(public name: string) { } } // 中间表实体 @entity.name('userGroup') class UserGroup { id: number & PrimaryKey & AutoIncrement = 0; constructor( public user: User & Reference, public group: Group & Reference, ) { } }
使用这些实体后,你可以创建用户与组,并通过中间实体将它们关联起来。通过在 User 中使用反向引用,我们可以在 User 的查询中直接获取其 groups。
const database = new Database(new SQLiteDatabaseAdapter(':memory:'), [User, Group, UserGroup]); await database.migrate(); const user1 = new User('User1'); const user2 = new User('User2'); const group1 = new Group('Group1'); await database.persist(user1, user2, group1, new UserGroup(user1, group1), new UserGroup(user2, group1)); //[ // { id: 1, username: 'User1', groups: [ [Group] ] }, // { id: 2, username: 'User2', groups: [ [Group] ] } // ] const users = await database.query(User) .select('username', 'groups') .joinWith('groups') .find();
要将用户与组解除关联,删除对应的 UserGroup 记录即可:
const users = await database.query(UserGroup) .filter({user: user1, group: group1}) .deleteOne();
一对一
约束
删除/更新时:RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT