[JPA] 객체와 테이블 매핑
JPA가 영속성 컨텍스트 개념을 사용해 동작하는건 알겠는데, 자바 객체와 데이터베이스 테이블은 어떻게 매핑되는걸까?
JPA에서 가장 중요한 부분은 자바 객체인 엔티티와 데이터베이스의 테이블을 정확하게 매핑하는 작업이다.
JPA에서는 @Entity 애너테이션을 사용해서 엔티티를 관리한다.
@Entity 애너테이션이 붙은 클래스는 데이터베이스 테이블과 매핑될 수 있다.
예전에는 xml 등 설정 파일을 사용해 매핑 정보를 작성했는데, 애너테이션을 사용하는 편이 좀 더 쉽고 직관적이다.
@Entity 애너테이션의 속성으로 name을 지정해 JPA에서 사용할 엔티티 이름을 지정할 수 있고,
@Table 애너테이션의 속성으로 name을 지정해 매핑할 테이블 이름을 지정할 수 있다.
엔티티 클래스는 public 또는 protected 접근 제한자의 기본 생성자가 필요하다.
(보통 Lombok이나 기본 생성자로 편하게 작성한다)
원래는 데이터베이스 테이블을 직접 생성해야 하지만, JPA는 데이터베이스 스키마를 자동으로 생성해 주는 기능을 제공한다.
설정 파일에서 ddl-auto 부분을 조작해 다양한 옵션을 사용할 수 있다. (설정 파일은 maven과 gradle이 각각 다르다)
데이터베이스 종류마다 다른 방언을 사용해주니 편하다.
편하긴 하지만 운영 환경에서 사용해도 될 만큼 완벽하지는 않으니 매핑이 어떻게 되는지 참고하는 정도로만 사용하자.
create로 사용 시 기존에 있던 데이터베이스 테이블을 모두 드랍하고 새로 만들기 때문에 데이터베이스 테이블에 데이터가 들어있었다면 모두 초기화된다.
개발 초기 단계에서는 create로 설정해 편하게 테스트 할 때 사용하지만, 테스트 서버나 운영 서버에서는 none을 사용하는 편이 합리적이다.
컬럼 값으로는 Enum 타입 등 여러 자료형이 올 수 있다.
객체와 테이블을 각각 다른 이름으로 다루고 싶은 경우 @Column 애너테이션을 사용한다.
@Enumerated 애너테이션을 사용해 데이터베이스에 Enum 타입을 표현할 수 있고, @Temporal 애너테이션을 사용해 데이터베이스에 날짜를 표현한다.
@Lob 애너테이션을 사용해 길이 제한 없는 문자열을 데이터베이스에 표현한다.
@Column 애너테이션의 속성 값으로 null 여부, 최소 길이 여부 등을 설정할 수 있지만...
DDL을 자동 생성할 때만 적용되고 JPA의 실행 로직에는 영향을 주지 않음을 기억하자.
이 외에도 @Transient 애너테이션이 있는데, 해당 애너테이션이 붙은 필드는 데이터베이스에 매핑되지 않고 메모리에서만 사용하게 된다.
@Column 애너테이션의 속성값을 자세히 살펴보자.
@Column 애너테이션의 속성 값으로 insertable과 updatable을 사용하면 해당 필드가 등록되거나 변경되지 않도록 설정할 수 있다. (데이터베이스 워크벤치에서 강제로 업데이트하는건 어쩔 수 없다)
이 외에도 여러 속성이 있으니 위의 표를 참고해서 사용하자.
Enumerated
EnumType.ORDINAL으로 설정 시 enum 클래스의 순서를 데이터베이스에 저장하고,
EnumType.STRING으로 설정 시 enum 클래스의 이름을 데이터베이스에 저장한다.
(여기서 순서는 0,1,2)
추후 추가되는 데이터를 고려해서 보통 STRING으로 사용하는 편이다.
JPA에서는 @Id 애너테이션을 사용해 데이터베이스의 PK를 지정할 수 있다.
이 때 @GeneratedValue 애너테이션을 함께 붙이면 데이터베이스가 알아서 기본 키를 설정해준다.
@GeneratedValue의 strategy 값을 설정해 여러 가지 옵션을 줄 수 있다.
AUTO
데이터베이스 방언에 맞춰 알아서 생성해준다.
아래 세 가지 전략 중 하나가 선택된다.
IDENTITY
기본 키 생성을 데이터베이스에 위임한다.
Id에 값을 넣지 않고 데이터베이스에 insert 한다.
그럼 Id가 null로 insert 되는데, 이 때 데이터베이스에서 Id 값을 설정해 주는 방식으로 동작한다.
즉, Id 값은 데이터베이스에 저장된 후에 알 수 있다.
그런데 영속성 컨텍스트로 관리되기 위해서는 PK값이 꼭 필요한데..?
JPA는 이 부분을 극복하기 위해 em.persist를 수행한 이후 바로 데이터베이스에 insert 쿼리를 날린다.
원래는 커밋하는 시점에 날려야 하지만.. PK값을 얻어야 한다.
Identity 전략에서는 sql을 모아서 한번에 날릴 수 없다.
이 부분이 단점이긴 하지만.. 성능 차이가 그렇게 크지는 않다.
Statement.getGeneratedKeys() 메서드는 데이터를 저장하면서 기본 키 값을 얻을 수 있는데, 하이버네이트는 해당 메서드를 사용해 데이터베이스와 한 번만 통신한다.
SEQUENCE
데이터베이스에 있는 시퀀스 오브젝트를 통해 값을 생성한다.
(Oracle, Postgre 등 RDBMS에서 유니크한 값을 순차적으로 생성할 때 사용하는 객체이다)
테이블마다 다른 시퀀스를 주려면 @SequenceGenerator 애너테이션으로 엔티티와 시퀀스를 매핑해 주면 된다.
매핑하지 않으면 기본 시퀀스가 만들어지고 사용된다.
영속성 컨텍스트로 엔티티를 관리하려면 PK값이 필요하다.
시퀀스 전략에서 JPA는 먼저 데이터베이스 시퀀스를 통해 다음 값을 얻어와 PK값으로 사용한다.
성능을 좀 더 끌어올리기 allocationSize 속성을 사용한다.
매번 PK값을 얻기 위해 데이터베이스에 요청하는건 성능 저하를 유발하기 때문에 PK 값이 1씩 증가하는 경우 allocationSize를 50정도로 크게 설정해 PK값이 50을 넘어설 때 다시 요청하도록 한다.
원래대로라면 식별자를 구하기 위해 SELECT 구문을 사용한 후 INSERT 구문으로 데이터베이스에 값을 저장하는데, allocationSize를 사용하면 메모리에 설정된 값 만큼의 식별자를 캐시로 저장하고 있어 성능이 향상된다.
Table
키 생성 전용 테이블을 만들어 데이터베이스 시퀀스처럼 동작하는 전략이다.
데이터베이스마다 조금씩 차이가 있어 적용하지 못하는 기능도 Table 전략을 사용하면 일관성 있게 적용할 수 있다.
단, 테이블을 직접 사용하다 보니 성능이 조금 떨어진다는 단점이 있다.
운영 서버에서는 데이터베이스에서 관례로 사용되는 기능을 쓰는게 합리적이다.
앞에서 언급했듯 AUTO는 사용하는 데이터베이스에 따라서 전략을 알아서 선택해준다.
자연 키를 PK로 선택하든 대리 키를 PK로 선택하든 그건 상황마다 다르다.
JPA는 모든 엔티티에 일관된 방식으로 대리 키 사용을 권장하니 참고해두자.
PK 매핑 전략은 웬만하면 데이터베이스에서 사용되는 전략대로 설정하고, 필요 시 직접 할당하는 방법도 고려하자.
'Spring > JPA' 카테고리의 다른 글
[JPA] 상속관계 매핑 (0) | 2022.12.23 |
---|---|
[JPA] 다양한 연관관계 매핑 (0) | 2022.12.22 |
[JPA] 연관관계 매핑 (0) | 2022.12.22 |
[JPA] 영속성 컨텍스트 (0) | 2022.12.21 |
[JPA] 도입 (0) | 2022.12.21 |
댓글
이 글 공유하기
다른 글
-
[JPA] 다양한 연관관계 매핑
[JPA] 다양한 연관관계 매핑
2022.12.22 -
[JPA] 연관관계 매핑
[JPA] 연관관계 매핑
2022.12.22 -
[JPA] 영속성 컨텍스트
[JPA] 영속성 컨텍스트
2022.12.21 -
[JPA] 도입
[JPA] 도입
2022.12.21