JPA

[JPA] Entity 매핑 - 기본

이덩우 2024. 1. 25. 23:07

1. 객체 - DB 테이블 간 매핑

@Entity

  • `@Entity`애노테이션을 붙여야 JPA에서 인식하고 관리한다.
  • 따라서 JPA를 사용해 DB 테이블과 매핑할 클래스에는 `@Entity`를 꼭 붙여줘야한다.
  • 주의사항
    • 기본 생성자는 필수이다. JPA가 내부적으로 리플렉션을 통해 기본 생성자를 호출하기 때문에 넣어줘야한다.
    • final, inner 클래스로 생성할 수 없고, interface나 enum으로도 생성할 수 없다.
    • 저장할 필드에도 final 키워드를 사용할 수 없다.
    • PK로 사용될 필드에 `@Id` 애노테이션을 꼭 붙여야한다.
@Entity
public class Member {
    @Id
    private Long id;

    private String userName;

    public Member() {
    }
}

 

@Table

  • 엔티티와 매핑할 DB 테이블을 지정해주는 애노테이션이다.
  • 주로 엔티티 클래스 이름과 테이블 명이 다를 때, 혹은 유니크 제약조건을 걸기 위해 사용한다.
  • 따라서 속성으로 `name`, `uniqueConstraints`등을 부여할 수 있다.
@Entity
@Table(name = "USER")
public class Member {
    @Id
    private Long id;

    private String userName;

    public Member() {
    }
}

 


2. 객체 필드 - DB 테이블 컬럼 간 매핑

@Column

  • 주로 필드와 매핑할 DB 컬럼명이 다른 경우 사용한다.
  • 필드 레벨에 유니크 제약 조건을 걸 수 도 있다. 하지만 제약 조건 이름이 랜덤값으로 생성되기 때문에 `@Table`에서 지원하는 유니크 제약 조건 기능을 사용하길 추천한다. (이름을 지정할 수 있음)
  • null 허용 여부, 문자 길이 제약 등 다양한 속성을 지원한다.
@Column(name = "name", nullable = false)
private String userName;

 

@Enumerated

  • 자바 Enum 타입을 필드로 활용할 때 사용되는 애노테이션이다.
  • 아무 설정도 하지 않으면 기본 옵션이 `ORDINAL`타입인데, 이는 해당 Enum의 인덱스를 DB에 저장하는 방식이다.
  • 나중에라도 Enum 클래스가 수정된다면 인덱스가 변경되어 문제를 발생할 수 있다.
  • 따라서 DB에 인덱스가 아닌 문자열 그대로를 저장하는 `STRING`타입의 옵션을 사용해야한다.
@Enumerated(EnumType.STRING)
private RoleType roleType;

 

@Transient

  • 필드에 아무런 애노테이션을 달지 않으면 JPA는 이를 실제 DB 컬럼과 매핑된 정보로 인식한다.
  • 만약 DB 컬럼에 종속되지 않고 사용하고 싶은 경우 해당 애노테이션을 통해 매핑을 안할 수 있다.
    ex) 로컬 테스트, 캐시
@Transient
private String testString;

 

@Lob

  • 데이터베이스 BLOB, CLOB과 매핑한다.
  • 필드 타입이 문자면 CLOB 매핑, 나머지는 BLOB으로 매핑한다.

 

@Temporal

  • 자바의 `Date`클래스를 DB 컬럼과 매핑시킬 때 사용하는 애노테이션이다.
  • 옵션으로 `DATE`, `TIME`, `TIMESTAMP` 세 가지로 설정할 수 있다.
  • 현재 Hibernate는 자바의 `LocalDate`, `LocalDateTime`을 사용하면 자동으로 매핑해준다!
  • 만약 과거의 프로젝트, 혹은 특별한 이유로 `Date`를 써야만 한다면 해당 애노테이션으로 매핑하면 된다.

 


3. 기본 키(PK) 매핑

수동 생성

  • PK로 사용할 필드에 단순히 @Id 애노테이션만 붙이고, 직접 Setter나 생성자를 통해 수동으로 생성한다.
