Problem saving ManyToMany relation in MySQL database via microservices - mysql

I have a relationship created between User and Groups, where a group can contain multiple users and a user can be present in more than one group. The relationship was built on the UserEntity and GroupsEntity entities. The problem occurs when the request is made in Postman. The User record is created, but the ids corresponding to the relationship between groups and user are not created in the new table (relationship of the two entities).
Help codes:
UserEntitity.ts
import { AssociateEntity } from 'src/associate/entities/associate.entity';
import { Associate } from 'src/associate/interfaces/associate.interface';
import { GroupsEntity } from 'src/groups/entities/group.entity';
import { Groups } from 'src/groups/interfaces/groups.interface';
import { SubsidiaryEntity } from 'src/subsidiary/entities/subsidiary.entity';
import { Subsidiary } from 'src/subsidiary/interfaces/subsidiary.interface';
import {
Column,
DeleteDateColumn,
Entity,
JoinTable,
ManyToMany,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
#Entity()
export class UserEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#Column()
login: string;
#Column()
password: string;
#Column()
email: string;
#Column()
adm: boolean;
#Column()
passwordValidity: string;
#Column()
initials: string;
#Column()
system: string;
#ManyToOne(() => SubsidiaryEntity, (subsidiary) => subsidiary.id)
subsidiary: Subsidiary;
#ManyToMany(() => GroupsEntity, { cascade: true })
#JoinTable()
group: Groups[];
#Column({ default: true })
status: boolean;
#DeleteDateColumn()
deletedAt: Date;
#OneToMany(() => AssociateEntity, (associate) => associate.id)
associate: Associate[];
}
GroupsEntity.ts
import { UserEntity } from 'src/user/entities/user.entity';
import { User } from 'src/user/interfaces/user.interface';
import {
Column,
DeleteDateColumn,
Entity,
ManyToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
#Entity()
export class GroupsEntity {
#PrimaryGeneratedColumn()
id: number;
#Column()
description: string;
#Column({ default: true })
status: boolean;
#DeleteDateColumn()
deletedAt: Date;
#ManyToMany(() => UserEntity, (user) => user.group)
user: User;
}
user.service.ts (method create)
async create(user: User) {
try {
const groups: Array<Groups> = await this.groupService.findByIds(
user.groups,
);
//#ts-ignore
user.groups = groups;
const newUser = await this.userRepository.save(user);
return newUser;
} catch (error) {
this.logger.error(`error: ${JSON.stringify(error.message)}`);
throw new RpcException(error.code + error.message);
}
}
The structure of the bank is given by:
Bank entity relationships
The code doesn't report errors, but it doesn't create the relationship with the ids either. I'm using microservices in nestJS, the language is TypeScript and the database used is MySQL.

Join table you have define the join Column and inverseJoinColumn Details. Example
#JoinTable({
name: 'cat_use_cloth',
joinColumn: { name: 'cat_id', referencedColumnName: 'id'},
inverseJoinColumn: { name: 'cloth_id', referencedColumnName: 'id'},
})
Ref: https://medium.com/#rodrigo.tornaciole/nest-js-many-to-many-relationship-using-typeorm-and-crud-ec6ed79274f0

Related

Relations in Nest JS (Type ORM using SQL DB)

I am unable to understand relations basically I know the concept of relations but I am unable to us them in my project like m not getting what I want.
I don't know what is wrong here
import { ApiProperty } from '#nestjs/swagger';
import {
Entity,
Column,
PrimaryGeneratedColumn,
OneToOne,
JoinColumn,
} from 'typeorm';
import { Customer } from './Customer.entitye';
import { Sale } from './Sale.entity';
#Entity()
export class Mobile {
#PrimaryGeneratedColumn()
#ApiProperty()
id: number;
#Column()
#ApiProperty()
Mobile_name: string;
#Column()
#ApiProperty()
Mobile_Model: string;
#Column()
#ApiProperty()
Mobile_EMEI: String;
#Column()
#ApiProperty()
Mobile_Price: number;
#Column()
#ApiProperty()
Mobile_Color: string;
#OneToOne(() => Sale)
#JoinColumn()
Profile: Sale;
#OneToOne(() => Customer)
#JoinColumn()
customer: Customer;
}
and the second entity is
import { ApiProperty } from '#nestjs/swagger';
import { Entity, Column, PrimaryGeneratedColumn, OneToOne, JoinColumn } from 'typeorm';
import { Mobile } from './Mobile.entity';
#Entity()
export class Sale{
#PrimaryGeneratedColumn()
id:number
#Column()
#ApiProperty()
Sold_Mobile : String
#OneToOne(() => Mobile, (mobile) => mobile.Mobile_EMEI)
#JoinColumn()
mobile: Mobile
}
why this Mobileid is null
SS of DB
kindly give me the sol or just tell me the best source to learn thi thing

