TypeOrm에서 Lazy Loading이 되지 않는 문제

윤학·2023년 9월 17일
0

MySQL

목록 보기
3/3
post-thumbnail
post-custom-banner

문제

TypeORM을 사용하다 @OneToOne 관계의 객체들을 항상 가져올 필요가 없어서 Lazy Loading으로 변경하고자 했다.

하지만 계속해서 처음 조회한 이후 다시 로드하려 해도 불러오질 못했고, 쿼리도 나가질 않았다.

빠르게 코드로 아래와 같은 @OneToOne 관계의 상황을 살펴보자.

import { Entity, OneToOne, PrimaryGeneratedColumn } from "typeorm";
import { UserPhone } from "./a";

@Entity('test_user')
export class TestUsera {
    @PrimaryGeneratedColumn('increment')
    id: number;

    @OneToOne(() => UserPhone, (phone) => phone.user, { lazy: true,cascade: ['insert' ]})
    phone: Promise<UserPhone>

    constructor(phone: UserPhone) {
        this.phone = Promise.resolve(phone);
    }
}
import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from "typeorm";
import { TestUsera } from "./b";

@Entity('phone')
export class UserPhone {
    @PrimaryGeneratedColumn('increment')
    id: number;

    @Column({ nullable: true})
    value: string

    @OneToOne(() => TestUsera, { onDelete: 'CASCADE'}) 
    @JoinColumn({ referencedColumnName: 'id' })
    user: TestUsera;
}

UserPhone Entity를 Lazy Loading이 되도록 설정하였으니 정상적으로 동작하는지 간단히 검사해보자.

	it('테스트', async() => {
        const c = new UserPhone();
        const a = new TestUsera(c);
        await repo.save(a);

        const [result] = await repo.find();
        console.log(result)
        console.log('------------------------');
        console.log(await result.phone)
        console.log('------------------------')

        await repo.delete({})
    })

UserPhone Entity를 생성자 초기화로 TestUsera Entity와 함께 설정하고 저장하였다.

그리고 조회할 때 정상적이라면 await result.phone부분에서 UserPhone Entity를 로드하기 위해 쿼리를 날려야 하는데...

아무일도 일어나지 않는다.

명확한 해결법이 없어서 TypeORM 문서에서 비슷한 글을 찾아 추측해봤는데 Lazy Loading으로 설정된 객체를 생성자에서 초기화 해주고 있어서 DB로부터 로드되는 시점에 이미 해당 Entity도 초기화를 수행한게 아닐까 싶다.

해결

생성자 초기화를 하지 않고 Setter로 초기화 해보자.

import { Entity, OneToOne, PrimaryGeneratedColumn } from "typeorm";
import { UserPhone } from "./a";

@Entity('test_user')
export class TestUsera {
    @PrimaryGeneratedColumn('increment')
    id: number;

    @OneToOne(() => UserPhone, (phone) => phone.user, { lazy: true,cascade: ['insert' ]})
    phone: Promise<UserPhone>

    setPhone(userPhone: UserPhone) { this.phone = Promise.resolve(userPhone)}
}
import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from "typeorm";
import { TestUsera } from "./b";

@Entity('phone')
export class UserPhone {
    @PrimaryGeneratedColumn('increment')
    id: number;

    @Column({ nullable: true})
    value: string

    @OneToOne(() => TestUsera, (user) => user.phone, { onDelete: 'CASCADE'}) 
    @JoinColumn({ referencedColumnName: 'id' })
    user: TestUsera;
}

그리고 테스트를 돌려보자.

    it('테스트', async() => {
        const c = new UserPhone();
        const a = new TestUsera();
        a.setPhone(c);
        await repo.save(a);

        const [result] = await repo.find();
        console.log(result)
        console.log('------------------------');
        console.log(await result.phone)
        console.log('------------------------')

        await repo.delete({})
    })

정상적으로 수행되는 것을 확인할 수 있다.

로드 시점이 일정하지 않을 때 생성자에서 초기화를 수행하면 이후 저장하거나 업데이트 하는 과정에서 덮어버릴 수도 있으니 꼭 필요한 값만 생성자에서 초기화를 수행하도록 하자.

틀린 내용이 있다면 알려주세요!

참고

Avoid relation property initializers

profile
해결한 문제는 그때 기록하자
post-custom-banner

0개의 댓글