프로그래머스 백엔드 데브코스 TIL 35일차

Inchang Choi·2022년 5월 11일
0

백엔드 데브코스 TIL

목록 보기
20/30
post-thumbnail

엔티티와 테이블을 정확히 매핑하는 것은 JPA를 사용 할 때 가장 중요한 일 중 하나 입니다.

JPA에서 지원하는 매핑 어노테이션은 크게 4가지로 분류할 수 있습니다.

  • 객체와 테이블 매핑 : @Entity, @Table
  • 기본 키 매핑 : @Id
  • 필드와 컬럼 매핑 : @Column
  • 연관관계 매핑 : @ManyToOne, @JoinColumn

@Entity

JPA를 사용해서 테이블과 매핑할 클래스는 @Entity을 필수로 붙여야 합니다.

@Entity 적용 시 주의사항은 다음과 같습니다.

  • 기본 생성자를 반드시 작성해야 합니다. (파라미터가 없는 public 또는 protected 생성자)
  • final 클래스, enum, interface, inner 클래스에는 사용할 수 없습니다.
  • 저장할 필드에 final을 사용하면 안 됩니다.

기본 생성자가 하나도 없으면 기본 생성자를 자동으로 만들며, 만약 생성자를 하나 이상 만들면 기본 생성자를 직접 만들어 줘야합니다.

@Table

@Table은 엔티티와 매핑할 테이블을 지정합니다.

다양한 매핑 사용

  • Enum을 사용하려면 @Enumerated으로 매핑 하여야 합니다.
  • 자바의 날짜 타입은 @Temporal을 사용해서 매핑합니다.
  • 길이 제한이 없는 필드는 @Lob을 사용하여 CLOB, BLOB 타입으로 매핑 할 수 있습니다.

데이터베이스 스키마 자동 생성

스프링에서는 application.yaml 또는 application.properties를 통해 hibernate 설정으로 데이터베이스 스키마 자동 생성에 관한 설정을 할 수 있습니다.

hibernate.hdm2ddl.auto 속성

DDL 생성 기능

@Column(name = "NAME", nullable = false, length = 10)
private String name;

@Column의 속성 값으로 DDL 생성 시의 제약조건을 추가할 수 있습니다.

@Table(name = "MEMBER", uniqueConstraints = {@UniqueConstraint (
	name = "NAME_AGE_UNIQUIE",
	columnNames = {"NAME", "AGE"} ) } )

@UniqueConstraint로 테이블에도 유니크 제약조건을 추가할 수 있습니다.

기본 키 매핑

데이터베이스 마다 기본 키를 생성하는 방식이 서로 다릅니다.

JPA에서는 아래와 같은 기본 키 생성 전략을 제공합니다.

  • 직접 할당 : 기본 키를 애플리케이션에서 직접 할당합니다.
  • 자동 생성 : 대리 키 사용 방식
    • IDENTITY : 기본 키 생성을 데이터베이스에 위임합니다.
    • SEQUENCE : 데이터베이스 시퀀스를 사용해서 기본 키를 할당합니다.
    • TABLE : 키 생성 테이블을 사용합니다.

데이터베이스 벤더마다 지원하는 방식이 달라, SEQUENCE나 IDENTITY 전략은 사용하는 데이터베이스에 의존합니다.
그러나 TABLE 전략은 키 생성용 테이블을 하나 만들어 두고 시퀀스 처럼 사용하는 방법입니다.
이 전략은 테이블을 사용하므로 모든 데이터베이스에서 사용할 수 있습니다.

기본 키 직접 할당 전략

@Id 적용 가능 자바 타입은 아래와 같습니다.

  • 자바 기본형
  • 자바 Wrapper형
  • String
  • java.util.Date
  • java.sql.Date
  • java.math.BigDecimal
  • java.math.BigInteger

IDENTITY 전략

IDENTITY는 기본 키 생성을 데이터베이스에 위임하는 전략입니다.

IDENTITY 전략의 특징은 아래와 같습니다.

  • 데이터베이스에 값을 저장한 후에 기본 키 값을 구할 때 사용합니다.
  • 데이터베이스에 INSERT한 후에 기본 키 값을 조회 할 수 있습니다.
  • 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있기 때문에 persist()를 호출하는 즉시 INSERT SQL이 데이터베이스에 전달 됩니다. 그렇기 때문에 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않습니다.

SEQUENCE 전략

SEQUENCE 전략은 유일한 값을 순서대로 생성하는 데이터베이스 오브젝트를 사용하여 기본 키를 생성하는 전략입니다.

persist()를 호출할 때 먼저 데이버베이스 시퀀스를 사용해서 식별자를 조회합니다.

그리고 조회한 식별자를 엔티티에 할당한 후 엔티티를 영속성 컨텍스트에 저장합니다.

후 트랜잭션을 커밋해서 플러시가 일어나면 엔티티를 데이터베이스에 저장합니다.

💡 SquenceGenerator.allocationSize는 최적화를 위해 기본값이 50으로 설정되어 있습니다. allocationSize 값이 50이면 시퀀스를 한 번에 50 증가 시킨 다음에 1~50까지는 메모리에서 식별자를 할당합니다. 그리고 51이 되면 시퀀스 값을 100으로 증가 시킨 다음 이를 반복합니다. 이 최적화 방법은 시퀀스 값을 선점 하므로 여러 JVM이 동시에 동작 해도 기본 키 값이 충돌하지 않는 장점이 있습니다. 반면에 데이터베이스에 직접 접근해서 데이터를 등록할 때 시퀀스 값이 한번에 많이 증가한다는 점을 염두 해 두어야 합니다. 참고로 앞서 설명한 hibernate.id.new_generator_mappings속성을 true로 설정해야 지금까지 설명한 최적화 방법이 적용된다.

