node.js - TypeORM ایجاد یک رکورد با کلید خارجی
من دو موجودیت questionnaires و sections دارم. یک پرسشنامه چندین بخش دارد بنابراین رابطه OneToMany است.
وقتی سعی میکنم یک رکورد جدید برای پرسشنامه ایجاد کنم - همه چیز درست عمل میکند. یک شناسه دریافت میکنم و سپس یک رکورد بخش ایجاد میکنم و آن شناسه را در آنجا مرجع میکنم.
پاسخ، وقتی تلاش میکنم یک بخش ایجاد کنم، این است
{
"name": "Section 1",
"questionnaire_id": 1,
"description": "Section 1 description",
"id": 1
}
همانطور که میبینید، questionnaire_id را برمیگرداند، اما مشکل این است که آن رکورد در پایگاه داده questionnaire_id=null است. اگر آن را بهصورت دستی تغییر دهم و یک مقدار شناسه به آن اختصاص دهم، سپس کار میکند و من سند JSON خود را با بخشها در آن دریافت میکنم.
اگر تمام رکوردها را از پایگاه داده بگیرم، پس این را دریافت میکنم
[
{
"id": 1,
"name": "Section 1",
"description": "Section 1 description"
}
]
questionnaire_id موجود نیست زیرا null است. 😕.
میدانی ممکن است چه کاری را اشتباه انجام میدهم؟ احساس میکنم به چیزی مربوط به رابطه موجودیت است اما دقیقاً مطمئن نیستم.
@Entity({ name: 'questionnaires' })
export class Questionnaire {
@PrimaryGeneratedColumn()
id: number;
@Column({ type: 'varchar', length: 128 })
name: string;
@Column({ type: 'enum', enum: STATUS, default: STATUS.ACTIVE })
status: STATUS;
@OneToMany(() => Section, (section) => section.questionnaire)
@JoinColumn({ name: 'questionnaire_id' })
sections: Section[];
}
@Entity({ name: 'sections' })
export class Section {
@PrimaryGeneratedColumn()
id: number;
@Column({ type: 'varchar', length: 128 })
name: string;
@Column({ type: 'text', default: '' })
description: string;
@ManyToOne(() => Questionnaire, (questionnaire) => questionnaire.sections)
questionnaire: Questionnaire;
}
هدف این است که همه پرسشنامهها همراه با بخشهایشان جمعآوری شوند.
راهی که من در تلاش هستم به آن برسم این است
@Injectable()
export class QuestionnaireService {
constructor(@InjectRepository(Questionnaire) private readonly questionnaire: Repository<Questionnaire>) {}
create(createQuestionnaireDto: CreateQuestionnaireDto) {
return this.questionnaire.save(createQuestionnaireDto);
}
findAll() {
return this.questionnaire.find({
relations: ['sections'],
});
}
}
@Injectable()
export class SectionService {
constructor(@InjectRepository(Section) private readonly sectionRepository: Repository<Section>) {}
create(createSectionDto: CreateSectionDto) {
return this.sectionRepository.save(createSectionDto);
}
findAll() {
return this.sectionRepository.find();
}
}
export class CreateSectionDto {
@ApiProperty({
description: 'Section name',
example: 'Section 1',
})
@IsString()
@IsNotEmpty()
@MaxLength(128)
name: string;
@ApiProperty({
description: 'Relation to the questionnaire',
example: 1,
})
@IsNumber()
questionnaire_id: number;
@ApiProperty({
description: 'Section description',
example: 'Section 1 description',
default: '',
})
@IsString()
@IsOptional()
@MaxLength(512)
description?: string;
}
پ.ن.
من این ردیف درج TypeORM را با کلید خارجی بررسی کردم
اما برای من کار نکرد.
ویرایش :
این همان پرسوجویی است که هنگام اجرای دستور درج اجرا میشود
query: START TRANSACTION
query: INSERT INTO "sections"("created_at", "updated_at", "name", "description", "questionnaire_id") VALUES (DEFAULT, DEFAULT, $1, $2, DEFAULT) RETURNING "created_at", "updated_at", "id", "description" -- PARAMETERS: ["Section 1","Section 1 description"]
query: COMMIT
من همچنین فیلدهای "created_at" و "updated_at" را دارم اما آنها را در مثالهای بالا وارد نکردم.
همانطور که مشخص است، شامل questionnaire_id میشود اما به دلایلی مقدار آن روی DEFAULT تنظیم شده است. مقداری که از طریق درخواست HTTP ارسال میکنم، منتقل نمیشود.
راهحل
مشکل مربوط به رویکرد من در ایجاد رکوردها بود. از آنجا که من یک رابطه پایگاه داده تنظیم کردهام و جدول Section وابسته به جدول Questionnaire است - TypeORM اجازه نمیدهد رکوردهای Sections را با مقدار FK خودم ایجاد کنم. این کتابخانه به طور خودکار این کار را انجام میدهد اگر ابتدا Questionnaire را ایجاد کرده و شیء Sections را به آن اختصاص داده باشم.
با پیکربندی بالا، خط زیر بهخوبی کار میکند
async create(createQuestionnaireDto: CreateQuestionnaireDto) {
const questionnaire = this.questionnaire.create(createQuestionnaireDto);
const sections = new Section();
sections.name = 'Default Section';
sections.summary = 'Default Section Summary';
sections.questionnaire = questionnaire;
await this.questionnaire.save(questionnaire);
await this.section.save(sections);
return questionnaire;
}
و پرسوجویی که در حال اجرا است این است
query: START TRANSACTION
query: INSERT INTO "sections"("created_at", "updated_at", "name", "summary", "questionnaire_id") VALUES (DEFAULT, DEFAULT, $1, $2, $3) RETURNING "created_at", "updated_at", "id", "summary" -- PARAMETERS: ["Default Section","Default Section Summary",1]
query: COMMIT
همانطور که میبینید، هیچ DEFAULT برای questionnaire_id وجود ندارد.
اگر میخواستم رویکرد اولیهام کار کند، نباید از دکوراتورهای رابطهای TypeORM استفاده میکردم، که ایده نسبتاً بدی است.
پاسخ شماره ۱
از آنجایی که من زیاد از TypeORM استفاده نکردهام، پس کاملاً از جواب مطمئن نیستم، اما میتوانید این را بررسی کنید.
پس احتمالاً در تعریف رابطه موجودیت خود اشتباه میکنید. شما از @JoinColumn در موجودیت پرسشنامه استفاده میکنید.
باید در بخشهای موجودیت به صورت زیر تعریف شود:
@Entity({ name: 'sections' })
export class Section {
@PrimaryGeneratedColumn()
id: number;
@Column({ type: 'varchar', length: 128 })
name: string;
@Column({ type: 'text', default: '' })
description: string;
@ManyToOne(() => Questionnaire, (questionnaire) => questionnaire.sections)
@JoinColumn({ name: 'questionnaire_id' })
questionnaire: Questionnaire;
}
پاسخ بر اساس این سند است https://typeorm.io/relations#joincolumn-options. امیدوارم مشکل شما را حل کند.
ویرایش: (مربوط نیست، ممکن است منسوخ شده باشد اما در اسناد رسمی ذکر شده است) مطمئن هستید که بارگذاری روابط به این روش کار میکند؟
findAll() {
return this.questionnaire.find({
relations: ['sections'],
});
}
همانطور که در مدارک اینجا میبینم، آنها کار دیگری انجام میدهند.
findAll() {
return this.questionnaire.find({
relations: {
sections: true,
},
});
}
ویرایش ۲:
پس اگر حالا یک ستون به موجودیت بخش خود اضافه کنید، این کار خواهد کرد.
@Entity({ name: 'sections' })
export class Section {
@PrimaryGeneratedColumn()
id: number;
@Column({ type: 'varchar', length: 128 })
name: string;
@Column({ type: 'text', default: '' })
description: string;
@Column({ type: 'number' })
questionnaire_id: number;
@ManyToOne(() => Questionnaire, (questionnaire) => questionnaire.sections)
@JoinColumn({ name: 'questionnaire_id' })
questionnaire: Questionnaire;
}
به من اطلاع بده اگر این جواب میدهد.
