自引用多对多关系类型ORM
Self-Referencing ManyToMany Relationship TypeORM
我刚开始使用 TypeORM,我正在努力让以下关系起作用:
用户->朋友,而朋友也是用户对象。
但是,我的 getters、getFriends 和 getFriendsInverse 正在运行;我现在确实想区分两者。换一种说法;当我执行 mysql 连接时,我不想对 friends 进行左连接而对 inverseFriends 进行另一个连接。
getter getFriends() 需要 return 所有朋友,无论 "side" 我在哪个对象上。
这有意义吗?
这是我的模型定义:
getFriends() {
// This method should also return inverseFriends;
// I do not want to return the concat version; this should
// happen on the database/orm level
// So I dont want: this.friends.concat(this.inverseFriends)
return this.friends;
}
@ManyToMany(type => User, user => user.friendsInverse, {
cascadeInsert: false,
cascadeUpdate: false,
})
@JoinTable()
friends = [];
@ManyToMany(type => User, user => user.friends, {
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: false,
})
friendsInverse = [];
我希望有人能理解我的问题 :D
谢谢
马特
您可以自引用您的关系。这是一个简单的有向图的示例(也就是一个节点可以有一个父节点和多个子节点)。
@Entity()
export class Service extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
@Index({ unique: true })
title: string;
@ManyToOne(type => Service, service => service.children)
parent: Service;
@OneToMany(type => Service, service => service.parent)
children: Service[];
}
需要牢记的一个重要注意事项是,当使用 find*
函数从数据库中读取对象时,这些关系不会自动加载。
要实际加载它们,您现在必须使用查询生成器并加入它们。 (您可以加入多个级别。)例如:
let allServices = await this.repository.createQueryBuilder('category')
.andWhere('category.price IS NULL')
.innerJoinAndSelect('category.children', 'product')
.leftJoinAndSelect('product.children', 'addon')
.getMany();
请注意我是如何使用不同的名称来引用它们的(category
、product
和 addon
)。
我相信我迟到了 3 年,但比以往任何时候都好。投票最多的答案 没有 回答这个问题,因为它只适用于 tree-like 和层次结构,所以如果你按照那个例子,就会发生这种情况:
Fred
/ \
Albert Laura
/ \
John Foo
在这个例子中,Foo
不能和Fred
成为朋友,因为他只能有一个parent。朋友不是树状结构,它就像一张网。答案如下:
import { Column, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';
@Entity(UserModel.MODEL_NAME)
export class UserModel {
static MODEL_NAME = 'users';
@PrimaryGeneratedColumn()
id?: number;
@Column({ type: 'varchar', unique: true, length: 50 })
username: string;
@Column({ type: 'varchar', length: 50, unique: true })
email: string;
@ManyToMany(type => UserModel)
@JoinTable()
friends: UserModel[];
@Column({ type: 'varchar', length: 300 })
password: string;
}
这将创建一个 table 来保存人与人之间的关系。现在是下一个重要的事情。你如何查询这个并获得用户的朋友?这并不像看起来那么容易,我已经玩了几个小时,但无法使用 TypeORM 方法甚至查询构建器来完成。答案是:原始查询。这将 return 一个包含用户朋友的数组:
async findFriends(id: Id): Promise<UserModel[]> {
return await this.userORM.query(
` SELECT *
FROM users U
WHERE U.id <>
AND EXISTS(
SELECT 1
FROM users_friends_users F
WHERE (F."usersId_1" = AND F."usersId_2" = U.id )
OR (F."usersId_2" = AND F."usersId_1" = U.id )
); `,
[id],
);
}
(users_friends_users
是 typeORM 为保存用户关系的 table 自动生成的名称)
2021 在这里,正在寻找相同的问题并找到一种无需自定义原始 SQL 即可解决该问题的方法(为简单起见,提供与示例相同的模型):
import { Column, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn, JoinTable } from 'typeorm';
@Entity(UserModel.MODEL_NAME)
export class UserModel {
static MODEL_NAME = 'users';
@PrimaryGeneratedColumn()
id?: number;
@Column({ type: 'varchar', unique: true, length: 50 })
username: string;
@Column({ type: 'varchar', length: 50, unique: true })
email: string;
@ManyToMany(type => UserModel)
@JoinTable({ joinColumn: { name: 'users_id_1' } })
friends: UserModel[];
@Column({ type: 'varchar', length: 300 })
password: string;
}
这里的关键时刻是为JoinTable
设置joinColumn
。
当你定义 ManyToMany 关系时,TypeORM 会自动创建 n-n table users_friends_users
,其中一列名为 user_id_1
,另一列名为 user_id_2
(如果有外键,它们会自动递增是一样的)
因此,从这个 table 中选择 任何 列作为“主连接列”就足够了,并且有效
我刚开始使用 TypeORM,我正在努力让以下关系起作用:
用户->朋友,而朋友也是用户对象。 但是,我的 getters、getFriends 和 getFriendsInverse 正在运行;我现在确实想区分两者。换一种说法;当我执行 mysql 连接时,我不想对 friends 进行左连接而对 inverseFriends 进行另一个连接。
getter getFriends() 需要 return 所有朋友,无论 "side" 我在哪个对象上。
这有意义吗?
这是我的模型定义:
getFriends() {
// This method should also return inverseFriends;
// I do not want to return the concat version; this should
// happen on the database/orm level
// So I dont want: this.friends.concat(this.inverseFriends)
return this.friends;
}
@ManyToMany(type => User, user => user.friendsInverse, {
cascadeInsert: false,
cascadeUpdate: false,
})
@JoinTable()
friends = [];
@ManyToMany(type => User, user => user.friends, {
cascadeInsert: true,
cascadeUpdate: true,
cascadeRemove: false,
})
friendsInverse = [];
我希望有人能理解我的问题 :D 谢谢 马特
您可以自引用您的关系。这是一个简单的有向图的示例(也就是一个节点可以有一个父节点和多个子节点)。
@Entity()
export class Service extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
@Index({ unique: true })
title: string;
@ManyToOne(type => Service, service => service.children)
parent: Service;
@OneToMany(type => Service, service => service.parent)
children: Service[];
}
需要牢记的一个重要注意事项是,当使用 find*
函数从数据库中读取对象时,这些关系不会自动加载。
要实际加载它们,您现在必须使用查询生成器并加入它们。 (您可以加入多个级别。)例如:
let allServices = await this.repository.createQueryBuilder('category')
.andWhere('category.price IS NULL')
.innerJoinAndSelect('category.children', 'product')
.leftJoinAndSelect('product.children', 'addon')
.getMany();
请注意我是如何使用不同的名称来引用它们的(category
、product
和 addon
)。
我相信我迟到了 3 年,但比以往任何时候都好。投票最多的答案 没有 回答这个问题,因为它只适用于 tree-like 和层次结构,所以如果你按照那个例子,就会发生这种情况:
Fred
/ \
Albert Laura
/ \
John Foo
在这个例子中,Foo
不能和Fred
成为朋友,因为他只能有一个parent。朋友不是树状结构,它就像一张网。答案如下:
import { Column, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';
@Entity(UserModel.MODEL_NAME)
export class UserModel {
static MODEL_NAME = 'users';
@PrimaryGeneratedColumn()
id?: number;
@Column({ type: 'varchar', unique: true, length: 50 })
username: string;
@Column({ type: 'varchar', length: 50, unique: true })
email: string;
@ManyToMany(type => UserModel)
@JoinTable()
friends: UserModel[];
@Column({ type: 'varchar', length: 300 })
password: string;
}
这将创建一个 table 来保存人与人之间的关系。现在是下一个重要的事情。你如何查询这个并获得用户的朋友?这并不像看起来那么容易,我已经玩了几个小时,但无法使用 TypeORM 方法甚至查询构建器来完成。答案是:原始查询。这将 return 一个包含用户朋友的数组:
async findFriends(id: Id): Promise<UserModel[]> {
return await this.userORM.query(
` SELECT *
FROM users U
WHERE U.id <>
AND EXISTS(
SELECT 1
FROM users_friends_users F
WHERE (F."usersId_1" = AND F."usersId_2" = U.id )
OR (F."usersId_2" = AND F."usersId_1" = U.id )
); `,
[id],
);
}
(users_friends_users
是 typeORM 为保存用户关系的 table 自动生成的名称)
2021 在这里,正在寻找相同的问题并找到一种无需自定义原始 SQL 即可解决该问题的方法(为简单起见,提供与示例相同的模型):
import { Column, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn, JoinTable } from 'typeorm';
@Entity(UserModel.MODEL_NAME)
export class UserModel {
static MODEL_NAME = 'users';
@PrimaryGeneratedColumn()
id?: number;
@Column({ type: 'varchar', unique: true, length: 50 })
username: string;
@Column({ type: 'varchar', length: 50, unique: true })
email: string;
@ManyToMany(type => UserModel)
@JoinTable({ joinColumn: { name: 'users_id_1' } })
friends: UserModel[];
@Column({ type: 'varchar', length: 300 })
password: string;
}
这里的关键时刻是为JoinTable
设置joinColumn
。
当你定义 ManyToMany 关系时,TypeORM 会自动创建 n-n table users_friends_users
,其中一列名为 user_id_1
,另一列名为 user_id_2
(如果有外键,它们会自动递增是一样的)
因此,从这个 table 中选择 任何 列作为“主连接列”就足够了,并且有效