[JPA] JPA의 엔티티 퍼시스턴스 매핑
JPA의 매핑 어노테이션
JPA가 제공하는 자바 퍼시스턴스 어노테이션(java persistence annotation)을 사용한 객체 관계 매핑은 크게 두 가지로 나뉜다.
- 논리적 매핑: 엔티티 클래스들 간 연관 관계 설정과 관련된 어노테이션
- 다중성(카디널리티) 설정:
@ManyToOne
,@OneToMany
,@OneToOne
,@ManyToMany
- 외래키 매핑 (논리적 외래키 제약 조건 설정):
@JoinColumn
- 다중성(카디널리티) 설정:
- 물리적 매핑: 테이블과 엔티티 간 매핑, 테이블 컬럼과 엔티티 프로퍼티(필드) 간 매핑 어노테이션, 데이터베이스 스키마 자동 생성(DDL 생성) 어노테이션
- 테이블 매핑 및 생성:
@Table
- 기본키 매핑 및 생성:
@Id
- 컬럼 매핑 및 생성:
@Column
- 인덱스 생성:
@Index
- 테이블 매핑 및 생성:
엔티티 식별자 매핑 및 생성 전략
@Id
어노테이션은 엔티티의 특정 프로퍼티를 엔티티의 식별자로 정의하기 위해 사용한다. 식별자 프로퍼티는 애플리케이션 자체에 의해 설정되거나 하이버네이트에 의해 생성될 수 있다. @GeneratedValue
어노테이션은 이 식별자 생성 전략을 정의한다.
JPA에는 다섯 가지 유형의 식별자 생성 전략이 있다.
AUTO
: 기본 데이터베이스 따른 ID 컬럼, 시퀀스 또는 테이블TABLE
: ID 갖는 테이블IDENTITY
: ID 컬럼SEQUENCE
: 시퀀스ID
복사: 다른 엔티티에서 복사된 ID
@GeneratedValue
어노테이션을 설정하지 않을 경우
@GeneratedValue(strategy = GenerationType.AUTO)
생성 전략이 사용된다. 이 경우 애플리케이션에서 식별자 값을 설정해야 한다. @GeneratedValue(strategy = GenerationType.AUTO)
생성 전략을 문자열 타입과 함께 사용할 수는 없으며 문자열을 식별자 값으로 사용하려면 수동으로 할당해야 한다.
테이블 외래키 제약 조건과 JPA 엔티티 연관 관계
테이블 간 연관 관계 설정과 엔티티 간 연관 관계 설정 유형은 다음과 같다.
- 테이블 간 외래키를 설정하지 않은 경우: 테이블 간 연관 관계 없음
- 엔티티 객체 간 참조하지 않고 논리적 매핑 어노테이션을 사용하지 않는 경우: 테이블 간 연관 관계와 엔티티 간 연관 관계 모두 없음
- 엔티티 객체 간 참조하고 논리적 매핑 어노테이션을 사용하는 경우: 테이블 간 연관 관계는 없지만 엔티티 간 연관 관계 존재
- 테이블 간 외래키를 설정하는 경우: 테이블 간 양방향 연관 관계 존재
- 엔티티 객체 간 참조하지 않고 논리적 매핑 어노테이션을 사용하지 않는 경우: 테이블 간 연관 관계는 있지만 엔티티 간 연관 관계가 없음
- 엔티티 객체 간 참조하고 논리적 매핑 어노테이션을 사용하는 경우: 테이블 간 연관 관계와 엔티티 간 연관 관계 모두 존재
데이터베이스 상에서 테이블 간 외래키 제약 조건을 설정하는 것을 물리적 외래키 제약 조건 설정, JPA 어노테이션을 이용하여 애플리케이션 상에서 객체(엔티티 객체) 간 외래키 제약 조건을 설정하는 것을 논리적 외래키 제약 조건 설정이라고도 한다.
엔티티 객체 간 연관 관계를 설정하는 것은 연관 관계의 방향과 다중성을 정하고 외래키를 매핑하는 것이다. 이를 위한 어노테이션은 다음과 같다.
- 방향 설정
- 다중성(카디널리티) 설정
- 외래키 매핑 (논리적 외래키 제약 조건 설정)
한 테이블이 다른 테이블의 기본키에 해당하는 값을 컬럼에 가지고 있도록 외래키 제약 조건을 설정한 경우 두 테이블은 양방향 연관 관계를 갖는다. 이 경우 두 테이블에 해당하는 두 엔티티의 연관 관계는 객체의 참조(한쪽 또는 모두) 여부, 다중성 설정에 따라 달라지게 된다.
- 객체 참조가 없을 경우 경우 연관 관계는 설정되지 않는다.
- 객체 참조가 있고 다중성을 설정하는 경우 연관 관계가 설정된다.
데이터베이스 상의 명시적인 물리적 외래키 제약 조건 설정이 없는 두 테이블에 대해서도 해당 엔티티 간의 연관 관계를 설정할 수 있다. 하지만 이 경우 데이터베이스가 외래키를 통해 제공하는 데이터의 무결성과 일관성이 유지되지 못할 수 있다. 실제 데이터베이스에 저장된 데이터 중 무결성 및 일관성이 위반된 데이터가 존재하는 경우 논리적 외래키 제약 조건 설정을 통해 엔티티 간의 연관 관계를 설정하더라도 적절한 외래키 매핑이 되지 않아 엔티티 객체 간 연관 관계를 기반으로 수행하는 객체 그래프 탐색이 정상적으로 수행되지 않게 된다. 이 경우 JPA가 물리적으로 테이블 상에 존재하지 않는 외래 키 참조를 무시하도록 할지 아니면 예외를 던지도록 할지 정의함으로써 애플리케이션 코드 수준에서 외래키 참조가 깨진 엔티티를 처리할 수 있다. 즉, 외래키를 사용하지 않음으로써 위반될 수 있는 데이터 무결성을 지키기 위해서는 데이터를 사용하는 애플리케이션에서의 검증이 필요하다.
외래키를 사용하여 데이터베이스가 제공하는 참조 무결성 제약 조건을 활용하지 않는 이유는 일반적으로 다음과 같다.
- 외래키 컬럼 삭제가 연관된 모든 테이블에 영향을 주므로 컬럼 변경에 따른 영향도가 크다.
- 대용량 데이터의 경우 데이터베이스 성능이 저하된다.
테이블의 물리적 외래키 제약 조건 설정 여부와 관계 없이 논리적 외래키 제약 조건을 설정하려면 외래키를 가지고 있는 테이블에 해당하는 엔티티 필드에 @JoinColumn
어노테이션을 사용하여 연관 관계를 설정한다. @JoinColumn
어노테이션은 두 엔티티 간 논리적 외래키 매핑을 위해 사용하며 생략할 경우 필드명_참조하는 테이블의 컬럼명
을 외래키로 사용한다.
논리적 외래키 제약 조건 설정을 위한 @JoinColumn
어노테이션의 프로퍼티는 다음과 같다.
name
: 현재 테이블에서 외래키로 설정할 컬럼명referencedColumnName
: 외래키가 참조하는 대상 테이블의 컬럼명 (기본키 컬럼명)foreignKey
: 테이블의 물리적 외래키 제약 조건 설정을 위한 프로퍼티- 테이블에 물리적 외래키 제약 조건이 설정되어 있지 않은 상태에서 프로퍼티 값을
@jakarta.persistence.ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
으로 설정할 경우 테이블 상에 물리적 외래키 제약 조건을 생성한다.
- 테이블에 물리적 외래키 제약 조건이 설정되어 있지 않은 상태에서 프로퍼티 값을
테이블 상에 물리적 외래키 제약 조건 설정 여부와 관계 없이 엔티티 간 논리적 외래키 제약 조건을 설정하는 엔티티 객체 코드는 다음과 같다.
@Entity
@Table(name = "테이블명")
public class EntityA {
@OneToOne
@JoinColumn(name = "외래키컬럼명",
referencedColumnName = "참조대상테이블의컬럼명",
foreignKey = @jakarta.persistence
.ForeignKey(value = ConstraintMode.NO_CONSTRAINT))
private EntityB entityB;
}
@NotFound 어노테이션
@NotFound
어노테이션은 외래키 참조가 깨진 경우 엔티티 객체 탐색 시 발생하는 EntityNotFoundException
예외를 무시하도록 하거나 연관 관계 엔티티 객체를 인스턴스화할 때 FetchNotFoundException
예외를 던지도록 지시한다. action
파라미터를 설정하여 처리 방법을 선택할 수 있다.
@NotFound(action = NotFoundAction.IGNORE)
어노테이션은 외래 키 참조를 해결할 수 없는 경우(연관 관계 객체가 테이블에 존재하지 않는 경우) 널을 반환함으로써 예외가 발생하지 않도록 한다. 하지만 이 설정을 사용하는 경우 연관 관계가 설정되지 않았는지, 존재하지 않는 데이터를 참조하려고 하는지 구분할 수 없다. 두 경우에 대해 서로 다른 처리를 하려는 경우에는 위 설정을 사용할 수 없다.
@NotFound(action = NotFoundAction.EXCEPTION)
어노테이션은 외래 키 참조를 해결할 수 없는 경우 FetchNotFoundException
예외를 던지도록 지시한다.
Comments