Nestjs one to many relationship save issue - nestjs

I have a service where I'm trying to save an entity with relation entities(one to many).What I have is:
Payment entity:
#Entity('payment')
export class PaymentEntity {
#PrimaryColumn()
id: number;
#OneToMany(type => PaymentExamEntity, paymentExam => paymentExam.payment, { cascade: true })
paymentExams: PaymentExamEntity[];
...
}
PaymentExamEntity:
#Entity('payment_exam')
export class PaymentExamEntity {
#PrimaryColumn()
id: number;
#ManyToOne(type => PaymentEntity, payment => payment.paymentExams)
#JoinColumn({name: 'payment_id'})
payment: PaymentEntity;
....
}
PaymentService:
#Injectable()
export class PaymentService {
constructor(#InjectRepository(PaymentEntity) private paymentRepo: Repository<PaymentDTO>,
#InjectRepository(PaymentExamEntity) private paymentExamRepo: Repository<PaymentExamDTO>) { }
async create(data: PaymentDTO) {
const payment = this.paymentRepo.create(data);
await this.paymentRepo.save(payment);
for(let item of data.paymentExams){
item.payment = payment;
const paymentExams = this.paymentExamRepo.create(data.paymentExams);
this.paymentExamRepo.save(data.paymentExams);
}
return payment;
}
I can see that the payment entity is saved but the paymentExams entities failed to saved due to missing foreign key value.
[Nest] 11545 - 01/01/2021, 12:15:52 AM [ExceptionsHandler] ER_NO_DEFAULT_FOR_FIELD: Field 'payment_id' doesn't have a default value
The problem is that the payment object does not updated with auto generated id from database after save.Thus the assignment of foreign key is null. Is there any other solution for this. How can I store child entities?
Thanks in advance

save method returns an object of type Payment with id field. You can store it in a variable and use it in item.payment assignment.

Ok I managed to make it work with the following changes:
1. Payment entity changed id field annotation from #PrimaryColumn() to #PrimaryGeneratedColumn()
2. PaymentExam entity changed id field annotation from #PrimaryColumn() to #PrimaryGeneratedColumn()
In the payment service I can have only this:
async create(data: PaymentDTO) {
const payment = this.paymentRepo.create(data);
await this.paymentRepo.save(data);
return payment;
}
Also I have Auto Increment for the primary key in this 2 tables using mysql database

Related

Why is inverseSide of #ManyToOne not hydrated on entity save?

Starting from default npx typeorm init project:
#Entity()
export class A {
#PrimaryGeneratedColumn()
id!: number
#OneToMany(() => B, (b) => b.a)
bs: B[]
constructor(bs: B[]) {
this.bs = bs
}
}
#Entity()
export class B {
#PrimaryGeneratedColumn()
id!: number
#ManyToOne(() => A, (a) => a.bs)
a: A
constructor(a: A) {
this.a = a
}
}
AppDataSource.initialize().then(async () => {
const a = await AppDataSource.manager.save(new A([]))
await AppDataSource.manager.save(new B(a))
await AppDataSource.manager.save(new B(a))
await AppDataSource.manager.save(new B(a))
console.log(a.bs.length)
}).catch(error => console.log(error))
This displays 0 (a.bs is still empty).
I would expect that a.bs is hydrated with the saved B's. Isnt that the whole purpose of supplying an inverse mapping on the #ManyToOne anyway?
Or, if I'm getting this wrong, what's the proper way of maintaining DB<->model consistency when adding relationed entities?
Your mapping definitions are correct. The issue is that the object a was initialised with empty array for the values of bs and so a.bs.length returns 0.
While you created multiple records of entity B afterwards. Object a isn't aware of those. Typeorm won't trace or find all entities in your project and refresh them now that some relations/mappings in the database has changed. In order to refresh the mappings, you'll need to reload a from the database:
await a.reload();
so that a now knows about the updated mappings.

Circular dependency between entities

i have three entities TestUser , TestProfile and TestPhoto in which TestUser has a OneToOne relationship with TestProfile and TestProfiles has a OneToOne relationship with TestPhoto and at the las TestPhoto has this ManyToOne relationship with User which might has not been created yet
im using cascade when defining my entites and i wish to have them all get created with a single call in my UserService but facing this Cyclic dependency: "TestPhoto" Error and had no progress since then , i see its not probably what is should do in real life scenarios but apart from that ,any possible hack for it or its just fundamentally not possible?
#Entity()
#Unique(["name"])
export class TestUser {
#PrimaryGeneratedColumn()
id: number;
#Column()
name: string;
#OneToOne(() => TestProfile,{
cascade:true,
nullable:true
})
#JoinColumn()
profile: TestProfile;
#Column({nullable:true})
profileId: number
#OneToMany(() => TestPhoto, photo => photo.user)
photos: TestPhoto[];
}
#Entity()
export class TestProfile {
#PrimaryGeneratedColumn()
id: number;
#Column()
gender: string;
#OneToOne(type=>TestPhoto,{
cascade:true,
nullable:true
})
#JoinColumn()
photo: TestPhoto;
#Column({nullable:true})
photoId: number
}
#Entity()
export class TestPhoto {
#PrimaryGeneratedColumn()
id: number;
#Column()
url: string;
#ManyToOne(() => TestUser, user => user.photos,{
cascade:true,
nullable:true
})
user: TestUser;
#Column({nullable:true})
userId: number;
}
and in my UserService abstracted the calls as followed
const user = new TestUser();
const profile1 = new TestProfile();
const photo1 = new TestPhoto();
photo1.user = user;
profile1.photo = photo1;
user.profile = profile1
await connection.manager.save(user);
Does these entities are living in the same file?
I use import type TS's feature to resolve cyclic dependencies at module resolution level. I'm not sure if that is your case tho.
Before you write code please feel free to understand the concept of circular dependency; Link. It is possible to have circular dependency in your case but might not be in real life scenarios. What you have to do is make your Entity/Modal a forwardRef on both side. Then make services inject-able to others using #Inject(forwardRef(() => YourService)) inside constructor of another service. If you did not get an idea I will post a complete example of how circular dependency works in your case and in real life scenarios.
I solved this issue with typeorm Relation as explaine here tyopeorm entities circular dependencies
I use a common import file when there is a circular dependency problem:
// common.ts
// also it's important to keep order, parent first and then...
export * from './parent.entity';
export * from './child1.entity';
export * from './child2.entity';
// parent.entity.ts ------------------------------------
import { Child1Entity, Child2Entity } from './common.ts'
export class ParentEntity {
#OneToOne(() => Child1Entity, child => child.parent)
public child1: Child1Entity[];
#OneToOne(() => Child2Entity, child => child.parent)
public child2: Child2Entity[];
}
// child1.entity.ts ----------------------
import { ParentEntity } from './common.ts'
export class Child1Entity extends Parent {
#OneToOne(() => ParentEntity, parent => parent.child1)
#JoinColumn()
public parent: ParentEntity;
}
// child2.entity.ts ----------------------
import { ParentEntity } from './common.ts'
export class Child2Entity extends Parent {
#OneToOne(() => ParentEntity, parent => parent.child2)
#JoinColumn()
public parent: ParentEntity;
}
This pattern can help you with various circular dependency problems