@Id
private Long id;


void main() {
	...
	member.setId(1L);
	em.persist(member);
}

 

자동 생성(1) - IDENTITY 전략

  • 기본 키 생성을 DB에 위임한다.
  • 주로 `MySQL`, PostgreSQL에서 사용한다. (MySQL의 `AUTO_INCREMENT`)
  • 이 방식을 사용하면 JPA에서 쿼리를 DB에 전달하는 시점이 약간 달라진다.
    • 기본 원칙은 트랜잭션이 커밋될 때 `flush()`가 호출되면서 실제 쿼리를 전달하게 되는데, `IDENTITY`전략을 사용한다면 `em.persist()`를 호출할 경우 실제로 네트워크를 타서 DB에 접근해봐야 PK값을 얻어올 수 있다.
    • 만약 트랜잭션이 커밋되기 전 해당 객체를 조회하는 일이 발생한다면 PK 정보가 없기 때문에 정확한 정보를 조회할 수 없다. 따라서 `IDENTITY` 전략을 사용하는 경우, `em.persist()`시점에 트랜잭션의 커밋과 별개로 INSERT 쿼리는 즉시 전달하게된다.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

 

자동 생성(2) - SEQUENCE 전략

  • 유일한 값을 순서대로 생성해주는 시퀀스 오브젝트를 활용해 PK값을 자동으로 생성하는 전략이다.
  • 테이블마다 다른 시퀀스를 적용하고 싶으면 `@SequenceGenerator`를 통해 새로 생성하면 된다.
  • 주로 `Oracle`, `H2 DB`에서 사용한다.
@Entity
@SequenceGenerator(
        name = "MEMBER_SEQ_GENERATOR",
        sequenceName = "MEMBER_SEQ",
        initialValue = 1, allocationSize = 1)
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR")
    private Long id;

 

자동 생성(3) - TABLE 전략

  • 시퀀스 오브젝트와 동일한 역할을 하는 테이블을 따로 생성해 흉내내는 전략이다.
  • 테이블을 직접 생성하고 다루기 때문에 성능이 단점이다.
  • 모든 데이터베이스에서 사용할 수 있다는 것이 장점이다.
@Entity
@TableGenerator(
        name = "MEMBER_SEQ_GENERATOR",
        table = "MEMBER_SEQS",
        pkColumnName = "MEMBER_SEQ", allocationSize = 1)
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "MEMBER_SEQ_GENERATOR")
    private Long id;

 

 

성능 최적화

  • `SEQUENCE`전략이나 `TABLE`전략 모두 INSERT 쿼리가 발생할 때 시퀀스 call을 통해 다음 시퀀스를 알아와야 하는데, 매 번 네트워크를 타기 때문에 성능이 좋지 않다.
  • 이를 해결하기위해 JPA는 `Generator`속성에 `allocationSize`를 지원하는데, 한번에 몇 개의 시퀀스를 가져와 메모리에 올려놓고 사용할지 정하는 것이다.
  • 기본값은 `50`이다. 이렇게되면 최초 1~50의 시퀀스를 가져와 50번의 INSERT 요청 동안은 시퀀스 call이 발생하지 않아 성능 상 이점을 얻는다. 
  • `동시성`을 고려해도 문제가 없다. 현재 DB의 시퀀스를 기준으로 각자 50개씩 가져가기때문에, 각 환경에서 메모리에 적재해놓고 사용할 시퀀스가 겹칠 일은 업다.

 


다음은?

지금까지 기본적인 테이블 매핑, 컬럼 매핑, 기본 키 매핑을 알아봤고 다음 포스팅에는 `DB에 맞춰 엔티티를 설계하는 것`이 아니라 `객체지향에 초점을 맞춰 엔티티를 설계`할 수 있는 `연관관계 매핑`에 대해서 알아볼 것이다!

 

 

출처 : 인프런, 김영한의 자바 ORM 표준 JPA 프로그래밍 - 기본편