JPA를 사용하는 데 가장 중요한 일은 엔티티와 테이블을 정확히 매핑하는 것이다. 따라서 매핑 어노테이션을 숙지하고 사용해야 한다.
@Entity
어노테이션은 JPA를 사용해서 테이블과 매핑해야하는 클래스에 필수로 붙여야 한다.
기본 생성자 필수.
자바는 생성자가 없으면 기본생성자를 자동 생성한다.
public Member() {} // 기본 생성자
생성자를 하나 이상 만들면 기본 생성자를 자동으로 만들지 않는다.
// 직접 만든 기본 생성자
public Member () {}
public Member (String name) {
this.name = namw
}
final 클래스, enum, interface, inner 클래스에는 사용 불가.
저장할 필드에 final을 사용하면 안 됨.
@Table
어노테이션은 엔티티와 매핑할 테이블을 지정한다. 생략하면 매핑한 엔티티 이름을 테이블 이름으로 사용한다.
간단히 어떤 것들이 있는지 확인해보자. (자세한 설명은 7. 필드와 컬럼 매핑: 레퍼런스
에서 😊)
회원은 일반 회원과 관리자로 구분
@Enumerated(EnumType.STRING)
private RoleType roleType;
// RoleType
public enum RoleType {
ADMIN, USER
}
자바의 Enum은 @Enumerated
어노테이션을 사용하여 매핑
회원 가입일과 수정일 존재
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
자바의 날씨 타입은 @Temporal
어노테이션을 사용하여 매핑
회원을 설명할 수 있는 필드 존재 (길이 제한 없음)
@Lob
private String description;
길이 제한이 없는 필드는 @Lob
어노테이션을 사용하여 매핑
JPA는 데이터베이스 스키마를 자동으로 생성하는 기능을 지원한다. 클래스의 매핑 정보를 보면 어떤 테이블에 어떤 컬럼을 사용하는지 알 수 있다.
예시
<property name="hibernate.hbm2ddl.auto" value="create" />
persistence.xml에 이 속성을 추가하면 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성한다(create).
운영 서버에서 create, create-drop처럼 DDL을 수정하는 옵션은 절대 사용하면 안 된다. 오직 개발 서버나 개발 단계에서만 사용해야 한다. 이 옵션들은 운영 중인 데이터베이스의 테이블이나 컬럼을 삭제할 수 있다.
@Entity
@Table(name="MEMBER")
public class Member {
@Id
@Column(name = "ID")
private String id;
@Column(name = "NAME")
private String username;
// ...
}
이 엔티티는 어떤 DDL이 생성될까?
create table MEMBER (
ID varcher(255) not null,
NAME varchar(255),
...
primary key (ID)
)
여기서 제약조건이 추가되었다고 생각하자.
그럼 엔티티는 이렇게 수정될 것이다.
@Entity
@Table(name="MEMBER")
public class Member {
@Id
@Column(name = "ID")
private String id;
@Column(name = "NAME", nullable = false, length = 10)
private String username;
// ...
}
그럼 어떤 DDL이 생성될까?
create table MEMBER (
ID varcher(255) not null,
NAME varchar(10) not null,
...
primary key (ID)
)
NAME 컬럼은 보면 not null이 추가되었고, varchar 조건이 10으로 바뀐 것을 확인할 수 있다. 자세한 것은 뒤에서 설명하겠다.
이런 기능들은 단지 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.
직접 할당 예시
@Id
@Column(name = "id")
private String id;
@Id
적용 가능 자바 타입
기본 키 생성을 데이터베이스에 위임하는 전략이다.
데이터베이스에 값을 저장할 때 ID 칼럼을 비워두면 데이터베이스가 순서대로 값을 채워준다.
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // 추가
private Long id;
// ...
}
데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트다. SEQUENCE 전략은 이 시퀀스를 사용해서 기본 키를 생성한다.
시퀀스 생성
CREATE TABLE BOARD (
ID BIGINT NOT NULL PRIMARY KEY,
DATA VARCHER(255)
)
// 시퀀스 생성
CREATE SEQUENCE BOARD_SEQ START WITH 1 INCREMENT BY 1;
시퀀스 매핑 코드
@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;
// ...
}
IDENTITY 전략과 SEAUENCE 전략의 차이
@SequenceGenerator
키 생성 전용 테이블을 하나 만들고 여기에 이름과 값으로 사용할 컬럼을 만들어 데이터베이스 시퀀스를 흉내내는 전략이다.
키 생성 용도 테이블
create table MY_SEQUENCE (
sequence_name varchar(255) not null,
next_val bigint,
primary key (sequence_name)
)
테이블 전략 매핑 코드
@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;
// ...
}
시퀀스 대신에 테이블을 사용한다는 것만 제외하면 SEQUENCE 전략과 내부 동작방식이 같다.
@TableGenerator
GenerationType.AUTO는 선택한 데이터베이스 방언에 따라 IDENTITY, SWQUENCE, TABLE 전략 중 하나를 자동으로 선택한다.
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// ...
}
장점
객체 필드를 테이블 컬럼에 매핑한다.
자바의 enum 타입을 매핑할 때 사용한다.
속성
날짜 타입(java.util.Date, java.util.Calendar)을 매핑할 때 사용한다.
속성
데이터베이스 BLOB, CLOB 타입과 매핑한다.
매핑하는 필드 타입이 문자면 CLOB으로 매핑하고 나머지는 BLOB로 매핑한다.
이 필드는 매핑하지 않는다.
데이터베이스에 저장하지도 조회하지도 않는다. 객체에 임시로 어떤 값을 보관하고 싶을 때 사용한다.
JPA가 엔티티 데이터에 접근하는 방식을 지정한다.
@Access를 설정하지 않으면 @Id의 위치를 기분으로 접근 방식이 설정된다.
필드 접근: AccessType.FIELD
@Entity
@Access(AccessType.FIELD)
public class Member {
@Id
private String id;
private String data1;
private String data2;
// ...
}
프로퍼티 접근: AccessType.PROPERTY
@Entity
@Access(AccessType.PROPERTY)
public class Member {
private String id;
private String data1;
private String data2;
@Id
public String getId() {
return id;
}
@Column
public String getData1() {
return data1;
}
@Column
public String getData2() {
return data2;
}
}
👍👍