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
애너테이션은 함께 사용한다.
파라미터가 없는 기본 생성자는 필수로 추가해야한다.
중복되는 엔티티 클래스가 없고, 테이블 이름이 클래스 이름과 같을 경우에는 @Entity
애너테이션과 @Table
애너테이션에 name
애트리뷰트를 지정하지 않고, 클래스 이름으로 사용하는게 권장된다.
JPA에서 지원하는 기본키 생성 전략
기본키 직접 할당
기본키 자동 생성
IDENTITY
AUTO_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)과 같이 기본키를 직접 할당해서 엔티티를 저장한다.
@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
로 지정해주면 된다.
@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
값을 허용할지 여부를 지정한다.true
false
로 지정한다.updatable
true
false
로 지정했다.unique
false
true
로 지정했다.
@Column
애너테이션이 생략된 경우 기본적으로nullable=true
다.그런데 필드의 데이터 타입이 int, long 같은 Java의 원시 타입이라면
null
값을 입력할 수 없다.null
은 객체 타입일 경우에만 적용되기 때문이다.따라서 만약 Java의 원시 타입 필드에서
@Column
애너테이션이 없거나@Column
애너테이션이 있지만 애트리뷰트를 생략한 경우, 최소한nullable=false
는 설정하는 것이 에러를 미연에 방지할 수 있다.
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
을 사용하는 것이 좋다.