自引用多对多关系类型ORM
Posted
技术标签:
【中文标题】自引用多对多关系类型ORM【英文标题】:Self-Referencing ManyToMany Relationship TypeORM 【发布时间】:2017-09-30 13:26:49 【问题描述】:我刚刚开始使用 TypeORM,我正在努力让以下关系发挥作用:
User->Friends,而 Friend 也是一个用户对象。 但是,我的吸气剂 getFriends 和 getFriendsInverse 正在工作;我现在确实想区分这两者。换一种说法;当我执行 mysql join 时,我不想对朋友进行左连接,而对 inverseFriends 进行另一个左连接。
getter getFriends() 需要返回所有朋友,而不管我在对象的“哪一边”。
这有意义吗?
这是我的模型定义:
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 谢谢 马特
【问题讨论】:
如果我的回答回答了您的问题,请将其标记为正确。在此先感谢:) 【参考方案1】:这里是 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;
这里的关键时刻是将joinColumn
设置为JoinTable
。
在定义多对多关系时,TypeORM 会自动创建 n-n 表 users_friends_users
,其中一列名为 user_id_1
,另一列名为 user_id_2
(如果外键相同,它们会自动递增)
因此,从该表中选择 任何 列作为“主连接列”就足够了,它可以工作
【讨论】:
【参考方案2】:我相信我迟到了 3 年,但迟到比以往任何时候都好。投票率最高的答案不回答了这个问题,因为它只适用于树状结构和层次结构,所以如果你按照这个例子,就会发生这种情况:
Fred
/ \
Albert Laura
/ \
John Foo
在这个例子中,Foo
不能和Fred
成为朋友,因为他只能有一个父母。朋友不是树状结构,它就像一张网。答案如下:
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;
这将创建一个表格,用于保存人与人之间的关系。现在是下一个重要的东西。你如何查询这个并获得用户的朋友?这并不像看起来那么容易,我已经玩了几个小时并且无法使用 TypeORM 方法甚至查询生成器来做到这一点。答案是:原始查询。这将返回一个包含用户朋友的数组:
async findFriends(id: Id): Promise<UserModel[]>
return await this.userORM.query(
` SELECT *
FROM users U
WHERE U.id <> $1
AND EXISTS(
SELECT 1
FROM users_friends_users F
WHERE (F."usersId_1" = $1 AND F."usersId_2" = U.id )
OR (F."usersId_2" = $1 AND F."usersId_1" = U.id )
); `,
[id],
);
(users_friends_users
是 typeORM 为保存用户关系的表自动生成的名称)
【讨论】:
从这个答案中,使用空表手动实现它不是更好,就像在 SQL 中使用联结表一样?还是我错过了使用 @ManyToMany 的好处? @talfreds 使用该注释将自动为您创建联结表。这就是 ORM 及其注释的魔力。由于所有这些注释,它还将自动生成带有原始查询的迁移文件,这有点疯狂。在对我的架构进行相对较大/复杂的更改后,我尝试生成迁移,它就像一个魅力。 @talfreds 同样,一致地使用这些注释而不是自动制作部分和手动制作部分将有助于 ORM 跟踪所有内容。 啊,我的意思是这样的:***.com/questions/55253563/… 但我明白你的意思,如果你不需要额外的字段或其他东西,使用 @ManyToMany 会更好【参考方案3】:您可以自我引用您的关系。这是一个简单的有向图示例(也就是一个节点可以有一个父节点和多个子节点)。
@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
)。
【讨论】:
嗨,我已经这样做了,但是在进行生产发布时,实体服务无法自行识别 我不确定这是否适用于这种情况。您在谈论树结构,但这更像是一张网。没有 1 个父母,x 个孩子。 是的,这绝对行不通。请参阅我对这篇文章原始问题的具体情况的回答。【参考方案4】:希望有人能理解我的问题
是的,这就是问题所在。我不知道想做什么。 Friend
是一个单独的类还是仍然是 User->User[]
关系?
也许这会对您有所帮助: https://typeorm.github.io/tables-and-columns.html#closure-table
【讨论】:
以上是关于自引用多对多关系类型ORM的主要内容,如果未能解决你的问题,请参考以下文章