Book Review

자바 ORM 표준 JPA 프로그래밍 - 엔티티 매핑

빈코 2022. 10. 8. 14:04

JPA를 사용하는 데 가장 중요한 일은 엔티티와 테이블을 정확히 매핑하는 것이다. 따라서 매핑 어노테이션을 정확히 숙지하고 사용해야 한다. JPA는 다양한 매핑 어노테이션을 지원하는데 크게 4가지로 분류한다. 오늘은 이 4가지 부류의 매핑에 대해서 알아보려 한다.

 

로고
JPA

 

개요

매핑 어노테이션은 크게 객체와 테이블 매핑, 기본 키 매핑, 필드와 칼럼 매핑, 연관관계 매핑으로 나눌 수 있다. 물론 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

 

비전공 개발자 취업 준비방(질문&답변)

#비전공 #개발자 #취업 #멘토링 #부트캠프 #국비지원 #백엔드 #프론트엔드 #중소기업 #중견기업 #자바 #Java #sql

open.kakao.com

 


👨‍💻 전자책 출간

아울러 제가  🌟비전공자에서 2년만에 보안 전문 중견기업으로 이직 한 방법들을 정리한 전자책을 출간 하게 되었습니다. 어떤 걸 공부해야 하는지, 이직을 위해서 무엇을 준비해야 하는지, 제가 받았던 기술 면접 리스트 등 다양한 목차로 구성되어 있습니다. 또한, 구매 시 1:1 채팅을 이용하여 포트폴리오 첨삭을 도와드리고 있습니다. 🐕전자책으로 얻은 모든 수익은 유기견 센터 '팅*벨 입양센터'에 후원될 예정입니다. 관심 있으신 분들은 아래 링크를 참고해주세요😁

https://kmong.com/gig/480954

 

비전공개발자 2년만에 중견기업 들어간 방법 | 14000원부터 시작 가능한 총 평점 0점의 전자책, 취

0개 총 작업 개수 완료한 총 평점 0점인 Binco의 전자책, 취업·이직 전자책 서비스를 0개의 리뷰와 함께 확인해 보세요. 전자책, 취업·이직 전자책 제공 등 14000원부터 시작 가능한 서비스

kmong.com


 

마치며

지금까지 엔티티 매핑에 대해 알아보았다. 엔티티 매핑은 크게 4 부류로 나뉘었고, 정말 다양한 매핑이 있다는 것을 알게 되었다. 다음 포스팅은 연관관계 매핑에 대해 알아보려고 한다😄

 

Reference

자바 ORM 표준 프로그래밍 - 김영한

반응형