엔티티 매핑

seongmin·2022년 11월 4일
0

Spring

목록 보기
31/38
post-thumbnail

JPA를 이용해 데이터베이스의 테이블과 상호 작용(데이터 저장, 수정, 조회, 삭제 등) 하기 위해 제일 먼저 해야되는 작업이 데이터베이스의 테이블과 엔티티 클래스 간의 매핑 작업이다.

엔티티 매핑 작업 종류

  • 객체와 테이블 간의 매핑
  • 기본키 매핑
  • 필드(멤버 변수)와 컬럼 간의 매핑
  • 엔티티 간의 연관 관계 매핑

엔티티와 테이블 간의 매핑

@Entity(name = "USERS")
@Table(name = "USERS")
public class Member {
    @Id
    private Long memberId;
}
  • @Entity

    • 클래스 레벨에 사용하며, 사용 시 JPA 관리 대상 엔티티가 된다.

    • name 애트리뷰트를 통해 엔티티명을 설정할 수 있고, 미설정 시 기본값으로 클래스명을 엔티티명으로 사용한다.

  • @Table

    • name 애트리뷰트의 특징은 위와 같다.

    • @Table 애너테이션은 옵션이며, 추가하지 않을 경우 클래스 이름을 테이블 이름으로 사용한다.

    • 주로 테이블 이름이 클래스 이름과 달라야 할 경우에 추가한다.

주의사항

  • @Table 애너테이션은 옵션이지만 @Entity 애너테이션과 @Id 애너테이션은 필수다.

  • @Entity 애너테이션과 @Id 애너테이션은 함께 사용한다.

  • 파라미터가 없는 기본 생성자는 필수로 추가해야한다.

    • Spring Data JPA의 기술을 적용할 때 기본 생성자가 없는 경우 에러가 발생하는 경우가 있기 때문이다.
  • 중복되는 엔티티 클래스가 없고, 테이블 이름이 클래스 이름과 같을 경우에는 @Entity 애너테이션과 @Table 애너테이션에 name 애트리뷰트를 지정하지 않고, 클래스 이름으로 사용하는게 권장된다.

기본키 매핑

JPA에서 지원하는 기본키 생성 전략

  • 기본키 직접 할당

    • 애플리케이션 코드 상에서 기본키를 직접 할당 해주는 방식
  • 기본키 자동 생성

IDENTITY

  • 데이터베이스에서 기본키를 대신 생성해준다.
  • 데이터베이스에서 기본키를 생성해주는 대표적인 방식은 MySQLAUTO_INCREMENT 기능을 통해 자동 증가 숫자를 기본키로 사용하는 방식이 있다.

SEQUENCE

  • 데이터베이스에서 제공하는 시퀀스를 사용해서 기본키를 생성하는 전략이다.

TABLE

  • 별도의 키 생성 테이블을 사용하는 전략이다.

기본키 직접 할당 전략

@Configuration
public class JpaIdDirectMappingConfig {
    private EntityManager em;
    private EntityTransaction tx;

    @Bean
    public CommandLineRunner testJpaSingleMappingRunner(EntityManagerFactory emFactory){
        this.em = emFactory.createEntityManager();
        this.tx = em.getTransaction();

        return args -> {
            tx.begin();
            em.persist(new Member(1L));  // (1)
            tx.commit();
            Member member = em.find(Member.class, 1L);

            System.out.println("# memberId: " + member.getMemberId());
        };
    }
}

(1)과 같이 기본키를 직접 할당해서 엔티티를 저장한다.

IDENTITY 전략

@NoArgsConstructor
@Getter
@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // (1)
    private Long memberId;

    public Member(Long memberId) {
        this.memberId = memberId;
    }
}

IDENTITY 기본키 생성 전략을 설정하려면 (1) 과 같이 @GeneratedValue 애너테이션의 strategy 애트리뷰트의 값을 GenerationType.IDENTITY 로 지정해주면 된다.

SEQUENCE 전략

@NoArgsConstructor
@Getter
@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)  // (1)
    private Long memberId;

    public Member(Long memberId) {
        this.memberId = memberId;
    }
}

SEQUENCE 전략을 사용하기 위해서는 @GeneratedValue(strategy = GenerationType.SEQUENCE) 를 지정하면 된다.

엔티티 필드와 컬럼 간의 매핑

@NoArgsConstructor
@Getter
@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long memberId;

		// (1)
    @Column(nullable = false, updatable = false, unique = true)
    private String email;

		...
		...
		
    public Member(String email) {
        this.email = email;
    }
}

@Column 애너테이션은 필드와 컬럼을 매핑해주는 애너테이션이다. 만약 @Column 애너테이션이 없고, 필드만 정의되어 있다면 JPA는 기본적으로 이 필드가 테이블의 컬럼과 매핑되는 필드라고 간주하게 된다. 또한, @Column 애너테이션에 사용되는 애트리뷰트의 값은 디폴트 값이 모두 적용된다.


