Typeorm ManyToMany JOIN 3 tables - typeorm

I have this query
SELECT * FROM USERS
LEFT JOIN USER_GROUPS
ON USER_GROUPS.USER_ID = USERS.USER_ID
LEFT JOIN GROUPS_ROLES
ON GROUPS_ROLES.GROUP_ID = USER_GROUPS.GROUP_ID
LEFT JOIN ROLES
ON ROLES.ROLE_ID = GROUPS_ROLES.ROLE_ID;
And I want to describe his relation ManyToMany in Entity
Is it possible?

Of course it's, just describe it smth like that:
#Entity()
export class User {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#ManyToMany(type => Group)
#JoinTable()
groups: Group[];
}
#Entity()
export class Group {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#ManyToMany(type => Role)
#JoinTable()
roles: Role[];
}
#Entity()
export class Role {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
}
And that's it. It should works.

Related

Cascade delete in Typeorm for one-to-one

I read and searched a lot but did not find any solution to my problem. I read this, this, ...
My database is MySQL. There is no problem with one-to-many and many-to-many. in one-to-one relation
// Student.ts(Parent)
#Entity({ name: "student" })
export class StudentEntity {
#PrimaryGeneratedColumn()
public id: number;
#Column({ nullable: true })
public name: string;
#OneToOne(() => StudentProfile, (profile) => profile.student, { onDelete: "CASCADE", cascade: ['insert', 'update'] })
public profile: StudentProfile
}
// Profile.ts
#Entity({ name: "profile" })
export class StudentProfile {
#PrimaryGeneratedColumn()
id: number
#Column({ nullable: true })
username: string
#OneToOne(() => StudentEntity, (student) => student.profile, { onDelete: "CASCADE" })
public student: StudentEntity
}
Now, With The following code I want to remove the student and their profile:
const student = await this._studentRepository.findOne({ where: { id: 4 } })
await this._studentRepository.delete(student)
The code above does not work. there is another way: I can remove the student and profile individually, I do not want to do this.
any help would be appreciated. Thanks in advance.
As the stackoverflow you included, mentions: You have to delete referencing side to take cascade deletion to take in effect.
I guess you've to delete like:
const student = await this._studentRepository.findOne({ where: { id: 4 } })
const profile = await this._profileRepository.findOne({ where: { id: student.profile } }) // if you don't have an eager relationship
await this._profileRepository.delete(profile)
or in case of an eager relationship between student and profile:
const student = await this._studentRepository.findOne({ where: { id: 4 } })
await this._profileRepository.delete(student.profile) // if you've an eager relationship

Is there a way to flatten the fields returned by embeddedEntities in the parent entity?

If you have models set up as such:
#Entity()
class parentEntity {
#Column()
public id;
#Column(() => Name, { prefix: false })
name: Name;
}
export default class Name {
#Column()
firstName;
#Column()
lastName();
}
And you run
const test = await manager.findOne(ParentEntity, { id: 'stuffs' });
console.log(test);
You'll get the following result:
ParentEntity {
id: stuffs,
name: {
firstName: 'hello'
lastName: 'friends'
}
}
But I'd like for there to be a way to have the found entity assume this format:
ParentEntity {
id: stuffs,
firstName: 'hello'
lastName: 'friends'
}
Is there any way to achieve this with typeORM as it currently is? Thanks a ton!

Error: Object metadata not found after adding many-to-many relationships

I need to have additional properties for many-to-many relationships, I created a new entity myself as shown in the documentation.
https://github.com/typeorm/typeorm/blob/master/docs/many-to-many-relations.md#many-to-many-relations-with-custom-properties
It's user entity.
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
import { ShareTask } from './../../share-taks/entity/share-taks.entity';
#Entity('users')
export class User {
#PrimaryGeneratedColumn()
public id: number;
#Column({ type: 'varchar', nullable: false })
public email: string;
#Column({ type: 'varchar', nullable: false })
public password: string;
#OneToMany(() => ShareTask, (shareTask: ShareTask) => shareTask.user)
public shareTask: ShareTask[];
}
It's task entity.
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
import { ShareTaks } from './../../share-taks/entity/share-taks.entity';
#Entity('tasks')
export class Taks {
#PrimaryGeneratedColumn()
public id: number;
#Column({ type: 'varchar', nullable: false, length: 50 })
public title: string;
#OneToMany(() => ShareTaks, (shareTaks: ShareTaks) => shareTaks.taks)
public shareTaks: ShareTaks[];
}
It's ormconfig.json.
{
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "7047",
"database": "db",
"entities": ["dist/**/*.entity{.ts,.js}"],
"synchronize": true
}
It's ShareTaks entity.
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
import { User } from './../../users/entity/user.entity';
import { Taks } from './../../taks/entity/taks.entity';
#Entity('shareTasks')
export class shareTask {
#PrimaryGeneratedColumn()
public id: number;
#Column({ type: 'integer', nullable: false })
public taskId: number;
#Column({ type: 'integer', nullable: false })
public userId: number;
#ManyToOne(() => (task: Task) => task.shareTask, { cascade: ['insert', 'update', 'remove'] })
public task: Task;
#ManyToOne(() => (user: User) => user.shareTask, { cascade: ['insert', 'update', 'remove'] })
public user: User;
}
Before I made a many-to-many relationship, everything worked well. When I added a many-to-many relationship, I get an error.
Error: Entity metadata for ShareTask#task was not found. Check if you specified a correct entity object and if it's connected in the connection options.
Because of what I get an error, I can’t understand.
I found some answers to my question, but they did not help me.
Entity metadata for Role#users was not found
https://github.com/typeorm/typeorm/issues/420
Likely, the error is caused by the path you provided in the entities in ormconfig file. Try changing it to a relative path from the current directory, something like
"entities": [__dirname + "/../**/*.entity{.ts,.js}"]
I resolved this by exporting from an index, and then importing from that index in both my User and Role file. When I was importing directly from the class files I was getting the this error.
Error during server startup TypeORMError: Entity metadata for User#role_id was not found. Check if you specified a correct entity object and if it's connected in the connection options.
My guess is that the strict equals (===) is colliding with a cache and thinking that the different relative import paths refer to different classes.
src/entities/index.ts:
import Team from "./Team";
import Role from "./Role";
import User from "./User";
export { Team, Role, User };
DataService.ts (where the datasources are defined)
import { Role, Team, User } from "../entities";
...
entities: [Role, Team],
...
src/entities/User.ts
import { Team, Role } from "../entities";
...
#Column()
#IsNotEmpty()
#ManyToOne(() => Role, (role) => role.id)
role_id: number;

