DB 구조에 따라 일대다, 다대일, 다대다 등의 많은 관계가 있을 수 있음
일대다, 다대일은 다측에 외래키를 두고 다대다는 테이블 하나로 뺌
연관관계 종류: 단방향, 양방향
단뱡향: 외래 참조를 한 객체만 하는 것. ex) 회원과 팀 관계(일대다) 팀 객체만 회원의 외래 참조 객체를 두는 것
양방향: 외래 참조를 양 쪽에서 하는 것 ex) 팀뿐만 아니라 회원 객체에도 팀에 해당하는 외래 참조 객체를 두는 것
양방향의 경우 회원에서도 접근 가능하고 팀에서도 접근 가능해서 양쪽에서 다르게 데이터를 수정할 수 있기 때문에 누가 DB상 데이터를 수정할 권한을 갖는지 정해주는게 필요 이게 연관관계 주인 주인이 아닌 애는 조회만 가능
하지만 객체상에서는 양쪽 다 데이터를 추가해주는게 편함 그래서 필요한 게 연관관계 메소드. 연관관계 메소드는 팀이든 회원이든 어느 객체에 두든 상관없지만 하나에만 놔둬도 됨.
// team 객체에서 다음과 같이 지정. member은 외부에서 받은 회원 객체임
this.team.member.add(member);
member.team = this;
따라서 연관관계 메소드는 양방향 매핑일 시에만 필요함-적어도 JPA에서는,,? 스프링에서는
일단 단방향으로 설계하되, 양방향이 필요할 것 같으면 양방향 추가해라
ex) A 팀에 무슨 회원이 있는지 조회하는 기능 뿐만 아니라 각 회원이 어느 팀에 소속되어있는지 바로 확인하고 싶은 경우에 양방향 추가
@JoinColumn - 연관관계 주인을 설정하는 어노테이션. (단방향, 양방향 매핑 모두 사용함. 다만 양방향에서는 외래 참조 객체 2개 중 무조건 1개만 써야됨)
이걸 붙이면 붙여준 쪽의 테이블에 외래키 필드가 자동으로 생기고 상대 엔티티를 참조함.
ex) 팀 테이블에 member 넣고 @JoinColumn 붙이면 DB에도 memberId로 자동 반영
The side you set @JoinColumn on, that side's table will contain a "relation id" and foreign keys to target entity table.
연관 관계의 주인이 아닌(외래키가 없는) 엔티티를 먼저 저장하는게 좋다. 외래키가 있는 회원을 저장한다고 하면, 회원 객체의 외래키를 null로 설정해주고 이후에 이 외래키에 다른 값을 넣어주기 때문에 쿼리가 한번 더 날라가기 때문.
eager relation, lazy relation 주의
Note: if you came from other languages (Java, PHP, etc.) and are used to use lazy relations everywhere - be careful. Those languages aren't asynchronous and lazy loading is achieved different way, that's why you don't work with promises there. In JavaScript and Node.JS you have to use promises if you want to have lazy-loaded relations. This is non-standard technique and considered experimental in TypeORM
UserEntity
@Entity("user") // user 테이블과 매핑
export class UserEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 32 })
username: string;
@Column()
password: string;
@BeforeInsert()
async hashPassWord() {
this.password = await argon2.hash(this.password);
}
}
PostEntity
@Entity("post")
export class PostEntity {
@PrimaryGeneratedColumn()
id: number;
title: string;
content: string;
@ManyToOne(()=>UserEntity)
writer: UserEntity;
}
post create 시
PostService
async create(createPostDto: CreatePostDto) {
const newPost = new PostEntity();
newPost.content = createPostDto.content;
newPost.title = createPostDto.title;
const writer = await this.userRepository.findOne(createPostDto.writerId);
if (!writer) {
throw new HttpException({
status: HttpStatus.BAD_REQUEST,
error: '잘못된 ID입니다',
}, HttpStatus.BAD_REQUEST);
}
// 여기
newPost.writer = writer;
return await await this.postRepository.save(newPost);
}
@JoinColumn이 있는, 즉 외래 참조 객체가 있는 엔티티에만 추가해주면 됨
당연함. 하나밖에 없기 때문
결론: JPA + Spring 처럼 연관관계 매핑(양쪽에서 하는 매핑) 해줄필요 없다.
@Column이나 @JoinColumn 안붙었으면 db에도 필드 안생김. 즉, 연관관계 주인 아닌 쪽에 있는 외래 참조 객체는 db에 외래키로 반영 안됨.
UserEntity
@Entity("post")
export class PostEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
content: string;
// 양방향 시
@ManyToOne(()=>UserEntity, (writer)=>writer.post)
@JoinColumn()
writer: UserEntity;
}
PostEntity
async create(createPostDto: CreatePostDto) {
const newPost = new PostEntity();
newPost.content = createPostDto.content;
newPost.title = createPostDto.title;
const writer = await this.userRepository.findOne(createPostDto.writerId);
if (!writer) {
throw new HttpException({
status: HttpStatus.BAD_REQUEST,
error: '잘못된 ID입니다',
}, HttpStatus.BAD_REQUEST);
}
newPost.writer = writer;
return await this.postRepository.save(newPost);
}
단방향 때와 동일하다.