Typeorm NestJS duplicate column name

We have this typeorm entities defined in our NestJS application:
#Entity()
export class Crawler {
#PrimaryColumn({ length: 50 })
id!: string;
...
#ManyToOne(() => CrawlerFamily, (crawlerFamily) => crawlerFamily.id, {
nullable: false,
eager: true,
})
#JoinColumn({ name: 'crawler_family_id' })
crawler_family!: CrawlerFamily;
}
#Entity()
export class CrawlerFamily {
#PrimaryGeneratedColumn()
id?: number;
#Column({ length: 255 })
name!: CrawlerFamilyName;
}
Now, when we try to search for this Crawler entity we need to use findBy instead of findOneBy when we want only one crawler, because when we try to use findOneBy this is the error message we get in response:
{"context":"RpcExceptionsHandler","level":"error","message":"Duplicate column name 'Crawler_crawler_family_id'","stack":["QueryFailedError: Duplicate column name 'Crawler_crawler_family_id'\n at Query.<anonymous> (/home/node/app/node_modules/typeorm/driver/mysql/MysqlQueryRunner.js:158:37)\n at /home/node/app/node_modules/newrelic/lib/shim/shim.js:1313:22\n at LegacyContextManager.runInContext (/home/node/app/node_modules/newrelic/lib/context-manager/legacy-context-manager.js:59:23)\n at DatastoreShim.applySegment (/home/node/app/node_modules/newrelic/lib/shim/shim.js:1303:25)\n at Query.wrappedCallback [as onResult] (/home/node/app/node_modules/newrelic/lib/shim/shim.js:1189:21)\n at Query.execute (/home/node/app/node_modules/mysql2/lib/commands/command.js:36:14)\n at PoolConnection.handlePacket (/home/node/app/node_modules/mysql2/lib/connection.js:456:32)\n at PacketParser.onPacket (/home/node/app/node_modules/mysql2/lib/connection.js:85:12)\n at PacketParser.executeStart (/home/node/app/node_modules/mysql2/lib/packet_parser.js:75:16)\n at TLSSocket.<anonymous> (/home/node/app/node_modules/mysql2/lib/connection.js:360:25)"],"timestamp":"2022-08-17T13:48:23.146Z"}
How to properly setup these entities? I've tried using #Column() decorator to change column name but it's not working.
You're using one to many/many to one wrong, see:
https://orkhan.gitbook.io/typeorm/docs/many-to-one-one-to-many-relations
Here's what you should have:
#Entity()
export class Crawler {
#PrimaryColumn({ length: 50 })
id!: string;
...
#ManyToOne(() => CrawlerFamily, (crawlerFamily) => crawlerFamily.crawler, {
nullable: false,
eager: true,
})
crawler_family!: CrawlerFamily;
}
#Entity()
export class CrawlerFamily {
#PrimaryGeneratedColumn()
id?: number;
#Column({ length: 255 })
name!: CrawlerFamilyName;
#OneToMany(() => Crawler, (crawler) => crawler.crawlerFamily)
crawler!: Crawler[];
}

Update a many-to-many relationship with TypeORM, Nest JS

I'm having trouble updating an entity that has many-to-many.
Below are the product entities and categories.
#Entity()
class Products {
#PrimaryGeneratedColumn()
public id: number;
#Column()
public title: string;
#Column()
public content: string;
#ManyToMany((type) => Category, (category) => category.products)
#JoinTable()
#Type(() => Category)
categories: Category[];
}
#Entity()
class Category {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#ManyToMany((type) => Products, (products) => products.categories, {
cascade: true,
})
#Type(() => Products)
products: Products[];
}
Update in my service.
async updateProduct(id: number, product: UpdateProductDto) {
await this.service.update(id, product);
const updatedProduct = await this.service.findOne(id, {
relations: ['categories'],
});
if (updatedProduct) {
return updatedProduct;
}
throw new ProductNotFoundException(id);
}
I do not understand what exactly the problem is, since the addition works, the data in the database is there, I send a change request with data similar to how to create it.
{
"content": "test",
"title": "test",
"categories": [
{
"id": 3
}
]
}
Error message
[ExceptionsHandler] column "productsId" of relation "products" does not exist +23003ms
QueryFailedError: column "productsId" of relation "products" does not exist
at new QueryFailedError (...\node_modules\typeorm\error\QueryFailedError.js:12:28)
at PostgresQueryRunner.<anonymous> (...\node_modules\typeorm\driver\postgres\PostgresQueryRunner.js:248:31)
at step (...\node_modules\typeorm\node_modules\tslib\tslib.js:143:27)
at Object.throw (...\node_modules\typeorm\node_modules\tslib\tslib.js:124:57)
at rejected (...\node_modules\typeorm\node_modules\tslib\tslib.js:115:69)
at processTicksAndRejections (internal/process/task_queues.js:93:5)