애트리뷰트

  • nullable

    • 컬럼에 null 값을 허용할지 여부를 지정한다.
    • default = true
    • email 주소는 일반적으로 회원 정보에서 ID로 많이 사용되며, 따라서 필수 항목이기때문에 nullable 값을 false 로 지정한다.
  • updatable

    • 컬럼 데이터를 수정할 수 있는지 여부를 지정한다.
    • default = true
    • 여기서는 email 주소가 사용자 ID 역할을 한다고 가정하고 한번 등록되면 수정이 불가능하도록 하기 위해서 updatable 값을 false 로 지정했다.
  • unique

    • 하나의 컬럼에 unique 유니크 제약 조건을 설정한다.
    • default = false
    • email의 경우 고유한 값이어야 하므로 unique 값을 true 로 지정했다.

@Column 애너테이션이 생략된 경우 기본적으로 nullable=true 다.

그런데 필드의 데이터 타입이 int, long 같은 Java의 원시 타입이라면 null 값을 입력할 수 없다. null 은 객체 타입일 경우에만 적용되기 때문이다.

따라서 만약 Java의 원시 타입 필드에서 @Column 애너테이션이 없거나 @Column 애너테이션이 있지만 애트리뷰트를 생략한 경우, 최소한 nullable=false 는 설정하는 것이 에러를 미연에 방지할 수 있다.


  • length 애트리뷰트는 컬럼에 저장할 수 있는 문자 길이를 지정하는 것으로 디폴트 값은 255 이다.
@Column(length = 100, nullable = false)
    private String name;
  • java.util.Date, java.util.Calendar 타입으로 매핑하기 위해서는 @Temporal 애너테이션을 추가해야하지만 아래와 같이 LocalDateTime 타입일 경우, @Temporal 애너테이션은 생략 가능하다.

  • LocalDateTime 은 컬럼의 TIMESTAMP 타입과 매핑된다.

  • 회원 정보가 등록되는 시간 정보를 필드에 전달하기 위해 createdAt 필드에 LocalDateTime.now() 메서드로 현재 시간을 입력하고 있다.

@Column(nullable = false)
    private LocalDateTime createdAt = LocalDateTime.now();
  • @Column 애너테이션의 name 애트리뷰트를 생략하면 엔티티 클래스 필드의 이름으로 컬럼이 생성되지만 아래와 같이 name 애트리뷰트에 별도의 이름을 지정해서 엔티티 클래스 필드명과 다른 이름으로 컬럼을 생성할 수 있다.
@Column(nullable = false, name = "LAST_MODIFIED_AT")
    private LocalDateTime modifiedAt = LocalDateTime.now();
  • @Transient 애너테이션을 필드에 추가하면 테이블 컬럼과 매핑하지 않겠다는 의미로 JPA가 인식한다.
    • 따라서 데이터베이스에 저장 하지 않고, 조회할 때 역시 매핑되지 않는다.
    • @Transient 는 주로 임시 데이터를 메모리에서 사용하기위한 용도로 사용된다.
@Transient
    private String age;
  • @Enumerated 애너테이션은 enum 타입과 매핑할 때 사용하는 애너테이션으로 2가지 타입을 갖는다.
    • EnumType.ORDINAL : enum의 순서를 나타내는 숫자를 테이블에 저장한다.
    • EnumType.STRING : enum의 이름을 테이블에 저장한다.

EnumType.ORDINAL 로 지정할 경우, 기존에 정의되어 있는 enum 사이에 새로운 enum 하나가 추가 된다면 그때부터 테이블에 이미 저장되어 있는 enum 순서 번호와 enum에 정의되어 있는 순서가 일치하지 않게 되는 문제가 발생한다.

따라서 처음부터 이런 문제가 발생하지 않도록 EnumType.STRING 을 사용하는 것을 권장하고 있다.

@Enumerated(EnumType.STRING)
    private OrderStatus orderStatus = OrderStatus.ORDER_REQUEST;

앤티티와 테이블 매핑 권장 사용 방법

  • 클래스 이름 중복 등의 특별한 이유가 없다면 @Entity@Id 애너테이션만 추가한다.

    • 만일 엔티티 클래스가 테이블 스키마 명세의 역할을 하길 바란다면 @Table 애너테이션에 테이블명을 지정해줄 수 있다.
  • 기본키 생성 전략은 데이터베이스에서 지원해주는 AUTO_INCREMENT 또는 SEQUENCE를 이용할 수 있도록 IDENTITY 또는 SEQUENCE 전략을 사용하는 것이 좋다.

  • @Column 정보를 명시적으로 모두 지정하는 것은 번거롭긴하지만 다른 누군가가 엔티티 클래스 코드를 확인하더라도 테이블 설계가 어떤식으로 되어 있는지 한눈에 알 수 있다는 장점이 있다.

  • 엔티티 클래스 필드 타입이 Java의 원시타입일 경우, @Column 애너테이션을 생략하지 말고, 최소한 nullable=false 설정을 하는게 좋다.

  • @Enumerated 애너테이션을 사용할 때 EnumType.ORDINAL 을 사용할 경우, enum의 순서가 뒤바뀔 가능성도 있으므로 처음부터 EnumType.ORDINAL 대신에 EnumType.STRING 을 사용하는 것이 좋다.

0개의 댓글