[hibernate] 단방향/양방향 연관성 구현 (OneToMany, ManyToOne)

개요

Object Relational Mapping(ORM)을 사용하기 위해 도메인 주도 설계(Domain Driven Design)를 공부하던 중에 단방향/양방향 연관성에 대해 알게 되었고, 그것을 구현하려면 OneToOne, OneToMany, ManyToOne, ManyToMany를 사용해야 한다.
그러나 막상 모델을 저장하려 했더니 원하는 테이블과 컬럼에 저장하는 것이 쉬운일이 아니였다. 예제를 보면서 확인해보자.

예제

먼저 예제는 부서인 Department 안에 속하는 사람들을 Member 로 표현하겠다.


테이블 구조


 tbl_dept

 dept_no


 tbl_member

 member_no

dept_no 


목표 : 객체 간의 양방향/단방향 레퍼런스를 위 테이블 구조로 저장


예제1 : 양방향 (Department <-> Member)


Department.java

package com.tistory.jekalmin.domain;

import java.util.Collection;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="tbl_dept")
public class Department {

    @Id
    @GeneratedValue
    private int deptNo;

    @OneToMany(mappedBy="dept")
    private Collection<Member> members;


}

mappedBy 이후에 들어오는 문자열은 Member.java에서 Department를 가리키는 변수명과 일치해야한다.

Member.java

package com.tistory.jekalmin.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name="tbl_member")
public class Member {

    @Id
    @GeneratedValue
    private int memberNo;

    @ManyToOne
    @JoinColumn(name="dept_no")
    private Department dept;

}

JoinColumn은 명시하지 않아도 알아서 ID를 찾지만, 그럴 경우 컬럼 명이 원하는 대로 생성되지 않기 때문에 JoinColumn을 이용해 명시해주었다.



예제2 : 단방향 (Department <- Member)


이번 예제에서는 Member에서만 Department를 참조하는 단방향 예제이다.

Department.java

package com.tistory.jekalmin.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="tbl_dept")
public class Department {

    @Id
    @GeneratedValue
    private int deptNo;

}

Member.java

package com.tistory.jekalmin.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name="tbl_member")
public class Member {

    @Id
    @GeneratedValue
    private int memberNo;

    @ManyToOne
    @JoinColumn(name="dept_no")
    private Department dept;

}

양방향과 다른 점이라면 Department에서는 Member를 더이상 가지고 있지 않다.



예제3 : 단방향 (Department -> Member)


이번에는 Department에서만 Member를 참조하는 단방향 예제이다.

Department.java

package com.tistory.jekalmin.domain;

import java.util.Collection;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="tbl_dept")
public class Department {

    @Id
    @GeneratedValue
    private int deptNo;

    @OneToMany
    @JoinColumn(name="dept_no")
    private Collection<Member> members;

}

Member.java

package com.tistory.jekalmin.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name="tbl_member")
public class Member {

    @Id
    @GeneratedValue
    private int memberNo;

}

이전 예제들과 약간 다른 점이라면, 이전에는 JoinColumn을 썼던 이유가 원하는 컬럼명을 명시하기 위해서 였는데, 이번 예제에서는 JoinColumn을 사용하지 않으면 전혀 다른 테이블 구조가 생성이 된다. 그리고 Department.java의 JoinColumn 안에 들어가는 컬럼명은 tbl_member 에서 tbl_dept를 참조할 컬럼명이다.

JoinColumn을 제외하고 실행하면 아래와 같은 테이블이 생성된다.

create table tbl_dept (dept_no integer generated by default as identity (start with 1), primary key (dept_no));

create table tbl_dept_members (tbl_dept_deptNo integer not null, members_memberNo integer not null);

create table tbl_member (member_no integer generated by default as identity (start with 1), primary key (member_no));

tbl_dept_members라는 원하지 않았던 테이블이 생기고, tbl_member는 dept_no를 가지고 있지 않은 구조로 된다.

그래서 JoinColumn을 이전 두 예제에서는 생략해도 컬럼명만 바뀌지만, 이 경우엔 테이블 구조가 예상치 못한 결과가 나왔다.



결론

OneToMany, ManyToOne 어노테이션은 위에 달아만 주면 알아서 구조를 만들어 주기는 하지만, 그 구조가 내가 생각했던 테이블과 컬럼 구조 인지는 잘 확인해 보시길 바란다. 위 예제는 전부 같은 테이블 구조를 생성한다.