JPA를 사용하는 데 가장 중요한 일은 엔티티와 테이블을 정확히 매핑하는 것이다. 따라서 매핑 어노테이션을 정확히 숙지하고 사용해야 한다. JPA는 다양한 매핑 어노테이션을 지원하는데 크게 4가지로 분류한다. 오늘은 이 4가지 부류의 매핑에 대해서 알아보려 한다.
개요
매핑 어노테이션은 크게 객체와 테이블 매핑, 기본 키 매핑, 필드와 칼럼 매핑, 연관관계 매핑으로 나눌 수 있다. 물론 XML을 사용하여 매핑할 수 있지만, 어노테이션을 사용하는 방법이 좀 더 쉽고 직관적이다.
- 객체와 테이블 매핑 : @Entity, @Table
- 기본 키 매핑 : @Id
- 필드와 컬럼 매핑 : @Column
- 연관관계 매핑 : @ManyToOne, @JoinColumn
수많은 어노테이션이 있지만 기본적으로 사용하는 어노테이션만 정리하였다. 이후에 각각의 부류별로 조금 더 자세히 설명하고자 한다.
@Entity📗
JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 어노테이션을 필수로 붙여야 한다. @Entity에는 name 속성이 있는데 설정하지 않을 경우에는 기본적으로 클래스 이름을 그대로 사용한다(예: Member). 하지만 만약 다른 패키지에 이름이 같은 엔티티 클래스가 있다면 이름을 지정해서 충돌하지 않도록 해야 한다.
Entity는 기본 생성자가 필수이며 final 클래스, enum, interface, inner 클래스에는 사용할 수 없다. JPA가 엔티티 객체를 생성할 때 기본 생성자를 사용하기 때문에, 이 생성자는 반드시 있어야 하는데 만약 생성자가 하나도 없는 클래스는 자바가 기본 생성자를 자동으로 만든다.
public void Member() {} // 기본 생성자
📌 하지만 개발자가 임의의 생성자를 만들게 되면 자바는 기본 생성자를 자동으로 만들지 않기 때문에, 임의의 생성자를 만들 경우에는 기본 생성자를 직접 더 만들어줘야 하는 걸 잊으면 안 된다.
@Table📘
@Table은 엔티티와 매핑할 테이블을 지정한다. 만약에 어노테이션을 생략한다면 매핑한 엔티티 이름을 테이블 이름으로 사용한다.
이름 매핑 전략 변경하기
단어와 단어를 구분할 때 자바 언어는 관례상 userName과 같이 카멜(Camel) 표기법을 주로 사용하고, 데이터베이스는 관례상 user_name과 같이 언더스코어(_)를 주로 사용한다. 클래스에서는 카멜 표기법으로 작성하고 싶으면 @Column.name 속성을 명시적으로 사용해서 이름을 지어주고 매핑해야 한다.
@Column(name="user_name")
String userName
기본 키 매핑📙
엔티티의 기본 키 매핑은 애플리케이션에서 직접 할당하는 방법과 데이터베이스에 위임하여 할당하는 방법이 있다. 애플리케이션에서 직접 할당은 @Id 어노테이션을 이용하여 기본 키를 할당하였는데, 데이터베이스가 생성해주는 값을 사용하려면 어떻게 매핑해야 할까?
예를 들어, 오라클의 시퀀스 오브젝트라던가 아니면 MySql의 AUTO_INCREMENT 같은 기능을 사용해서 생성된 값을 기본 키로 사용하고 싶을 때, 데이터베이스마다 기본 키를 생성하는 방식이 서로 다르기 때문에 JPA에서는 데이터베이스 기본 키 생성 전략을 제공한다.
📌 자동 생성 : 대리 키 사용 방식
- IDENTITY : 기본 키 생성을 데이터베이스에 위임한다.
- SEQUENCE : 데이터베이스 시퀀스를 사용해서 기본 키를 할당한다.
- TABLE : 키 생성 테이블을 사용한다.
자동 생성 전략을 사용하려면 @Id에 @GeneratedValue를 추가하고 원하는 키 생성 전략을 선택하면 된다. 첫 번째로, IDENTITY 전략은 주로 MySQL, PostfreSQL, SQL Server에서 사용한다.
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
...
}
IDENTITY 전략을 사용해서 Board Entity를 생성하였다. 만약에 밑에 코드처럼 게시글의 Id를 가져오는 코드가 있다면 작동할까?
private static void test(EntityManager em) {
Board board = new Board();
em.persist(board);
System.out.println("board id = " + board.getId());
}
JPA를 공부하신 분이라면 대게 Id값을 가져오지 못한다고 대답했을 것이다. em.persist은 entity를 저장하는 쿼리문을 데이터베이스에 바로 전달하는 것이 아니고, 영속성 컨텍스트에 저장한 후 트랜잭션이 끝나고 난 후에 한 번에 전달하기 때문이다. 하지만 출력 결과는 Id 값으로 1을 가져온다. 왜 그럴까?
엔티티가 영속 상태가 되려면 식별자가 반드시 필요한데 우리는 IDENTITY 식별자 생성 전략을 사용하였고, 이 전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 em.persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달된다. 따라서 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다.
📌 자동 생성 전략을 사용하면 데이터베이스에 값을 저장할 때 ID 칼럼을 비워두면 데이터베이스가 순서대로 값을 채워준다😀
SEQUENCE 전략은 IDENTITY 전략과 비슷한 흐름으로 진행되며, 시퀀스를 지원하는 Oracle, PostgreSQL, H2 데이터베이스에서 사용할 수 있다.
@Entity
@SequenceGenerator(
name = "BOARD_SEQ_GENERATOR",
sequenceName = "BOARD_SEQ", // 매핑할 데이터베이스 시퀀스 이름
initialValue = 1, allocationSize = 1
)
public class Board {
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "BOARD_SEQ_GENERATOR"
)
private Long id;
...
}
여기서 주의할 점은 allocationSize의 기본 값이 50이기 때문에 1로 따로 지정해준 점이다. JPA가 기본으로 생성하는 데이터베이스 시퀀스는 create sequence [sequenceName] start with 1 increment by 50 이므로 시퀀스를 호출할 때마다 값이 50씩 증가한다. 최적화 때문에 50으로 기본값이 설정되어 있는데, 만약 INSERT 성능이 중요하지 않으면 값을 1로 설정하는 것이 좋다.
📌 대게 성능 이슈는 조회 로직에서 많이 나오므로 웬만하면 1로 설정하는 것이 좋다!
TABLE 전략은 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 칼럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략이다. 이 전략을 테이블을 사용하므로 모든 데이터베이스에 적용할 수 있다. TABLE 전략을 사용하려면 먼저 키 생성 용도로 사용할 테이블을 만들어야 한다.
create table MY_SEQUENCE (
sequence_name varchar(255) not null,
next_val bigint,
primary key (sequence_name)
)
sequence_name 컬럼을 시퀀스 이름으로 사용하고 next_val 컬럼을 시퀀스 값으로 사용한다. TABLE 전략은 시퀀스 대신에 테이블을 사용한다는 것만 제외하면 SEQUENCE 전략과 내부 동작 방식이 같다.
@Entity
@TableGenerator(
name = "BOARD_SEQ_GENERATOR",
table = "MY_SEQUENCES",
pkColumnValue = "BOARD_SEQ", allocationSize = 1
)
public class Board {
@Id
@GeneratedValue(
strategy = GenerationType.TABLE,
generator = "BOARD_SEQ_GENERATOR"
)
private Long id;
...
}
먼저 @TableGenerator를 사용해서 테이블 키 생성기를 등록한다. 여기서는 BOARD_SEQ_GENERTATOR라는 이름의 테이블 키 생성기를 등록하고 방금 생성한 MY_SEQUENCES 테이블을 키 생성용 테이블로 매핑했다.
다음으로 TABLE 전략을 사용하기 위해 GenerationType.TABLE을 선택했다. 그리고 @GeneratedValue.generator에 방금 만든 테이블 키 생성기를 지정했다. 이제부터 id 식별자 값은 BOARD_SEQ_GENERATOR 테이블 키 생성기가 할당한다.
📌 TABLE 전략은 SEQUENCE 전략과 비교해서 데이트베이스와 한 번 더 통신하는 단점이 있다.
데이터베이스의 종류도 많고 기본 키를 만드는 방법도 이처럼 다양하다. 마지막으로 AUTO 전략은 GenerationType.AUTO로 설정하고 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다.
데이터베이스 방언
JPA는 특정 데이터베이스에 종속적이지 않은 기술이다. 따라서 다른 데이터베이스로 손쉽게 교체할 수 있다. 그런데 각 데이터베이스가 제공하는 SQL 문법과 함수가 조금씩 다르다는 문제점이 있다. 이처럼 각각의 데이터베이스의 다른 점이나 특정 데이터베이스만의 고유한 기능을 JPA에서는 방언이라 한다.
예를 들어 오라클을 선택하면 SEQUENCE를, MySQL을 선택하면 IDENTITY를 사용한다.
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
}
사실 @GenerationType.strategy의 기본값은 AUTO다. 따라서 다음과 같이 사용해도 결과는 같다.
@Id @GeneratedValue
private Long id;
AUTO 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 것이다. 특히 키 생성 전략이 아직 확정되지 않은 개발 초기 단계나 프로토타입 개발 시 편리하게 사용할 수 있다.
자연 키 VS 대리 키📒
테이블의 기본 키를 선택하는 전략은 크게 2가지가 있다.
종류 | 자연 키 (Natural Key) | 대리 키 (Surrogate Key) |
정의 | - 비즈니스에 의미가 있는 키 - 예: 주민등록번호, 이메일, 전화번호 |
- 비즈니스와 관련 없는 임의로 만들어진 키, 대체 키로도 불린다. - 예: 오라클 시퀀스, auto_increment, 키생성 테이블 |
데이터베이스 기본 키는 null 값을 허용하지 않으며, 유일해야 하고 변해서는 안된다는 3가지 요구조건을 충족해야 한다. 하지만 자연 키 같은 경우는 언제든 변할 수 있는 값이기 때문에 대부분 대리 키를 사용하는 것을 권장한다.
이 밖의 다양한 매핑📚
분류 | 매핑 어노테이션 | 설명 |
필드와 컬럼 매핑 | @Column | 컬럼을 매핑한다. |
@Enumerated | 자바의 enum 타입을 매핑한다. | |
@Temporal | 날짜 타입을 매핑한다. | |
@Lob | BLOB, CLOB 타입을 매핑한다. | |
@Transient | 특정 필드를 데이터베이스에 매핑하지 않는다. | |
기타 | @Access | JPA가 엔티티에 접근하는 방식을 지정한다. |
👨👩👦👦 오픈채팅방 운영
취업을 준비하는 예비 개발자분들을 위한 질문&답변할 수 있는 공간을 만들었습니다. 취업과 이직을 하기 위해서 어떤 걸 중점적으로 준비해야 하는지부터 포트폴리오&이력서 작성법 등 다양한 질문들을 받고 답변을 드립니다. 참여하셔서 다양한 정보 얻고 가시면 좋을 것 같네요😁
참여코드 : 456456
https://open.kakao.com/o/gVHZP8dg
👨💻 전자책 출간
아울러 제가 🌟비전공자에서 2년만에 보안 전문 중견기업으로 이직 한 방법들을 정리한 전자책을 출간 하게 되었습니다. 어떤 걸 공부해야 하는지, 이직을 위해서 무엇을 준비해야 하는지, 제가 받았던 기술 면접 리스트 등 다양한 목차로 구성되어 있습니다. 또한, 구매 시 1:1 채팅을 이용하여 포트폴리오 첨삭을 도와드리고 있습니다. 🐕전자책으로 얻은 모든 수익은 유기견 센터 '팅*벨 입양센터'에 후원될 예정입니다. 관심 있으신 분들은 아래 링크를 참고해주세요😁
마치며
지금까지 엔티티 매핑에 대해 알아보았다. 엔티티 매핑은 크게 4 부류로 나뉘었고, 정말 다양한 매핑이 있다는 것을 알게 되었다. 다음 포스팅은 연관관계 매핑에 대해 알아보려고 한다😄
Reference
'Book Review' 카테고리의 다른 글
자바 ORM 표준 JPA 프로그래밍 - 다양한 연관관계 (0) | 2022.10.30 |
---|---|
자바 ORM 표준 JPA 프로그래밍 - 연관관계 매핑 (0) | 2022.10.23 |
자바 ORM 표준 JPA 프로그래밍 - JPA란? (0) | 2022.09.24 |
[그림으로 공부하는 IT인프라 구조] - 책 리뷰 (0) | 2022.06.27 |
[읽기 좋은 코드가 좋은 코드다] 책 리뷰 (0) | 2022.06.14 |