How to query a entity based on an related entity property in TypeORM?

I have an Entity Transaction and an Entity Integration
#Entity()
export default class Transaction {
#PrimaryGeneratedColumn()
public id_trans?: number;
#OneToOne(type => Integration, i => i.transaction, { nullable: true })
public integration?: Integration;
}
and
#Entity()
export default class Integration {
#PrimaryGeneratedColumn()
public id_cust?: number;
#OneToOne(type => Transaction, t => t.integration)
#JoinColumn({ referencedColumnName: 'id_trans', name: 'int_id_module' })
public transaction?: Transaction;
}
I tried to query the Transaction by using a Integration property as filter.
const id_api = 10;
const transaction = await repository.find({
where: { integration: { int_id_api: id_api} },
relations: ['integration', 'customer'],
});
but it returns the entire table of Transactions, even if the integration.int_id_api is different from id_api property
What am I doing wrong. What should I do to get this query working ?
You need to add a primary key to all your entities. From the doc:
Each entity must have at least one primary key column. This is a
requirement and you can't avoid it. To make a column a primary key,
you need to use #PrimaryColumn decorator.
Assuming you update the Integration entity like so
#Entity()
export default class Integration {
#PrimaryColumn()
public id_inte: number;
#OneToOne(type => Transaction, t => t.integration)
#JoinColumn({ referencedColumnName: 'id_trans', name: 'int_id_module' })
public transaction?: Transaction;
}
You should be able to find transactions like so
const transaction = await repository.find({
where: { integration: { id_inte: some_id } },
relations: ['integration', 'customer'],
});

Use ObjectId from MongoDB with mysql

Is their any possibility to use mongoDB ObjectID system with MySql and typeORM instead of using an incremental ID?
The goal would be to define my entity like this:
#Entity()
export class RolePermission implements IRolePermission {
#ObjectIdColumn() id?: ObjectID;
#Column()
#IsNotEmpty()
roleId: ObjectID;
#Column()
#IsNotEmpty()
permissionId: ObjectID;
}
My entities could therefore have an ID without even being persisted. The ObjectId system would prevent collisions on the unique constraint I'd like to use for this column.
If a system like that can be implemented, is their any performance downside? I remember implementing such a system with PHP, and at the time, I had read this response that made me think that it was ok: Is there a REAL performance difference between INT and VARCHAR primary keys?
It's in fact really simple. You just need to use the ObjectID object from the mongodb package and declare your entities like you would do usually.
First, install mongodb dependencies:
yarn add mongodb
yarn add #types/mongodb
Then, declare your entity. Here an example with a working relationship between a user and an article:
user.entity.ts:
import { Entity, Column, ManyToOne, PrimaryColumn } from 'typeorm';
import { Article } from './article.entity';
import { ObjectID } from 'mongodb';
#Entity()
export class User {
constructor() {
this.id = (new ObjectID()).toString();
}
#PrimaryColumn()
id: string;
#Column({ length: 500 })
username: string = null;
#OneToMany(type => Article, article => article.user)
articles: Article[];
}
article.entity.ts:
import { Entity, Column, ManyToOne, PrimaryColumn } from 'typeorm';
import { User } from './user.entity';
import { ObjectID } from 'mongodb';
#Entity()
export class Article {
constructor() {
this.id = (new ObjectID()).toString();
}
#PrimaryColumn()
id: string;
#Column({ length: 500 })
title: string = null;
#ManyToOne(type => User, user => user.articles, {nullable: true})
user: User;
}
And use it as you would normally do:
const user = new User();
user.username = 'email#adress.com';
const article = new Article();
article.title = 'Mon titre';
article.user = user;
await this.userRepository.save(user);
await this.articleRepository.save(article);

Resources