NestJS Validation failed (numeric string is expected) inside #Get Controller and Boolean Query

So I got a User Enitity (Benutzer) and want to get only those users, who are supervisors (Vorgesetzter). Therefore I implemented a Service called " getAllVorgesetzten".
The User Entity looks like:
#Entity()
export class Benutzer {
#PrimaryGeneratedColumn({ type: "int" })
BenutzerID: number;
#Column({ type: "varchar", length: 50})
Benutzername: string;
#Column({ type: "varchar", length: 50})
Passwort: string;
#Column()
istAdmin: boolean;
#Column()
istVorgesetzter: boolean;
#Column({ type: "varchar", length: 50})
Vorgesetzter: string;
#Column({ type: "varchar", length: 50})
Vorname: string;
#Column({ type: "varchar", length: 50})
Nachname: string;
#Column()
Geburtsdatum: Date;
#Column({ type: "varchar", length: 50})
Email: string;
#Column()
Eintrittsdatum: Date;
#ManyToOne(() => Arbeitstage, arbeitstage => arbeitstage.allebenutzer)
arbeitstage: Arbeitstage;
#ManyToOne(() => Bundesland, bundesland => bundesland.alleBenutzer)
bundesland: Bundesland;
#OneToMany(() => Urlaubsantrag, urlaubsantrag => urlaubsantrag.benutzer)
anträge: Urlaubsantrag[];
}
The User Service is implemented as follows:
#Injectable()
export class BenutzerService {
constructor(
#InjectRepository(Benutzer)
private benutzerRepository: Repository<Benutzer>,
) {}
getAllBenutzer(): Promise<Benutzer[]> {
return this.benutzerRepository.find({ relations: ['bundesland', 'arbeitstage', 'anträge']});
}
findBenutzerByID(id: number): Promise<Benutzer> {
return this.benutzerRepository.findOne(id, { relations: ['bundesland','arbeitstage','anträge']});
}
getAllVorgesetzten(istVorgesetzter: boolean): Promise<Benutzer[]>{
return this.benutzerRepository.find({ where: {istVorgesetzter: istVorgesetzter} });
}
async createBenutzer(benutzer: CreateBenutzer) {
const nutzer = this.benutzerRepository.create(benutzer);
await this.benutzerRepository.save(benutzer);
return nutzer;
}
async updateBenutzer(benutzer: UpdateBenutzer): Promise<UpdateResult> {
return await this.benutzerRepository.update(benutzer.BenutzerID, benutzer);
}
async deleteBenutzer(id: number): Promise<DeleteResult> {
return await this.benutzerRepository.findOne(id).then((value) => {
return this.benutzerRepository.delete(value);
});
}
}
And the Controller looks like:
#Controller('benutzer')
export class BenutzerController {
constructor(private readonly benutzerService: BenutzerService) {}
#Post()
async createBenutzer(#Body() createBenutzer: CreateBenutzer): Promise<Benutzer> {
return this.benutzerService.createBenutzer(createBenutzer);
}
#Get()
async getBenutzer(): Promise<Benutzer[]> {
return this.benutzerService.getAllBenutzer();
}
#Get(':id')
async getBenutzerByID(#Param('id', new ParseIntPipe()) id): Promise<Benutzer> {
return this.benutzerService.findBenutzerByID(id);
}
#Get('/vorgesetzter')
async getAllVorgesetzten(#Query('istVorgesetzter', new ParseBoolPipe()) istVorgesetzter: boolean): Promise<Benutzer[]>{
console.log(istVorgesetzter);
return this.benutzerService.getAllVorgesetzten(istVorgesetzter);
}
#Patch()
async update(#Body() updateBenutzer: UpdateBenutzer) {
return this.benutzerService.updateBenutzer(updateBenutzer);
}
#Delete(':id')
async remove(#Param('id', new ParseIntPipe()) id) {
return this.benutzerService.deleteBenutzer(id);
}
}
So I have the request method like http://localhost:3000/benutzer/vorgesetzter?istVorgesetzter=true and want to get all users (benutzer) where istVorgesetzter = true. But I get the following error:
error in swagger
{
"statusCode": 400,
"message": "Validation failed (numeric string is expected)",
"error": "Bad Request"
}
Order matters here, #Get('/vorgesetzter') should be implemented before #Get(':id'). With that request you are calling this.benutzerService.findBenutzerByID(id); with the following string: vorgesetzter?istVorgesetzter=true receiving the error you describe.
This is how express is implemented and NestJS follows the same behaviour: expressjs/express#2235, nestjs/nest#995