TABLE 전략

TABLE 전략은 키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이서 시퀀스를 흉내내는 전략입니다. 이 전략은 테이블을 사용하므로 모든 데이터베이스에 적용할 수 있습니다.

💡 TABLE 전략은 값을 조회하면서 SELECT 쿼리 를 사용하고 다음 값으로 증가시키기 위해 UPDATE 쿼리를 사용합니다. 이 전략은 SEQUENCE 전략과 비교해서 데이터베이스와 한번 더 통신하는 단점이 있습니다. TABLE 전략을 최적화하려면 TableGenerator.allocationSize를 사용하면 됩니다.

AUTO 전략

AUTO 전략은 데이터베이스 방언에 따라 앞에서 나온 기본 키 생성 전략 중 하나를 자동으로 선택합니다.

이 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 것입니다.

SEQUENCE나 TABLE 전략이 선택되면 시퀀스나 키 생성용 테이블을 미리 만들어 두어야 합니다.

기본 키 매핑 정리

persist()를 호출한 직후에 발생하는 일을 식별자 할당 전략 별로 정리하면 아래와 같습니다.

  • 직접 할당 : persist()를 호출하기 전에 애플리케이션에서 직접 식별자 값을 할당해야 합니다. 만약 식별자 값이 없으면 예외가 발생합니다.
  • IDENTITY : 데이터베이스에 엔티티를 저장해서 식별자 값을 획득한 후 영속성 컨텍스트에 저장합니다.
    • IDENTITY 전략은 테이블에 데이터를 저장해야 식별자 값을 획득할 수 있습니다.
  • SEQUENCE : 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장합니다.
  • TABLE : 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득한 후 영속성 컨텍스트에 저장합니다.

기본 키는 다음 3가지 조건을 모두 만족해야 합니다.

  1. null 값은 허용하지 않습니다.
  2. 유일 해야합니다.
  3. 변해서는 안됩니다.

기본 키 선택 전략은 크게 2가지가 있습니다.

  1. 자연 키 : 비즈니스 적으로 의미가 있는 키 (휴대전화 번호, 주민번호, 이메일 등)
  2. 대리 키 : 비즈니스와 관련 없는 임의로 만들어진 키, 대체 키로도 불립니다. (시퀸스, 키 생성 테이블 사용 등)

비즈니스적 환경은 언제나 변하기 때문에 현재는 물론이고 미래가지 충족하는 자연키를 찾는 것은 쉽지 않습니다. 대리 키는 비즈니스와 무관한 임의의 값이므로 요구사항이 변경되어도 기본 키가 변경되는 경우는 드뭅니다.

그렇기 때문에 자연 키보다는 대리 키의 사용을 권장합니다.

필드와 컬럼 매핑

@Column

@Column은 객체 필드를 테이블 컬럼에 매핑 합니다.

@Column을 자바 기본 타입 필드 위에서 사용할 때는 nullable = true가 기본 값이므로 기본 타입에 null 값이 들어갈 수 있습니다. 그렇기 때문에 기본 타입에 @Column을 사용하면 nullable = false로 지정하는 것이 안전합니다.

@Enumerated

EnumType.ORDINAL은 enum에 정의된 순서대로 데이터베이스에 저장됩니다.

  • 장점 : 데이터베이스에 저장되는 데이터 크기가 작다
  • 단점 : 이미 저장된 enum의 순서를 변경할 수 없다

EnumType.STRING은 enum 이름 그대로 문자로 데이터베이스에 저장됩니다.

  • 장점 : 저장된 enum의 순서가 바뀌거나 enum이 추가되어도 안전하다
  • 단점 : 데이터베이스에 저장되는 데이터 크기가 ORDINAL에 비해서 크다

💡 기본 값인 ORDINAL은 사이에 enum이 하나 추가되서 설정되면 기존에 있던 정보와 데이터베이스의 정보가 달라질 수 있기 때문에 주의해서 사용해야 합니다.

@Temporal

날짜 타입을 매핑할 때 사용합니다.

자바의 Date 타입에는 년월일 시분초가 있지만 데이터베이스에는 date, time, timestamp라는 세 가지 타입이 별도로 존재합니다.

@Temporal을 생략하면 자바의 Date와 가장 유사한 timestamp로 정의 됩니다. 다른 예약어를 사용하는 데이터베이스에서는 데이터베이스 방언에 따라 자동으로 해당하는 예약어로 설정 됩니다.

@Lob

데이터베이스 BLOB, CLOB 타입과 매핑합니다.

@Lob에는 지정할 수 있는 속성이 없습니다.

대신에 매핑하는 필드 타입이 문자면 CLOB으로 매핑하고 나머지는 BLOB으로 매핑합니다.

@Transient

이 필드는 매핑하지 않으며, 데이터베이스에 저장하지 않고 조회하지도 않습니다.

객체에 임시로 어떤 값을 보관하고 싶을 때 사용합니다.

@Access

JPA가 엔티티 데이터에 접근하는 방식을 지정합니다.

  • 필드 접근 : AccessType.FIELD로 지정합니다. 필드에 직접 접근하며, 필드 접근 권한이 private이어도 접근할 수 있습니다.
  • 프로퍼티 접근 : AccessType.PROPERTY로 지정합니다. 접근자(Getter)를 사용합니다.

설정하지 않으면 보통 @Id의 위치를 기준으로 접근 방식이 설정되며, 함께 사용할 수도 있습니다.

profile
always positive

0개의 댓글