반응형
해당 글에서는 Spring Boot JPA에서 엔티티 어노테이션 중 테이블 간의 관계에 사용되는 어노테이션에 대해 알아봅니다
💡 [참고] JPA 관련해서 구성 내용에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
분류 | 링크 |
Spring Boot Data JPA -1: ORM, JPA, Hibernate, QueryDSL 이론 | https://adjh54.tistory.com/421 |
Spring Boot Data JPA -2: 초기 환경 구성 + JpaRepository 활용 방법 | https://adjh54.tistory.com/422 |
Spring Boot Data JPA -3: 상세 JpaRepository 활용 방법 | https://adjh54.tistory.com/481 |
Spring Boot Data JPA 엔티티 어노테이션 -1 : 테이블 컬럼 단위 | https://adjh54.tistory.com/466 |
Spring Boot Data JPA 엔티티 어노테이션 -2 : 엔티티(테이블)간의 관계 | https://adjh54.tistory.com/477 |
Spring Boot Data JPA FetchType 이해하기 : 즉시/지연로딩 | https://adjh54.tistory.com/476 |
Spring Boot Data JPA + JPQL 활용 방법 | https://adjh54.tistory.com/479 |
Spring Boot Data JPA + Criteria API 활용 방법 | https://adjh54.tistory.com/483 |
Spring Boot Data JPA + QueryDSL 활용 방법-1 : 정의 및 구성요소 | https://adjh54.tistory.com/484 |
Spring Boot Data JPA + QueryDSL 활용 방법-2 : 초기 환경설정 및 활용예시 | https://adjh54.tistory.com/485 |
1) JPA 엔티티(Entity)
💡 JPA 엔티티(Entity)
- 데이터베이스의 테이블을 자바 클래스로 매핑하는 것을 의미합니다. 이 엔티티 클래스는 데이터베이스 테이블의 각 행을 표현하며, 클래스의 인스턴스는 특정 테이블의 한 행에 해당합니다.
- 데이터베이스 연산을 추상화하며 이를 통해 개발자는 SQL 쿼리에 의존하지 않고 데이터베이스와 상호작용할 수 있습니다. 이는 객체 지향 프로그래밍과 데이터베이스 간의 패러다임 불일치를 해결하는데 도움이 됩니다.
2) Entity 간의 관계 주요 어노테이션
💡 Entity 테이블 간의 관계 주요 어노테이션
- Entity 어노테이션 중에서 엔티티 간의 관계를 가지는 주요 어노테이션에 대해 알아봅니다.
1. 요약
어노테이션 | DB 매핑 타입 | 설명 |
@OneToOne | 1:1 관계 매핑 | 엔티티 간 1:1 관계를 정의합니다 |
@OneToMany | 1:N 관계 매핑 | 하나의 엔티티가 다른 엔티티와 1:N 관계를 가짐을 정의합니다 |
@ManyToOne | N:1 관계 매핑 | 하나의 엔티티가 다른 엔티티와 N:1 관계를 가짐을 정의합니다 |
@ManyToMany | N:N 관계 매핑 | 엔티티 간 N:N 관계를 정의합니다 |
@JoinColumns | 여러 외래키 컬럼 매핑 | 복합 키를 매핑할 때 사용합니다 |
@JoinColumn | 외래키 컬럼 매핑 | 외래키를 매핑할 때 사용합니다 |
@JoinTable | N:N 관계 매핑 | 엔티티 간의 N:N 관계를 가질 때 연결 테이블을 매핑할 때 사용합니다 |
3) 엔티티 어노테이션/속성 : 테이블 간의 관계
1. @OneToOne
💡 @OneToOne
- 테이블 간의 관계를 지정할 때 A 엔티티와 B 엔티티 간의 관계를 1:1 관계로 지정하는 데 사용하는데 어노테이션입니다.
- A 엔티티의 경우 하나의 row 데이터를 가지며, B 엔티티도 하나의 row 데이터를 가지는 구조입니다.
- 상황에 따라서 @OneToOne 어노테이션을 통해서 테이블 간의 INNER JOIN 또는 LEFT OUTER JOIN으로 구성될 수 있습니다.
1.1. @OneToOne 속성 및 속성값
속성 | 속성 타입 | 필수여부 | default | 설명 |
cascade | CascadeType | 선택 | 없음 | - 연관된 엔티티에 대해 특정 작업이 전파되도록 설정합니다. |
fetch | FetchType | 선택 | FetchType.EAGER | - 테이블 간의 연결 관계에서 EAGER을 통해 즉시 가져올지 LAZY를 통해 필요에 따라 가져올 지 전략을 설정합니다 |
mappedBy | String | 선택 | 없음 | - 양방향 연관관계에서 주인이 아닌 엔티티가 매핑을 위해 사용합니다. |
optional | boolean | 선택 | true | - 연관된 엔티티가 반드시 있어야 하는지 설정합니다. false일 경우, 연관된 엔티티는 반드시 있어야 합니다. |
orphanRemoval | boolean | 선택 | false | - 연관된 엔티티가 더 이상 관리되지 않을 때, 자동으로 삭제할지 설정합니다. |
targetEntity | Class | 선택 | 없음 | - 연관된 엔티티의 클래스를 명시적으로 지정합니다. |
💡 Casecade & FetchType 속성 및 속성값
분류 | 속성 값 | 설명 |
Casecade | Casecade.ALL | 모든 변경(저장, 병합, 삭제, 재로딩)이 관련 엔티티에 적용됩니다. |
Casecade.DETACH | 엔티티가 detach 상태로 변경될 때, 관련 엔티티도 함께 변경됩니다. | |
Casecade.MERGE | 현재 엔티티의 상태를 데이터베이스에 반영할 때, 관련 엔티티도 함께 반영됩니다. | |
Casecade.PERSIST | 엔티티를 데이터베이스에 저장할 때, 관련 엔티티도 함께 저장됩니다. | |
Casecade.REFRESH | 엔티티를 데이터베이스로부터 새로운 정보로 갱신할 때, 관련 엔티티도 함께 갱신됩니다. | |
Casecade.REMOVE | 엔티티를 삭제할 때, 관련 엔티티도 함께 삭제됩니다 | |
FetchType | FetchType.LAZY | 엔티티가 실제로 사용될 때까지 데이터 로드를 연기합니다. |
FetchType.EAGER | 엔티티가 조회될 때 연관된 엔티티들도 함께 조회합니다. |
💡 [참고] FetchType에 대해 궁금하시면 아래의 글이 도움이 됩니다.
1.2. @OneToOne 사용예시
💡 사용예시
- @OneToOne 관계에서 사용자 엔티티(tb_user)와 여권 엔티티(tb_passport)의 관계가 있다고 가정합니다.
- 한 사람은 하나의 여권을 가질 수 있고, 여권도 한 사람에게만 발급이 되는 구조입니다.
- 데이터베이스의 테이블 관점에서 볼 때 사용자 테이블(tb_user)과 여권 테이블(tb_passport) 테이블 사이에 1:1 관계이며 이를 @OneToOne 어노테이션을 이용하여 관계를 구성합니다.
@Entity
@Table(name = "tb_user")
public class UserEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_sq")
private long userSq;
@Column(name = "user_nm")
private String userNm;
@Column(name = "passport_id")
private long passportId;
@OneToOne
private PassportEntity passportEntity;
}
@Entity
@Table(name = "tb_passport")
public class PassportEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "passport_id")
private Long passportId;
// 여권번호
@Column(name = "passport_num")
private String passportNum;
// 발급일
@Column(name = "issue_date")
private String issueDate;
// 만료일
@Column(name = "expired_date")
private String expiredDate;
}
💡 결과 화면
- 해당 SQL에서는 FetchType을 EAGER로 지정한 UserEntity를 조회한 상태입니다.
- 해당 상황에서는 @OneToOne 상태로 지정되었기에 tb_user 테이블을 기준으로 여권 테이블이 Join 되어서 하나의 여권의 테이블이 조회됩니다. (* Left Join으로 수행이 되어서 tb_user 테이블 값만 존재하더라도 조회가 됩니다.)
1.3. @OneToOne으로 LEFT JOIN을 INNER JOIN으로 변경 사용예시
💡@OneToOne으로 LEFT JOIN을 INNER JOIN으로 변경 사용예시
- @JoinColmn 어노테이션을 추가하였습니다
- @OneToOne 속성으로 optional = false를 추가합니다. optinal 속성은 기본적으로 true이며 연관된 엔티티가 반드시 있어야 하는지 설정합니다. false일 경우, 연관된 엔티티는 반드시 있어야 합니다
@Entity
@Getter
@Table(name = "tb_user")
public class UserEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_sq")
private long userSq;
@Column(name = "user_nm")
private String userNm;
@Column(name = "passport_id")
private long passportId;
@OneToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "passport_id", insertable = false, updatable = false)
private PassportEntity passportEntity;
}
2. @OneToMany
💡 @OneToMany
- 테이블 간의 관계를 지정할때 A 엔티티와 B 엔티티 간의 관계를 1 : N 관계로 지정하는 데 사용하는 어노테이션입니다.
- A 엔티티의 경우는 하나의 row 데이터를 가지고, B 엔티티 같은 경우는 여러 개의 row 데이터를 가지는 구조입니다.
- 해당 어노테이션은 '하나의 row 데이터를 가지고 있는 쪽'에서 사용됩니다.
2.1. @OneToMany 속성 및 속성값
속성 | 속성타입 | 필수여부 | default | 설명 |
cascade | CascadeType | 선택 | 없음 | 연관된 엔티티에 대해 특정 작업이 전파되도록 설정합니다. |
fetch | FetchType | 선택 | FetchType.EAGER | 테이블 간의 연결 관계에서 EAGER을 통해 즉시 가져올지 LAZY를 통해 필요에 따라 가져올 지 전략을 설정합니다 |
mappedBy | String | 선택 | 없음 | 양방향 연관관계에서 주인이 아닌 엔티티가 매핑을 위해 사용합니다. |
orphanRemoval | boolean | 선택 | false | 연관된 엔티티가 더 이상 관리되지 않을 때, 자동으로 삭제할지 설정합니다. |
targetEntity | Class | 선택 | 없음 | 연관된 엔티티의 클래스를 명시적으로 지정합니다. |
💡 Casecade & FetchType 속성 및 속성값
분류 | 속성 값 | 설명 |
Casecade | Casecade.ALL | 모든 변경(저장, 병합, 삭제, 재로딩)이 관련 엔티티에 적용됩니다. |
Casecade.DETACH | 엔티티가 detach 상태로 변경될 때, 관련 엔티티도 함께 변경됩니다. | |
Casecade.MERGE | 현재 엔티티의 상태를 데이터베이스에 반영할 때, 관련 엔티티도 함께 반영됩니다. | |
Casecade.PERSIST | 엔티티를 데이터베이스에 저장할 때, 관련 엔티티도 함께 저장됩니다. | |
Casecade.REFRESH | 엔티티를 데이터베이스로부터 새로운 정보로 갱신할 때, 관련 엔티티도 함께 갱신됩니다. | |
Casecade.REMOVE | 엔티티를 삭제할 때, 관련 엔티티도 함께 삭제됩니다 | |
FetchType | FetchType.LAZY | 엔티티가 실제로 사용될 때까지 데이터 로드를 연기합니다. |
FetchType.EAGER | 엔티티가 조회될 때 연관된 엔티티들도 함께 조회합니다. |
2.2. @OneToMany 사용예시
💡 사용예시
- @OneToMany 관계에서 사용자 엔티티(tb_user)와 주문 엔티티(tb_order)의 관계가 있다고 가정합니다.
- 한 사람은 여러 개의 주문을 할 수 있는 구조입니다.
- 데이터베이스의 테이블 관점에서 볼때 사용자 테이블(tb_user)과 주문 테이블(tb_order) 테이블 사이에는 1 : N 관계이며 이를 @OneToMany 어노테이션을 이용하여 관계를 구성합니다.
@Entity
@Getter
@Table(name = "tb_user")
public class UserEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_sq")
private long userSq;
@Column(name = "user_nm")
private String userNm;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "userInfo")
private List<OrderEntity> orderEntity;
}
@Entity
@Table(name = "tb_order")
public class OrderEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "order_sq")
private long orderSq;
@Column(name = "order_nm")
private String orderNm;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_sq")
private UserEntity userInfo;
}
💡 결과 화면
- 해당 SQL에서는 EAGER으로 지정한 UserEntity를 조회한 상태입니다.
- 해당 상황에서는 FetchType.EAGER으로 UserEntity 조회시 OrderEntity를 함께 조회하는 식으로 수행이 됩니다. 또한, @OneToMany 상태로 지정되었기에 하나의 사용자 테이블의 모든 주문 테이블 정보가 조회되는 Left Join이 수행이 됩니다.
3. @ManyToOne
💡 @ManyToOne
- 테이블 간의 관계를 지정할때 A 엔티티와 B 엔티티 간의 관계를 N : 1 관계로 지정하는 데 사용하는 어노테이션입니다.
- A 엔티티의 경우 여러개의 row 데이터를 가지고 B 엔티티의 경우 하나의 데이터를 가지는 구조입니다.
- 해당 어노테이션은 '여러개의 row 데이터를 가지고 있는 쪽'에서 사용됩니다.
3.1. @ManyToOne 속성 및 속성값
속성 | 속성 타입 | 필수여부 | default | 설명 |
cascade | CascadeType | 선택 | 없음 | 연관된 엔티티에 대해 특정 작업이 전파되도록 설정합니다. |
fetch | FetchType | 선택 | FetchType.EAGER | 테이블 간의 연결 관계에서 EAGER을 통해 즉시 가져올지 LAZY를 통해 필요에 따라 가져올 지 전략을 설정합니다 |
optional | boolean | 선택 | true | 연관된 엔티티가 반드시 있어야 하는지 설정합니다. false일 경우, 연관된 엔티티는 반드시 있어야 합니다. |
targetEntity | Class | 선택 | 없음 | 연관된 엔티티의 클래스를 명시적으로 지정합니다. |
💡 Casecade & FetchType 속성 및 속성값
분류 | 속성 값 | 설명 |
Casecade | Casecade.ALL | 모든 변경(저장, 병합, 삭제, 재로딩)이 관련 엔티티에 적용됩니다. |
Casecade.DETACH | 엔티티가 detach 상태로 변경될 때, 관련 엔티티도 함께 변경됩니다. | |
Casecade.MERGE | 현재 엔티티의 상태를 데이터베이스에 반영할 때, 관련 엔티티도 함께 반영됩니다. | |
Casecade.PERSIST | 엔티티를 데이터베이스에 저장할 때, 관련 엔티티도 함께 저장됩니다. | |
Casecade.REFRESH | 엔티티를 데이터베이스로부터 새로운 정보로 갱신할 때, 관련 엔티티도 함께 갱신됩니다. | |
Casecade.REMOVE | 엔티티를 삭제할 때, 관련 엔티티도 함께 삭제됩니다 | |
FetchType | FetchType.LAZY | 엔티티가 실제로 사용될 때까지 데이터 로드를 연기합니다. |
FetchType.EAGER | 엔티티가 조회될 때 연관된 엔티티들도 함께 조회합니다. |
3.2. @ManyToOne 사용예시
💡 사용예시
- @ManyToOne 관계에서는 여러 주문 정보와 하나의 사용자 정보가 있다고 가정합니다.
- 여러 개의 주문을 할 수 있는 사람은 한 사람이다라는 구조입니다.
- 데이터베이스의 테이블 관점에서 볼 때는 주문 테이블(tb_order)과 사용자 테이블(tb_user) 사이에는 N : 1 관계이며 @ManyToOne 어노테이션을 이용하여 관계를 구성합니다.
@Entity
@Getter
@Table(name = "tb_user")
public class UserEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_sq")
private long userSq;
@Column(name = "user_nm")
private String userNm;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "userInfo")
private List<OrderEntity> orderEntity;
}
@Entity
@Table(name = "tb_order")
public class OrderEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "order_sq")
private long orderSq;
@Column(name = "order_nm")
private String orderNm;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "user_sq")
private UserEntity userInfo;
}
💡 결과 화면
- 해당 SQL에서는 EAGER으로 지정한 OrderEntity를 조회한 상태입니다.
- 해당 상황에서는 @ManyToOne 상태로 지정되었기에 하나의 사용자 테이블의 모든 주문 테이블 정보가 조회되는 Left Join이 수행이 됩니다.
4. @ManyToMany
💡 @ManyToMany
- 테이블 간의 관계를 지정할때 A 엔티티와 B 엔티티 간의 관계를 N : M 관계로 지정하는 데 사용하는 어노테이션입니다.
- A 엔티티의 경우 여러개의 row 데이터를 가지고 B 엔티티 역시 여러 개의 row 데이터를 가지고 있는 구조입니다.
- 해당 어노테이션은 '여러개의 row 데이터를 가지고 있는 어느 쪽'에서든 사용됩니다.
4.1. @ManyToMany 속성 및 속성값
속성 | 속성타입 | 필수여부 | default | 설명 |
cascade | CascadeType | 선택 | 없음 | 연관된 엔티티에 대해 특정 작업이 전파되도록 설정합니다. |
fetch | FetchType | 선택 | FetchType.EAGER | 테이블 간의 연결 관계에서 EAGER을 통해 즉시 가져올지 LAZY를 통해 필요에 따라 가져올 지 전략을 설정합니다 |
optional | boolean | 선택 | true | 연관된 엔티티가 반드시 있어야 하는지 설정합니다. false일 경우, 연관된 엔티티는 반드시 있어야 합니다. |
targetEntity | Class | 선택 | 없음 | 연관된 엔티티의 클래스를 명시적으로 지정합니다. |
💡 Casecade & FetchType 속성 및 속성값
분류 | 속성값 | 설명 |
Casecade | Casecade.ALL | 모든 변경(저장, 병합, 삭제, 재로딩)이 관련 엔티티에 적용됩니다. |
Casecade.DETACH | 엔티티가 detach 상태로 변경될 때, 관련 엔티티도 함께 변경됩니다. | |
Casecade.MERGE | 현재 엔티티의 상태를 데이터베이스에 반영할 때, 관련 엔티티도 함께 반영됩니다. | |
Casecade.PERSIST | 엔티티를 데이터베이스에 저장할 때, 관련 엔티티도 함께 저장됩니다. | |
Casecade.REFRESH | 엔티티를 데이터베이스로부터 새로운 정보로 갱신할 때, 관련 엔티티도 함께 갱신됩니다. | |
Casecade.REMOVE | 엔티티를 삭제할 때, 관련 엔티티도 함께 삭제됩니다 | |
FetchType | FetchType.LAZY | 엔티티가 실제로 사용될 때까지 데이터 로드를 연기합니다. |
FetchType.EAGER | 엔티티가 조회될 때 연관된 엔티티들도 함께 조회합니다. |
4.2. @ManyToMany 사용예시
💡 사용예시
- @ManyToMany 관계에서는 여러 사용자와 여러 수업 간의 정보가 있다고 가정합니다.
- 한 사용자는 여러 개의 수업을 참여할 수 있고, 하나의 수업에는 여러 학생이 참여할 수 있는 구조입니다.
- 데이터베이스의 테이블 관점에서 볼 때는 사용자 테이블(tb_user)과 수업 테이블(tb_course) 사이에 다대다 관계를 두는 사용자 수업 맵(tb_user_course_map)을 두고 N:N 관계이며 @ManyToMany 어노테이션을 이용하여 관계를 구성합니다.
@Entity
@Getter
@Table(name = "tb_user")
public class UserEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private long userId;
@Column(name = "user_nm")
private String userNm;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "tb_user_course_map",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private List<CourseEntity> courses = new ArrayList<>();
}
@Entity
@Getter
@Table(name = "tb_course")
public class CourseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "course_id")
private long courseId;
@Column(name = "course_nm")
private String courseNm;
@ManyToMany(mappedBy = "courses")
private List<UserEntity> students = new ArrayList<>();
}
💡 결과화면
- 해당 SQL에서는 EAGER으로 지정한 UserEntity를 조회한 상태입니다.
- 해당 상황에서는 @ManyToMany 상태로 지정되었기에 사용자 테이블(tb_user)을 기준으로 사용자 수업 맵 테이블(tb_user_course_map)과 조인하며 수업 테이블(tb_course) 테이블을 조인하여 최종 N:N 관계이며 @ManyToMany 어노테이션을 이용하여 구성되었습니다.
5. @JoinColumn
💡 @JoinColumn
- 테이블간의 관계에서 왜래키(FK)를 매핑하는 용도로 사용되는 어노테이션을 의미합니다.
- 주로 @ManyToOne, @OneToOne 어노테이션과 같이 사용이 됩니다.
5.1. @JoinColumn 속성 및 속성 값
속성 | 속성타입 | 필수여부 | default | 설명 |
columnDefinition | String | 선택 | “” | 컬럼의 SQL 선언을 지정하는데 사용되는 속성 |
foreignKey | ForeignKey | 선택 | “” | 외래 키 제약 조건을 지정하는데 사용되는 속성 |
unique | boolean | 선택 | false | 고유 제약 조건을 설정하는데 사용되는 속성 |
insertable | boolean | 선택 | true | 삽입이 가능한지 여부를 지정하는데 사용되는 속성 |
updatable | boolean | 선택 | true | 업데이트 가능한지 여부를 지정하는데 사용되는 속성 |
nullable | boolean | 선택 | true | null 값을 허용하는지 여부를 지정하는데 사용되는 속성 |
name | String | 선택 | “” | 컬럼명을 지정하는데 사용되는 속성 |
referencedColumnName | String | 선택 | “” | 참조하는 외래 키 컬럼명을 지정하는데 사용되는 속성 |
table | String | 선택 | “” | 외래 키를 매핑한 테이블 이름을 지정하는데 사용되는 속성 |
💡 [참고] ForeignKey 속성 및 속성 값
속성 | 속성타입 | 필수여부 | default | 설명 |
foreignKeyDefinition | String | 선택 | “” | 외래키 제약조건을 직접 정의하는 기능을 제공합니다. |
name | String | 선택 | “” | 외래키의 이름을 지정하는 기능을 제공합니다. |
value | ConstraintMode | 선택 | “” | 외래키 제약조건의 활성화, 비활성화를 설정하는 기능을 제공합니다. |
💡 [참고] ConstraintMode 속성 및 속성 값
속성 값 | 설명 |
ConstraintMode.CONSTRAINT | 제약 조건을 적용합니다. 이는 관계형 데이터베이스에서 무결성을 유지하기 위한 규칙을 설정하는데 사용됩니다. |
ConstraintMode.NO_CONSTRAINT | 제약 조건을 적용하지 않습니다. 이는 관계형 데이터베이스에서 무결성을 유지하기 위한 규칙을 설정하지 않는데 사용됩니다. |
ConstraintMode.PROVIDER_DEFAULT | JPA 제공자의 기본 제약 조건 설정을 사용합니다. 이는 JPA 구현체가 제공하는 기본 설정을 따르는데 사용됩니다. |
5.2. @JoinColumn 사용예시
💡 @JoinColumn 사용예시
- 해당 사용예시에서는 @OneToOne와 함께 사용되고 있습니다.
- UserEntity와 PassportEntity 간의 1:1 조인을 하는 관계에서 passport_id 컬럼 값과 조인을 수행합니다.
- 기본적으로 passport_id와 UserEntity에서는 기본 키(user_sq) 값과 조인이 수행됩니다.
@Entity
@Getter
@Table(name = "tb_user")
public class UserEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_sq")
private long userSq;
@Column(name = "user_nm")
private String userNm;
@Column(name = "passport_id")
private long passportId;
@OneToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "passport_id", insertable = false, updatable = false)
private PassportEntity passportEntity;
}
[ 더 알아보기 ]
💡 기본적으로 왜래키와 ID가 되는 컬럼이 조인이 된다고 하면, 내가 특정 컬럼을 지정할 수 있는 방법은 없는 건가?
- 아닙니다. @JoinColumn의 속성 중 referencedColumnName를 이용하여 참조할 키를 지정할 수 있습니다.
@JoinColumn(name = "passport_id", referencedColumnName = "id")
6. @JoinColumns
💡 @JoinColumns
- 엔티티 간의 관계를 매핑할 때 사용이 되는 어노테이션입니다. 해당 어노테이션은 주로 '복합 키'를 사용하는 다대다 관계를 표현할 때 사용됩니다.
- @JoinColumn 어노테이션을 배열로 가지고 있으며, 두 테이블이 더 많은 컬럼으로 조인되어야 할 때 사용됩니다.
- 하나의 컬럼을 나타내며, 이를 통해 복수의 컬럼을 사용하여 테이블 간의 관계를 정의할 수 있습니다.
- @JoinColumns 어노테이션은 @OneToMany, @ManyToOne, @OneToOne, @ManyToMany와 같은 다른 관계 어노테이션과 함께 사용될 수 있습니다.
6.1. @JoinColumns 속성 및 속성 값
속성 | 속성타입 | 필수여부 | default | 설명 |
value | JoinColumn[] | 필수 | [] | JoinColumn 어노테이션의 배열. 이를 통해 여러 개의 조인 컬럼을 지정할 수 있습니다. |
6.2. @JoinColumns 사용예시
💡 @JoinColumns 사용예시
- 해당 예시에서는 OneEntity 엔티티에서 OtherEntity 엔티티를 조인하고 있습니다. 조인 대상은 first_id, second_id 값을 가집니다.
@Entity
@Table(name = "tb_one")
public class OneEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name = "first_id")
private String firstId;
@Column(name = "second_id")
private String secondId;
@ManyToOne
@JoinColumns({
@JoinColumn(name = "first_id", referencedColumnName = "first_id", insertable = false, updatable = false),
@JoinColumn(name = "second_id", referencedColumnName = "second_id", insertable = false, updatable = false)
})
private OtherEntity otherEntity;
}
💡 JoinColumn 속성 및 속성 값
속성 | 속성타입 | 필수여부 | default | 설명 |
columnDefinition | String | 선택 | “” | 컬럼의 SQL 선언을 지정하는데 사용되는 속성 |
foreignKey | ForeignKey | 선택 | “” | 외래 키 제약 조건을 지정하는데 사용되는 속성 |
unique | boolean | 선택 | false | 고유 제약 조건을 설정하는데 사용되는 속성 |
insertable | boolean | 선택 | true | 삽입이 가능한지 여부를 지정하는데 사용되는 속성 |
updatable | boolean | 선택 | true | 업데이트 가능한지 여부를 지정하는데 사용되는 속성 |
nullable | boolean | 선택 | true | null 값을 허용하는지 여부를 지정하는데 사용되는 속성 |
name | String | 선택 | “” | 컬럼명을 지정하는데 사용되는 속성 |
referencedColumnName | String | 선택 | “” | 참조하는 외래 키 컬럼명을 지정하는데 사용되는 속성 |
table | String | 선택 | “” | 외래 키를 매핑한 테이블 이름을 지정하는데 사용되는 속성 |
7. @JoinTable
💡 @JoinTable
- 관계형 데이터베이스의 '중간 테이블'을 정의하는 데 사용되는 어노테이션입니다. 주로 다대다 관계에서 사용되며 두 엔티티 사이의 매핑을 위한 중간테이블을 정의합니다.
7.1. @JoinTable 속성 및 속성 값
속성 | 속성타입 | 필수여부 | default | 설명 |
name | String | 선택 | “” | 이름을 지정합니다. |
catalog | String | 선택 | “” | 카탈로그를 지정합니다. |
schema | String | 선택 | “” | 스키마를 지정합니다. |
joinColumns | JoinColumn[] | 선택 | {} | 조인 컬럼을 지정합니다. |
inverseJoinColumns | JoinColumn[] | 선택 | {} | 반대 방향의 조인 컬럼을 지정합니다. |
foreignKey | ForeignKey | 선택 | ConstraintMode.CONSTRAINT | 외래 키를 지정합니다. |
indexes | Index[] | 선택 | {} | 인덱스를 지정합니다. |
inverseForeignKey | ForeignKey | 선택 | ConstraintMode.PROVIDER_DEFAULT | 반대 방향의 외래 키를 지정합니다. |
uniqueConstraints | UniqueConstraint[] | 선택 | {} | 고유 제약 조건을 지정합니다. |
💡 JoinColumn, ForeignKey 속성 및 속성 값
분류 | 속성 | 속성타입 | 필수여부 | default | 설명 |
JoinColumn | columnDefinition | String | 선택 | “” | 컬럼의 SQL 선언을 지정하는데 사용되는 속성 |
foreignKey | ForeignKey | 선택 | “” | 외래 키 제약 조건을 지정하는데 사용되는 속성 | |
unique | boolean | 선택 | false | 고유 제약 조건을 설정하는데 사용되는 속성 | |
insertable | boolean | 선택 | true | 삽입이 가능한지 여부를 지정하는데 사용되는 속성 | |
updatable | boolean | 선택 | true | 업데이트 가능한지 여부를 지정하는데 사용되는 속성 | |
nullable | boolean | 선택 | true | null 값을 허용하는지 여부를 지정하는데 사용되는 속성 | |
name | String | 선택 | “” | 컬럼명을 지정하는데 사용되는 속성 | |
referencedColumnName | String | 선택 | “” | 참조하는 외래 키 컬럼명을 지정하는데 사용되는 속성 | |
table | String | 선택 | “” | 외래 키를 매핑한 테이블 이름을 지정하는데 사용되는 속성 | |
ForeignKey | foreignKeyDefinition | String | 선택 | “” | 외래키 제약조건을 직접 정의하는 기능을 제공합니다. |
name | String | 선택 | “” | 외래키의 이름을 지정하는 기능을 제공합니다. | |
value | ConstraintMode | 선택 | “” | 외래키 제약조건의 활성화, 비활성화를 설정하는 기능을 제공합니다. |
💡 [참고] ConstraintMode 속성 및 속성 값
속성 값 | 설명 |
ConstraintMode.CONSTRAINT | 제약 조건을 적용합니다. 이는 관계형 데이터베이스에서 무결성을 유지하기 위한 규칙을 설정하는데 사용됩니다. |
ConstraintMode.NO_CONSTRAINT | 제약 조건을 적용하지 않습니다. 이는 관계형 데이터베이스에서 무결성을 유지하기 위한 규칙을 설정하지 않는데 사용됩니다. |
ConstraintMode.PROVIDER_DEFAULT | JPA 제공자의 기본 제약 조건 설정을 사용합니다. 이는 JPA 구현체가 제공하는 기본 설정을 따르는데 사용됩니다. |
7.2. @JoinTable 사용예시
💡 @JoinTable 사용예시
- 아래의 예시에서는 tb_user, tb_course라는 테이블 간의 N:N 관계를 형성하고 있습니다.
- 이때에 중간 테이블인 tb_user_course_map 테이블이라는 중간 map 테이블을 둡니다.
- 기준이 되는 tb_user 테이블에서는 user_id를 이용하며 tb_course 테이블에서는 course_id를 이용하여 중간테이블을 구성하며 참조합니다.
@Entity
@Getter
@Table(name = "tb_user")
public class UserEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private long userId;
@Column(name = "user_nm")
private String userNm;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "tb_user_course_map",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private List<CourseEntity> courses = new ArrayList<>();
}
오늘도 감사합니다. 😀
반응형