이번 포스팅은 JPA(Java Persistence API)를 사용하여 엔티티 객체와 데이터베이스 테이블 간의 매핑, 그리고 엔티티 필드와 데이터베이스 컬럼 간의 매핑에 대해 살펴보겠습니다. JPA는 자바 객체와 관계형 데이터베이스 간의 매핑을 간소화하고 편리하게 처리할 수 있도록 도와줍니다.
객체와 테이블 매핑
@Entity
JPA에서 엔티티(Entity)는 데이터베이스 테이블과 대응되는 하나의 클래스로 데이터의 집합을 의미합니다. JPA를 사용하여 데이터베이스 테이블과 매핑할 클래스는 반드시 @Entity를 붙여야 합니다.
@Entity(name = "MEMBER")
public class Member {
private Long memberId;
private String username;
}
속성 | 설명 |
name | Entity의 이름을 지정하며, 가급적 default 값을 사용하는 것이 좋습니다. (default는 클래스 이름을 그대로 사용합니다.) |
💡 엔티티(Entity)의 이름 용도는 무엇일까?
JPQL에 테이블명 대신 사용되는 이름이다. name을 따로 설정하지 않으면 클래스명이 default 값으로 설정된다. 예를 들어 위 코드처럼 name = "MEMBER"로 설정할 경우 다음과 같은 JPQL이 된다.
(default) → select m from Member m
(name = "MEMBER") → select m from MEMBER m
@Table
엔티티와 매핑할 테이블을 지정합니다.
@Entity
@Table(name = "MEMBER_TABLE") // insert into MEMBER_TABLE values (...
public class Member {
private Long memberId;
private String username;
}
@Table(
uniqueConstraints = {
@UniqueConstraint(name = "UniqueNumberAndStatus", columnNames = {"personNumber", "isActive"}),
@UniqueConstraint(name = "UniqueSecurityAndDepartment", columnNames = {"securityNumber", "departmentCode"})
}
)
속성 | 설명 |
name | 매핑할 테이블 이름을 지정한다. |
catalog | 데이터베이스 catalog 매핑 |
schema | 데이터베이스 schema 매핑 |
uniqueConstraints | DDL 생성 시 유니크 제약 조건 설정한다. |
주의사항
- @Entity 클래스는 필수적으로 기본 생성자가 있어야 합니다.
- final, enum, inner 클래스 또는 인터페이스로 엔티티를 만들면 안 됩니다.
- 엔티티 객체 내 저장할 필드에 final을 적용하면 안 됩니다.
🤔 기본 생성자가 왜 필수일까?
Hibernate를 사용할 때, 객체의 생성에 Reflection이 사용됩니다. 이 과정에서 생성자가 private으로 선언되어 있을 수 있습니다. 그러나 프록시를 생성하거나 데이터 검색을 위해 bytecode instrumentation 없이도 효율적인 패키지 또는 public 접근 지정자가 필요합니다. (Lazy Loading과 같은 기능을 제공하기 위함으로 이는 타 포스팅에 다룹니다.)
🤔 final, enum, inner 클래스 또는 인터페이스는 엔티티가 되지 못할까?
엔티티는 값이 변경될 여지가 있기 때문에 final, enum 같은 클래스를 사용할 수 없습니다.
인터페이스의 경우 필드 값이 static final이며 생성자를 가질 수 없습니다.
inner 클래스의 경우 enclosing 클래스의 인스턴스 없이 인스턴스화될 수 없습니다. 이러한 이유로 Reflection을 활용하여 클래스를 인스턴스화하기 어려우며, 외부의 enclosing 클래스의 생성자가 private으로 설정 시 inner 클래스를 Reflection 할 수 없게 됩니다.
🤔 저장할 필드에 final 필드를 왜 사용하면 안 될까?
이후 포스팅에서 소개될 Lazy Loading과 같은 경우 엔티티 객체에 임의 프록시 객체를 생성하여 넣어주게 됩니다. Lazy Loading의 의미처럼 엔티티 객체의 모든 필드를 한 번에 채우지 않고 필요할 때 채우는 경우가 있습니다. 이러한 이유로 필드가 final로 지정되어 있으면 안 됩니다.
데이터베이스 스키마 자동 생성
JPA는 애플리케이션 실행 시점에 데이터베이스에 맞는 테이블을 자동으로 생성하는 기능을 제공합니다. 이는 개발자가 데이터베이스를 쉽게 설정하고 테스트할 수 있도록 도와주며, 데이터베이스 변경 시 자동으로 스크립트를 생성해 줍니다.
스키마 자동 생성 기능은 다음과 같습니다.
속성 | 설명 |
create | 기존 테이블을 drop 하고 다시 생성한다. |
create-drop | create 속성과 동일하나, 종료 시점에 테이블을 drop 한다. |
update | 기존 테이블에서 변경사항이 있다면 추가 반영한다. |
validate | 엔티티와 테이블이 정상 매핑이 되어있는지 확인한다. (일치하지 않으면 예외 발생) |
none | 사용하지 않는다. |
스키마 자동 생성을 사용하기 위해 다음과 같은 설정이 필요합니다.
- application.properties를 사용할 경우
spring.jpa.hibernate.ddl-auto=create
- application.yml을 사용할 경우
spring:
jpa:
hibernate:
ddl-auto: create # 스키마 자동 생성 설정
show-sql: true # DDL 로그 출력
설정을 마치고, 애플리케이션을 실행하면 다음과 같이 테이블이 생성됩니다.
---------------------------------------------------------
Hibernate:
drop table Member if exists
Hibernate:
create table Member (
id bigint not null,
username varchar(255),
primary key (id)
)
데이터베이스 스키마 자동 생성 주의사항
이러한 편리한 기능이지만 실제 운영 환경에서는 create, create-drop, update 같은 옵션 사용을 사용하는 것을 권장하지 않습니다. 로컬 환경 같은 개인 PC에서 매핑 참고용 정도로만 사용해야 합니다.
운영 환경에서 create 또는 create-drop을 사용하게 되면 기존 데이터가 삭제되는 문제가 발생합니다.
update의 경우 애플리케이션이 로딩되는 시점에 자동으로 테이블을 변경하는 것은 위험합니다. alter의 경우 데이터베이스에서 락이 발생할 수 있으며, 특정 시간 동안 서비스가 중지되는 참사가 발생할 수 있습니다.
필드와 컬럼 매핑
@Column
@Column은 객체 필드를 테이블의 컬럼에 매핑시켜 주는 역할을 합니다.
속성 | 기능 | 기본값(default) |
name | 필드와 매핑 할 테이블의 컬럼 이름을 지정한다. | 객체의 필드 이름 |
nullable | DDL 생성 시 null 값의 허용 여부를 설정한다. false로 설정하면 not null 제약조건이 붙는다. | true |
unique | @Table의 uniqueConstraints와 같으나 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용한다. | false |
columnDefinition | 데이터베이스 컬럼 정보를 직접 줄 수 있다. | 자바 필드의 타입과, DB 방언 설정 정보를 사용해 생성 |
length | 문자 길이 제약조건, String 타입에만 사용한다. | 255 |
precision, scale | BigDecimal 타입(혹은 BigInteger)에서 사용한다. precision은 소수점을 포함한 전체 자리수를, scale은 소수의 자리수다. | precision = 0, scale = 0 |
insertable (거의 사용 X) | 엔티티 저장 시 이 필드도 같이 저장한다. false로 설정하면 이 필드는 데이터베이스에 저장하지 않는다. false 옵션은 읽기 전용일 때 사용한다 | true |
updateable (거의 사용 X) | 엔티티 수정 시 이 필드도 같이 수정한다. false로 설정하면 데이터베이스에 수정하지 않는다. false 옵션은 읽기 전용일 때 사용한다 | true |
table (거의 사용 X) | 하나의 엔티티를 두 개 이상의 테이블에 매핑할 때 사용한다.(@SecondaryTable 사용) 지정한 필드를 다른 테이블에 매핑할 수 있다. | 현재 클래스가 매핑된 테이블 |
🤔 @Column을 생략하면?
@Column을 생략하면 대부분 @Column 속성의 기본값으로 적용됩니다. 그러나 자바 기본 타입일 때 nullable 속성에 예외가 있습니다.
int data1; // 자바 기본 타입 => data integer not null
Integer data2; // 객체 타입 => data integer
@Column
int data3; // 자바 기본 타입 => data integer
int 같이 자바의 기본 타입은 null을 입력할 수 없기 때문에 테이블을 따로 DDL로 생성할 경우 not null 제약 조건을 붙여주는 것이 안전합니다. JPA는 이를 고려하여 스키마 자동 생성 시 not null 제약 조건을 추가해 줍니다.
그러나 @Column을 사용하면 기본값이 nullable = true 이므로 false로 지정해 주는 것이 안전합니다.
@Enumerated
자바의 Enum 클래스 타입을 매핑할 때 사용합니다.
속성 | 기능 | 기본값 |
value | EnumType.ORDINAL: 순서를 데이터베이스에 저장 EnumType.STRING: 이름을 데이터베이스에 저장 |
EnumType.ORDINAL |
- EnumType.ORDINAL
enum에 정의된 순서대로 인덱스 값으로 저장됩니다. (0, 1, 2, ...)
이미 저장된 enum의 순서를 변경할 수 없는 단점이 있어 일반적으로 사용되지 않습니다.
- EnumType.STRING
enum 내 필드명 그대로 저장됩니다.
이미 저장된 enum의 순서가 무작위로 바뀌거나, 추가되어도 이름으로 저장되어 안전합니다.
@Temporal
날짜 타입을 매핑할 때 사용됩니다.
자바의 Date 타입은 "yyyyMMddHHmmss"으로 데이터베이스에는 date(날짜), time(시간), timestamp(날짜와 시간)라는 세 가지 타입이 별도로 존재하며, @Temporal을 생략하면 Date와 가장 유사한 timestamp로 정의됩니다.
Java 8 이상부터 LocalDate, LocalDateTime이 추가되었으며, @Temporal을 생략할 수 있게 되었습니다.
속성 | 기능 | 기본값 |
value | TemporalType.DATE: 날짜, DB date타입과 매핑 (예: 2022-12-27) TemporalType.TIME: 시간, DB time타입과 매핑 (예: 11:11:11) TemporalType.TIMESTAMP: 날짜, 시간, DB timestamp타입과 매핑 (예 : 2022-12-27 11:11:11) |
EnumType.ORDINAL |
@Lob
@Lob은 Large Object의 줄임말로 데이터베이스의 BLOB, CLOB 타입과 매핑하며, @Lob는 따로 지정하는 속성이 존재하지 않습니다. 대신에 매핑하는 필드 타입이 문자면 CLOB, 나머지는 BLOB로 매핑됩니다.
- CLOB : 문자 대형 객체
- BLOB : 이진 대형 객체 (이미지, 동영상 등등)
@Lob 주석은 @Basic 혹은 @ElementCollection과 함께 사용할 수 있습니다.
@Transient
해당 필드를 영속 대상에서 제외시키고 싶을 때 사용합니다. 데이터베이스에 저장되거나, 조회되지 않기 때문에 일반적으로 객체에 임시적으로 값을 보관하는 목적으로 사용합니다.
자바 ORM 표준 JPA 프로그래밍 - 기본편 | 김영한 - 인프런
김영한 | JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., 실무에서도
www.inflearn.com
'Spring > JPA' 카테고리의 다른 글
프록시(Proxy) (0) | 2024.04.28 |
---|---|
상속관계 매핑 (0) | 2024.04.24 |
연관관계 매핑 정리 (0) | 2024.04.23 |
엔티티 매핑(Entity Mapping) - 기본 키 (1) | 2024.04.22 |
영속성 컨텍스트(Perisistence Context) (0) | 2024.04.21 |