TIL

23/12/29 TIL __ typeOrm 관계설정 에러 (외래키 Null)

GABOJOK 2023. 12. 29. 23:30

 

 

오늘은 typeOrm 에서 관계설정시 외래키 값으로 Null이 들어가는 상황에 대해 적어보려 한다. 

 

상황은 이랬다. 

3개의 테이블간에 1: N 관계를 맺었고,

데이터가 제대로 들어가지만 외래키만 null 로 입력이 되었다. 

 

 

이를 해결하기 위해, 일단 관계 설정을 제대로 했는지, 

또한 데이터베이스가 생성되는 순서는 올바른지 확인해 봤지만 공식문서와 비교해 봤을때 똑같기에 이상이 없다고 생각되었다. 

 

스텍오버플로우나 구글에 검색해 봐도 해결책을 얻지 못했고,  챗 gpt 에게 물어봐도 원하는 해결책을 얻을 수 없어 튜터님께 찾아갔다. 

사실 정말 간단한 문제였는데, 결론적으로 2가지 해결방법을 알게 되었다. 

 

 

먼저 코드는 이랬다. 

const newConcertHall = await queryRunner.manager
    .getRepository(ConcertHall)
    .save({
      name: concertHallName,
      totalSeats,
});


const concertHallId = newConcertHall.id;

seatGrade.forEach((grade, j) => {
	seatGradePrice.forEach(async (price, i) => {
		if (i === j) {
			const newSeatGrade = await this.seatGradeRepository.save({
				concertHallId, 
				grade,
				price,
			});
		}
	});
});

 

 

사실 처음에는 concertHallId 라는 애를 적지 않아도 자동으로 생성될 것으로 기대했다. 

그렇지만 그렇게 작동하지 않자, 추가한 코드였다. 

이렇게 수정하면서 entity 코드도 아래와 같이 수정해서 concertHallId 라는 이름으로 사용할 수 있을줄 알았다. 

@Entity()
export class SeatGrade {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToOne((type) => ConcertHall, (concertHall)=> concertHall.seatGrade, {
  	concertHall: ConcertHall;
  })
  @JoinColumn({name: 'concertHallId', referencedColumnName: 'id'})
  concertHall: ConcertHall;

}

물론 그럼에도 작동하지 않았지만..ㅋㅋㅋ

 

 

이렇게 해서는 안되었다. 

왜냐하면, 디비에는 있지만, 코드에서는 인식을 못하기 때문.

이걸 하려면 따로 이 필드 명을 명시적으로 말해줘야 하는데 이부분에 있어서는 조금 뒤에 말하도록 하겠다. 

 

그럼 이 상황에서는 어떻게 해야 외래키 값에 null이 아닌 제대로 된 값을 넣을 수 있을까? 

 

 

첫번째로 제시할 방법은 저장한 데이터 자체를 넣어주는 것이다. 

const newConcertHall = await queryRunner.manager
    .getRepository(ConcertHall)
    .save({
      name: concertHallName,
      totalSeats,
});


const concertHallId = newConcertHall.id;

seatGrade.forEach((grade, j) => {
	seatGradePrice.forEach(async (price, i) => {
		if (i === j) {
			const newSeatGrade = await this.seatGradeRepository.save({
				concertHall : newConcertHall, 
				grade,
				price,
			});
		}
	});
});

 

concertHallId 라는 애는 엔티티에서 설정을 해주었지만, 여전히 코드에서는 읽지 못하는 상태이기 때문에,

코드에서는 인식할 수 없다. 

대신 인식할 수 있는 newConcertHall을 통해 입력을 하면, 제대로 된 외래키 값이 들어온다. 

 

 

그럼 이렇게 하지 않고, 단순히 아이디 값 만으로 관계설정을 하고싶다면 방법은 없는걸까?

아니다. 아래와 같은 방법이 있다.

 

두번째 방법

 

concert.service.ts 파일

const newConcertHall = await queryRunner.manager
    .getRepository(ConcertHall)
    .save({
      name: concertHallName,
      totalSeats,
});


const concertHallId = newConcertHall.id;

seatGrade.forEach((grade, j) => {
	seatGradePrice.forEach(async (price, i) => {
		if (i === j) {
			const newSeatGrade = await this.seatGradeRepository.save({
				concertHallId, 
				grade,
				price,
			});
		}
	});
});

 

entitie.ts파일

@Entity()
export class SeatGrade {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToOne((type) => ConcertHall, (concertHall)=> concertHall.seatGrade, {
  	concertHall: ConcertHall;
  })
  @JoinColumn({name: 'concertHallId', referencedColumnName: 'id'})
  concertHall: ConcertHall;
  
  @Column({type: 'int})
  concertHallId: number;

}

 

 

이런식으로 하면 코드에서 concertHallId 라는 애를 읽을 수 있어서 입력이 가능하다. 

 

 

다른 방법으로 튜터님께서 말씀해주신 @RelationId 라는 애를 이용해서 하는 것도 시도해 보았다. 

아래 내용은 공식문서에서 가져온 내용이다.

 @RelationId
Loads id (or ids) of specific relations into properties.
For example, if you have a many-to-one category in your Post entity, you can have a new category id by marking a new property with @RelationId.

This functionality works for all kind of relations, including many-to-many:

Relation id is used only for representation. The underlying relation is not added/removed/changed when chaining the value.

 

@Entity()
export class Post {
    @ManyToOne((type) => Category)
    category: Category

    @RelationId((post: Post) => post.category) // you need to specify target relation
    categoryId: number
}
@Entity()
export class Post {
    @ManyToMany((type) => Category)
    categories: Category[]

    @RelationId((post: Post) => post.categories)
    categoryIds: number[]
}

 

 

시도해 봤지만 제대로 작동하지 않았다. 

공식문서와 똑같이 사용했는데 왜 작동하지 않는지 의문이 들어 다시 찾아보니, 아래와 같은 자료를 발견했다.

 

https://stackoverflow.com/questions/61361008/typeorm-insert-with-relationid

 

TypeORM insert with relationId

I use TypeORM, and simply I want to insert a row by using relationId. But it's not working as I expected. Here is my entity: @Entity() export default class Address extends BaseEntity { @Column...

stackoverflow.com

The `@RelationId` decorator works in one direction. As written in the [documentation](https://github.com/typeorm/typeorm/blob/master/docs/decorator-reference.md): > Loads id (or ids) of specific relations into properties. > > Relation id is used only for representation. The underlying relation is not added/removed/changed when chaining the value. As suggested, You can use the `@Column` decorator to to update the value.

 

한마디로 가져와 사용하는건 가능하지만 그걸 가지고 데이터 추가, 삭제 , 변경은 불가하다는 말이다. 

 

 

데이터 베이스 erd 설계와 함께 생각하다 보니 더 복잡하게 생각했던 것 같은데, 

하나씩 작게 떼어내서 생각해야 겠다.