Typeorm how to get relations of relations

I am getting the Object ChatRoomEntity with entitymanager.findOne method. The ChatRoomEntity has the variable messages which is a OneToMany - ManyToOne Relation. I have no problems to select that but how do I get the user which sent the message. Its a variable on MessageEntity with a OneToMany Relation.
So basically I want to select a room and all messages of it. But all messages should also have their values on fromUser.
I select the room like this:
this.entityManager.findOne(ChatRoomEntity, {where: {id: roomToJoin.id}, relations: ['activeUsers', 'messages']}).then(roomEntity => {
// some code
}
Here my entities:
UserEntity
#Entity()
export class UserEntity {
#PrimaryGeneratedColumn()
id: number;
#CreateDateColumn()
registrationDate: Date;
#ManyToMany(type => ChatRoomEntity, room => room.activeUsers, {cascade: true})
#JoinTable()
activeChatRooms: ChatRoomEntity[];
#OneToMany(type => ChatRoomMessageEntity, msg => msg.fromUser)
chatRoomMessages: ChatRoomMessageEntity[];
}
ChatRoomEntity
#Entity()
export class ChatRoomEntity {
#PrimaryGeneratedColumn()
id: number;
#Column('varchar', {nullable: true})
title: string;
#OneToMany(type => ChatRoomMessageEntity, chatrmsg => chatrmsg.chatRoom)
messages: ChatRoomMessageEntity[];
#ManyToMany(type => UserEntity, user => user.activeChatRooms)
activeUsers: UserEntity[];
}
ChatRoomMessageEntity
#Entity()
export class ChatRoomMessageEntity {
#PrimaryGeneratedColumn()
id: number;
#Column('varchar', {nullable: true})
message: string;
#CreateDateColumn()
creationDate: Date;
#ManyToOne(type => UserEntity, user => user.chatRoomMessages)
fromUser: UserEntity;
#ManyToOne(type => ChatRoomEntity, chatRoom => chatRoom.messages)
chatRoom: ChatRoomEntity;
}
We can load sub-relations by using 'relation.subrelation' within the relations array itself like this:
relations: ['relation1', 'relation2', 'relation2.subrelation1']
So for your case, instead of using join you can simply do something like this:
this.entityManager.findOne(ChatRoomEntity, {
where: {id: roomToJoin.id},
relations: ['activeUsers', 'messages', 'messages.fromUser'],
}).then(roomEntity => {
...
This is specified here: https://github.com/typeorm/typeorm/blob/master/docs/find-options.md#basic-options
Using QueryBuilder
await getRepository(UserEntity)
.createQueryBuilder('user')
.leftJoinAndSelect('user.profile', 'profile')
.leftJoinAndSelect('profile.images', 'images')
.getMany()
Using FindOptions
await getRepository(UserEntity).find({
relations: ['profile', 'profile.images']
})
Update v0.4.0
Typeorm is ditching findOne, you should use findOneBy for the queries without relations, and if you want relations, simply use find.
const users = await userRepository.find({
where: { /* conditions */ },
relations: { /* relations */ }
})
// users[0] if you want a first row
here is the #gradii/fedaco orm how to implement this feature.
use can use multi relations by method with
export class User extends Model {
#HasOneColumn({related: Profile})
profile;
}
export class Profile extends Model {
#HasOneColumn({related: Image})
image;
}
export class Image extends Model {
name;
}
eager load
User.createQuery().with('profile.image').get()
lazy load
const user = await User.createQuery().first();
const image await (await user.profile).image