[JPA 기초] 03. 엔티티 매핑
카테고리: JPA
태그: Java
Entity Mapping
Java Object와 DB Table을 서로 연결한다고 이해하면 된다.
@Entity
public class Member {
@Id
private Long id;
@Column(name = "name")
private String username;
private Integer age;
/* Getter, Setter... */
}
Java Entity | DB Table |
---|---|
Member | Table |
username, age, etc. | column |
id | pk |
- 객체와 테이블 매핑:
@Entity
,@Table
- Member 객체와 Member 테이블을 매핑한다.
- 필드와 컬럼 매핑:
@Column
,@Temporal
,@Enumerated
,@Lob
,@Transient
- Member 객체의
private String name
항목과 Member 테이블의name
컬럼을 매핑한다.
- Member 객체의
- 기본 키 매핑:
@Id
,@GeneratedValue
- Member 객체의
private Long id
항목과 Member 테이블의pk
를 매핑한다.
- Member 객체의
- 연관관계 매핑:
@ManyToOne
,@OneToMany
,@JoinColumn
- [JPA 기초] 04. 연관관계 매핑에 정리했습니다.
1. 객체와 테이블 매핑
1) @Entity
@Entity
가 붙은 class는 JPA가 관리, Table과 매핑한다.-
기본 생성자가 필수이다.
@Entity public class Member { @Id private Long id; @Column(name = "name") private String username; /* 기본 생성자 */ public Member { } }
final
,enum
,interface
,inner
class 사용 금지name
속성- JPA에서 사용할 Entity 이름을 지정한다.
- 기본 값은 class 이름을 그대로 사용한다.
- 가급적 기본 값을 사용하는 것이 좋다.
2) @Table
2. 필드와 컬럼 매핑
package hellojpa;
import javax.persistence.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
@Entity
public class Member {
@Id
private Long id;
@Column(name = "name")
private String username;
private Integer age;
@Enumerated(EnumType.STRING) // EnumType.ORDINAL -> ENUM 순서가 바뀌면 뒤죽박죽 섞이는 대참사 발생
private RoleType roleType;
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
private LocalDate testLocalDate;
private LocalDateTime testLocalDateTime;
@Lob
private String description;
@Transient
private int temp;
/* Getter & Setter */
}
1) @Column
: column 매핑
unique
- ID가 랜덤으로 할당되어 운영에서는 이슈 트래킹이 불가하므로 잘 사용하지 않는다.
@Table(uniqueConstraints = )
방식을 많이 사용한다.
2) @Enumerated
: enum 타입 매핑
-
EnumType.ORDINAL
사용하면 안된다!!-
처음에 Member 객체의 RoleType enum을 아래와 같이 저장하고,
public enum RoleType { GUEST, ADMIN }
- DB에 RoleType을 저장하면
GUEST=1
,ADMIN=2
로 저장된다. -
그런데, RoleType의 수정이 필요해 다음과 같이 변경한 후,
public enum RoleType { GUEST, USER, ADMIN }
- DB에 RoleType을 저장하면
GUEST=1
,USER=2
,ADMIN=3
으로 저장된다. - 기존에 DB에 저장 된 RoleType=2는 ADMIN을 의미했는데, RoleType의 수정이 발생한 후로는 USER를 의미하게 되는 불상사 발생
- RoleType의 변경이 발생한다고 해도, JPA에서 이를 자동으로 인식하여 DB UPDATE를 해주는 기능은 없으니
EnumType.ORDINAL
은 사용하지 말 것!
-
3) @Temporal
: 날짜 타입 매핑
java.util.Date;
타입일 때 사용한다.java.time.LocalDate, LocalDateTime
을 사용하면 생략 가능하다.
4) @Lob
: BLOB, CLOB 매핑
- 텍스트, 그래픽, 이미지, 비디오, 사운드 등 구조화 되지 않은 대형 데이터를 저장하는 목적.
CLOB
: 필드 타입이 문자일 때 사용한다.BLOB
: 그 외 형태일 때 사용한다.
5) @Transient
: 매핑 무시
3. 기본 키 매핑
- DB Table의
pk
를 Entity 클래스의field
와 매핑하는 방법을 나타낸다. - Entity 클래스의 식별자를 정의하고, DB Table과의 연관성을 확립하는데 중요한 역할을 한다.
- 매핑 방법
- 직접 할당 →
@Id
- 자동 생성 →
@GeneratedValue
- IDENTITY 전략
- SEQUENCE 전략
- TABLE 전략
- AUTO 전략
- 직접 할당 →
1) IDENTITY 전략
/* Member class -> IDENTITY 전략 */
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // 기본 키
private String name;
private int age;
// getters, setters, constructors 등...
}
- 기본 키 생성을 DB에 위임하는 방식이다.
- DB에 위임하였기 때문에, ID에 값을 넣어주면 안된다.
- 대부분의 RDBMS에서 사용된다.
- MySQL: AUTO_ INCREMENT
/* main 함수 */
try {
Member member1 = new Member();
member1.setID("1L")
member1.setUsername("A");
/* @GeneratedValue(strategy=GenerationType.IDENTITY) 일 경우,
JPA가 INSERT SQL 날리는 분기 */
em.persist(member1);
/* 일반적으로, JPA가 INSERT SQL 날리는 분기 */
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
/* Entity Manager 종료 */
em.close();
}
- JPA는 보통
tx.commit
시점에 INSERT SQL을 실행하지만, IDENTITY 전략은 DB에 INSERT SQL를 날리기 전에는 ID를 알 수 없는 문제가 발생한다. - 따라서, IDENTITY 전략일 경우, JPA는
em.persist
시점에 INSERT SQL을 실행하고, 이후에object.getID()
를 통해 ID 호출 가능하도록 문제를 해결하였다.
2) SEQUENCE 전략
/* Member class -> Sequence 전략 */
@Entity
@SequenceGenerator(
name = "member_seq_generator",
sequenceName = "member_seq"
)
public class Member {
@Id
@GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "member_seq_generator"
)
private Long id;
/* Constructor, Getter & Setter ... */
}
- 개념
- DB
Sequence
객체를 사용하여 pk를 생성하는 전략이다. Sequence
는 특정 순서에 따라 일련번호를 생성하는 객체이다.- Oracle Sequence
- Object의 ID를 관리하기 위한 전용 DB 같은 개념이다.
- DB
-
작동 원리
setID()
는 사용하지 않는다.-
em.persist()
단계에서 DB Sequence에서getID()
진행한다.# em.persist() 진행 후 log Hibernate: call next value for MEM_SEQ member.getId() = 2
- 영속성 컨텍스트에 쌓아둔 후
tx.commit()
을 통해 INSERT SQL 날린다.
- 문제점
- DB에 여러 번 접근하면서 네트워크를 여러 번 타게 되어 성능 저하 이슈가 발생할 수 있다.
em.persist()
: ID를 얻기 위해 DB에 접근 1번tx.commit()
: INSERT SQL을 날리기 위해 DB에 접근 1번
- DB에 여러 번 접근하면서 네트워크를 여러 번 타게 되어 성능 저하 이슈가 발생할 수 있다.
- 해결 방안
allocationSize = 50
으로 설정하여 ID를 얻기 위해 DB에 접근하는 횟수를 줄인다.- DB Sequence에는 51을 저장, 나머지 50회는 미리 AP단 메모리에 저장한다.
- ID를 얻기 위해 DB가 아닌 AP단 메모리에 접근하여 ID Increment를 실행한다.
- 50회를 다 채우면 새로이 DB Sequence에 접근하여 101을 저장, 메모리에 추가로 50회를 저장한다.
- 여러 WAS가 있어도 동시성 이슈가 없다.
3) TABLE 전략
/* 키 생성 전용 테이블 생성 */
create table MY_SEQUENCES (
sequence_name varchar(255) not null,
next_val bigint,
primary key ( sequence_name )
)
/* Member class -> TABLE 전략 */
@Entity
@TableGenerator(
name = "MEMBER_SEQ_GENERATOR",
table = "MY_SEQUENCES",
pkColumnValue = "MEMBER_SEQ",
allocationSize = 1
)
public class Member {
@Id
@GeneratedValue(
strategy = GenerationType.TABLE,
generator = "MEMBER_SEQ_GENERATOR"
)
private Long id;
/* Constructor, Getter & Setter ... */
}
- 키 생성 전용 테이블을 하나 만들어서 Sequence 전략을 흉내내는 전략
- Sequence 전략은 Oracle 등 일부 DB에서만 사용 가능하지만, TABLE 전략은 모든 DB에 적용 가능하다.
- 하지만, 성능이 구리다..
4) 정리: 권장하는 식별자 전략
- 기본 키 제약 조건
- NOT NULL
- 유일성
- 불변성 → 이 조건 만족하는 것이 어렵다.
- 미래까지 기본 키 제약 조건을 만족하는 자연키는 찾기 어렵다.
- 국가에서 갑자기 주민등록번호를 사용하지 말라고 했던 사례가 있다..
- Long형 + 대체 키 + 키 생성전략을 사용하자!
- long형
- 범위가 약 -9경부터 9경에 이른다.
- ID 값이 경에 이르기는 쉽지 않으니 long형을 사용하는 것이 적절하다.
- 대체 키
- DB의 pk를 대체할 수 있는 entity 전용 키를 생성하는 것.
- 외부에 노출되지 않고, DB 내부적으로만 사용되어 보안 상 이점이 있다.
- 키 생성전략
- IDENTITY 전략의 AUTO_INCREMENT 방식
- SEQUENCE 전략
- long형
4. DB Schema 자동 생성
<!-- persistence.xml의 일부 -->
<properties>
<property name="hibernate.hbm2ddl.auto" value="create" />
</properties>
- 개요
- DDL(CREATE, ALTER, DROP 등)을 AP 실행 시점에 자동 생성한다.
- DB 방언을 활용해서 DB에 맞는 적절한 DDL 생성한다.
- DDL은 개발 서버에서만 사용하고, 운영 서버에서는 사용하지 않거나 적절히 다듬은 후 사용해야 한다.
- 속성
create
: DROP → CREATEcreate-drop
: DROP → CREATE → DROPupdate
- 변경 분 반영
- 삭제 불가능
- 운영DB 사용 금지
validate
: Entity와 Table이 정상 매핑 되었는 지 확인.none
: 사용 안함
- 장점
- 개발할 때, DB console 가서 schema 조작하는 것이 귀찮다.
- DDL문을 JPA로 해결 가능하여 편리함.
- 개발할 때, DB console 가서 schema 조작하는 것이 귀찮다.
- 주의
- 혼자 사용하는 프로젝트면 사용해도 무관하다.
- 남들이랑 사용할 때 → 웬만하면 사용하지 않는 것이 좋음
- 개발 초기: create, update
- 테스트 서버: update, validate
- 운영 서버: 사용하지 마라
댓글 남기기