

데이터베이스 테이블의 연관관계를 설계하는 방법은 2가지이다.
조인 컬럼 사용(외래 키)
테이블 간에 관계는 주로 조인 컬럼을 사용해서 관리한다.
조인 테이블 사용(테이블 사용)
조인 테이블이라는 별도의 테이블을 사용해서 연관관계를 관리한다.
조인 컬럼과는 다르게, 연관관계를 관리하는 조인 테이블을 추가하고 여기서 두 테이블의 외래 키를 갖고 연관관계를 관리한다. 따라서 MEMBER, LOCKER에는 연관관계를 관리하기 위한 외래 키 컬럼이 없다.
하지만 조인 테이블을 사용하면 테이블이 늘어나서 메모리 공간을 많이 차지하기 때문에, 기본적으로 조인 컬럼을 사용하되 필요한 경우만 조인 테이블을 사용하는 것을 추천한다.

@Entity
public class Parent {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToOne
@JoinTable(name = "PARENT_CHILD",
joinColumns = @JoinColumn(name = "PARENT_ID"),
inverseJoinColumns = @JoinColumn(name = "CHILD_ID"))
private Child child;
}
@Entity
public class Child {
@Id
@GeneratedValue
@Column(name = "CHILD_ID")
private Long id;
private String name;
}

일대다 관계를 만들려면 조인 테이블의 컬럼 중 다(N)와 관련된 컬럼인 CHILD_ID에 유니크 제약조건을 걸어야 한다.
//부모
@Entity
public class Parent {
@Id @GeneratedValue
@Column(name="PARENT_ID")
private Long id;
private String name;
@OneToMany
@JoinTable(name="PARENT_CHILD",
joinColumns = @JoinColumn(name="PARENT_ID"),
inverseJoinColumns = @JoinColumn(name="CHILD_ID"))
private List<Child> child = new ArrayList<Child>();
}
//자식
@Entity
public class Child {
@Id @GeneratedValue
@Column(name="CHILD_ID")
private Long id;
private String name;
}
다대일은 일대다에서 방향이 반대로 된 것이다.
//부모
@Entity
public class Parent {
@Id @GeneratedValue
@Column(name="PARENT_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "parent")
private List<Child> child = new ArrayList<Child>();
}
//자식
@Entity
public class Child {
@Id @GeneratedValue
@Column(name="CHILD_ID")
private Long id;
private String name;
@ManyToOne(optional = false)
@JoinTable(name="PARENT_CHILD",
joinColumns = @JoinColumn(name="CHILD_ID"),
inverseJoinColumns = @JoinColumn(name="PARENT_ID"))
private Parent parent;
}

다대다 관계를 만들려면 조인 테이블의 두 컬럼을 합해서 하나의 복합 유니크 제약 조건을 걸어야 한다.
@Entity
public class Parent {
@Id @GeneratedValue
@Column(name="PARENT_ID")
private Long id;
private String name;
@ManyToMany
@JoinTable(name="PARENT_CHILD",
joinColumns = @JoinColumn(name="PARENT_ID"),
inverseJoinColumns = @JoinColumn(name="CHILD_ID"))
private List<Child> child = new ArrayList<Child>();
}
@Entity
public class Child {
@Id @GeneratedValue
@Column(name="CHILD_ID")
private Long id;
private String name;
}

잘 사용하지는 않지만 @SecondaryTable을 사용하면 한 엔티티에 여러 테이블을 매핑할 수 있다.
@SecondaryTable을 사용해서 두 테이블을 하나의 엔티티에 매핑하는 방법보다는
테이블당 엔티티를 각각 만들어서 일대일 매핑하는 것을 권장한다.
사실 대부분의 경우 Join Column으로 시작하고, 필요한 경우에만 Join Table을 사용하는 것이 일반적인 접근 방식이다.
따라서 가볍게 읽자.
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
// getters and setters
}
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();
// getters and setters
}
@Entity
public class Enrollment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "student_id")
private Student student;
@ManyToOne
@JoinColumn(name = "course_id")
private Course course;
private LocalDate enrollmentDate;
private String grade;
// getters and setters
}
이 구조를 사용하면 "어떤 학생이 어떤 과목을 수강하는지", "특정 과목을 수강하는 학생 목록", "학생의 특정 과목 성적" 등 다양한 정보를 효과적으로 관리하고 조회할 수 있다.
유니크 제약조건(Unique Constraint)은 데이터베이스 테이블의 특정 컬럼 또는 컬럼 조합에 대해 중복된 값을 허용하지 않는 규칙이다.
유니크 제약조건은 데이터 무결성을 보장하고, 중복 데이터 입력을 방지하는 데 사용된다. 예를 들어, 사용자 테이블의 이메일 주소나 주민등록번호 컬럼에 유니크 제약조건을 걸어 중복 가입을 방지할 수 있다.
유니크 제약조건을 SQL로 표현하면 다음과 같다.
-- 테이블 생성 시
CREATE TABLE Products (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
sku VARCHAR(255) NOT NULL,
CONSTRAINT unique_sku UNIQUE (sku)
);
-- 기존 테이블 수정 시
ALTER TABLE Products
ADD CONSTRAINT unique_sku UNIQUE (sku);
사진 출처 및 참고: 자바 ORM 표준 JPA 프로그래밍