Attribute contains only ID, not whole entity, in persisted TypeORM entity

I've got this entity class:
#Entity("organization")
export class OrganizationEntity {
// ...
#PrimaryColumn({name: "party_id"})
#OneToOne(() => PartyEntity, {cascade: true})
#JoinColumn({name: "party_id", referencedColumnName: "id"})
party: PartyEntity
}
Then I create a new OrganizationEntity and persist it:
const savedOrganizationEntity = await this.organizationTypeOrmRepository.save(organizationEntity);
// see Repository.save
However, the returned savedOrganizationEntity contains a string in the field party, not a PartyEntity object.
How can I fix this behaviour, so that OrganizationEntity.party contains a PartyEntity, not a string?
The behaviour is working as designed: https://github.com/typeorm/typeorm/issues/3490

Removing many2many entity in typeorm with cascade

I have an entity called Entry and it relates to another entity called Image as Many2Many.
Here is what the Entry & Image relationships look like:
#Entity('entry')
export class EntryEntity extends BaseEntity implements IDeserializable<EntryEntity> {
#ManyToMany(type => ImageEntity, image => image.entries, { onDelete: 'CASCADE', cascade: true })
#JoinTable()
images: ImageEntity[];
}
and the Image entity class:
#Entity('image')
export class ImageEntity extends BaseEntity implements IDeserializable<ImageEntity> {
#ManyToMany(type => EntryEntity, entry => entry.images)
entries: EntryEntity[];
}
The method I use to delete an entry:
public async delete(entryId: number): Promise<void> {
const queryRunner = this.connection.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
await queryRunner.manager.getRepository(EntryEntity)
.createQueryBuilder('entry')
.delete()
.from(EntryEntity)
.where('entry.id = :entryId', { entryId })
.execute();
await queryRunner.commitTransaction();
} catch (err) {
await queryRunner.rollbackTransaction();
} finally {
await queryRunner.release();
}
}
Expected behaviour:
If I delete some entry then all its images should also be deleted.
Factual behaviour:
Entry gets removed from its table, it also gets removed from the entry_images_image table
but the images associated with this entry stay (they are still present in the image table).
I'm not very familiar with TypeOrm, why does it happen? I would highly appreciate some help.
what the cascade does is to remove the relations in both sides, not the entities themselves. so in you'r case, you will only receive images without relations, as you have mentioned.
Look at it like this: let's say that you have multiple images for multiple entries, all connected to each other. if you delete one entry, would you really like the image entity to be fully deleted? because in that case you will remove alot of data that hasn't been removed.

Nestjs, How to get entity table name?

How to get entity table name ? (ex: member-pre-sale-detail)
I want to set table comment
// Seeder: Clear & set Comment
export default class ClearAllSeed implements Seeder {
public async run(factory: Factory, connection: Connection): Promise<void> {
const deleteEntities = [
{table: OrderHead, comment: '訂單/主表'},
]
for(const entity of deleteEntities){
await connection
.createQueryBuilder()
.delete()
.from(entity.table)
.execute();
await connection
// >>>> but table name is MemberPreSaleDetail not member-pre-sale-detail
.query(`alter table ${entity.table.name} comment '${entity.comment}'`);
}
}
}
// Sampel Entity
#Entity('member-pre-sale-detail')
export class MemberPreSaleDetail {
#PrimaryGeneratedColumn({unsigned: true})
id?: number;
#Column({comment: '幾批(整批)', type: 'mediumint', default: 0})
batchQty: number;
}
Expected behavior
get the 'member-pre-sale-detail' string
Environment
Nest version: 7.0.7
For Tooling issues:
- Node version: v14.5.0
- Platform: Mac
I am guessing you are using TypeORM. In that case:
You could get the entity metadata by calling connection.getMetadata(MemberPreSaleDetail).
This method returns an EntityMetadata, which has name, tableName and givenTableName properties. For your usecase I guess you could simply use givenTableName.

Resources