김영한 강사님의 '자바 ORM 표준 JPA 프로그래밍-기본편' 강의 정리
2022.03.17~03.31 진행
강의를 듣고 개인적으로 정리한 글입니다. 코드와 그림 출처는 김영한 강사님께 있습니다. 문제 있을 시 알려주세요.
4.1. 객체와 테이블 매핑
@Entity
: 테이블과 매핑할 클래스에 붙인다(필수)
- JPA가 관리
- 속성 name : JPA에서 사용할 엔티티 이름을 지정.
- → 가급적 기본 값(클래스 이름)을 사용하되, 같은 클래스 이름이 있으면 name 지정해 변경
- 기본 생성자 필수 (public 또는 protected 생성자 붙이기)
- final 클래스, enum, interface, inner 클래스 사용X
- 저장할 필드에 final 사용 X
@Table
: 엔티티와 매핑할 테이블 지정
데이터베이스 스키마 자동 생성
JPA는 애플리케이션 실행 시점에, 데이터베이스에 적절한 DDL을 자동 생성해주는 기능을 지원한다(방언 설정 별로 달라진다).
DDL 생성 기능은 JPA의 실행 로직에는 영향을 주지 않는다.
자동생성된 DDL은 개발 시에만 사용하고 운영서버에서는 다듬어서 쓰거나 사용하지 않는다.
적용
<property name="hibernate.hbm2ddl.auto" value="속성값" />
속성
- create : 기존테이블 삭제 후 재생성 (DROP+CREATE)
- create-drop : create와 같으나 종료시점에 테이블 DROP
- update : 변경분만 반영(운영DB에는 사용하면 안됨)
- validate : 엔티티와 테이블이 정상 매핑되었는지 확인
- none : 사용하지 않음
※주의 : 환경에 맞게 속성 사용하기
- 운영 장비 : 절대 create, create-drop, update를 사용하지 말 것
- 개발 초기 단계 : create, update
- 테스트 서버 : update, validate → create 쓰면 테스트 데이터가 다 날아간다..
- 스테이징과 운영 서버 : validate, none
⇒ 스크립트를 가급적이면 alter table 도 내가 직접 스크립트 짠걸.. 테스트서버나 개발서버에도 적용하고 DBA분한테 전달해서 검수를 받고 운영서버에 반영하는 것이 좋다.
제약조건 추가
예) 회원 이름은 필수, 10자 초과X @Column(nullable = false, length = 10)
유니크 제약조건 추가
@Table(uniqueConstraints = {@UniqueConstraint(name = "NAME_AGE_UNIQUE", columnNames = {"NAME", "AGE"} )})
4.2. 필드와 컬럼 매핑
매핑 어노테이션 정리
hibernate.hbm2ddl.auto
어노테이션 | 설명 | 속성, 주의할 점 |
---|---|---|
@Column | 컬럼 매핑 | 따로 정리 |
@Temporal | 날짜 타입 매핑 LocalDate, LocalDateTime을 사용 시 생략 가능 |
[TemporalType 속성] .DATE/TIME/TIMESTAMP |
@Enumerated | 자바 enum 타입 매핑 | [EnumType 속성] .ORDINAL 사용X(default 값, 숫자로 저장) .STRING(문자로 저장)을 사용해야 속성 추가 시 순서가 안밀림 |
@Lob | 데이터베이스 필드 타입인 CLOB(문자일 경우), BLOB(나머지 경우) 와 매핑 |
속성X • CLOB: String, char[], java.sql.CLOB • BLOB: byte[], java.sql. BLOB |
@Transient | 특정 필드를 컬럼에 매핑하지 않음(매핑 무시) 데이터베이스에 저장X, 조회X |
메모리상에서만 임시로 어떤 값을 보관하고 싶을 때 주로 사용 |
@Column
속성 | 설명 | 기본값 |
---|---|---|
name | 필드와 매핑할 테이블의 컬럼 이름 | 객체의 필드 이름 |
insertable, updatable |
등록, 변경 가능 여부 | TRUE |
nullable(DDL) | null 값의 허용 여부를 설정한다. false로 설정하면 DDL 생성 시에 not null 제약조건이 붙는다. |
|
unique(DDL) | 이름을 반영하기 어렵고 한 컬럼에만 가능해서, 실무에서 유니크 제약조건을 걸 때는 @Table의 uniqueConstraints를 주로 사용 |
|
columnDefinition(DDL) | 데이터베이스 컬럼 정보를 직접 줄 수 있다. ex) varchar(100) default ‘EMPTY’ |
필드의 자바 타입과 방언 정보를 사용 |
length(DDL) | 문자 길이 제약조건, String 타입에만 사용 | 255 |
precision, scale(DDL) |
BigDecimal 타입에서 사용(BigInteger도 사용할 수 있다). precision은 소수점을 포함한 전체 자릿수, scale은 소수의 자릿수. (double, float 타입에는 적용되지X. 아주 큰 숫자나 정밀한 소수를 다루어야 할 때만 사용) |
precision=19, scale=2 |
4.3. 기본 키 매핑
기본 키 매핑 방법
@Id @GeneratedValue(strategy = GenerationType.속성)
private Long id;
- 직접 할당:
@Id
만 사용 - 자동 생성(
@GeneratedValue
) 속성- IDENTITY: 기본 키 생성을 데이터베이스에 위임
- 주로 MySQL(AUTO_INCREMENT), PostgreSQL, SQL Server, DB2에서 사용
- SEQUENCE: 데이터베이스 시퀀스 오브젝트 사용
- ORACLE, PostgreSQL, DB2, H2 에서 사용
@SequenceGenerator
필요
- TABLE: 키 생성 전용 테이블을 생성해서 사용(DB 시퀀스를 흉내내는 전략), 모든 DB에서 사용
- 실무에서는 거의 사용하지 X,
@TableGenerator
필요
- 실무에서는 거의 사용하지 X,
- AUTO: 방언에 따라 자동 지정, 기본값
- IDENTITY: 기본 키 생성을 데이터베이스에 위임
4.3.1. IDENTITY 전략 - 특징
JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL을 실행하는데,
AUTO_INCREMENT를 사용하면 데이터베이스가 기본키를 만들어주니까 데이터베이스에 INSERT SQL을 실행한 이후에야 ID 값을 알 수 있다.
그런데 영속성 컨텍스트에서 관리하려면 pk값이 필수이다.
그래서 IDENTITY 전략은 em.persist()
시점에 즉시 INSERT SQL을 실행하고, DB에서 식별자를 조회 한다(JDBC가 반환값을 받을 수 있도록 설계되어있음).
⇒ 따라서 버퍼링해서 insert 하는게 IDENTITY 전략에서는 불가능하다.
4.3.2. SEQUENCE 전략 - 특징
데이터베이스 시퀀스란 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트이다.
사용방법
@Entity
@SequenceGenerator(
name = “MEMBER_SEQ_GENERATOR",
sequenceName = “MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
initialValue = 1, allocationSize = 1)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MEMBER_SEQ_GENERATOR")
private Long id;
@SequenceGenerator
속성 | 설명 | 기본값 |
---|---|---|
name | 식별자 생성기 이름 | 필수 |
sequenceName | 데이터베이스에 등록되어 있는 시퀀스 이름 | hibernate_sequence |
initialValue | DDL 생성 시에만 사용됨, 시퀀스 DDL을 생성할 때 처음 1 시작하는 수를 지정 | 1 |
allocationSize | 시퀀스 한 번 호출에 증가하는 수(성능 최적화에 사용됨) 데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정 |
50 (주의!!) |
catalog, schema | 데이터베이스 catalog, schema 이름 |
동작방식
- DB에서 시퀀스 값과 pk값을 얻고
- 영속성 컨텍스트에 쿼리가 쌓여있다가
- 실제 트랜잭션이 커밋되는 시점에 insert 쿼리가 날아간다(버퍼링 가능)
try{
Member member = new Member();
member.setName("C");
System.out.println("==================");
em.persist(member); //member 저장
System.out.println("member.getId() = " + member.getId());
System.out.println("==================");
tx.commit();
} catch(Exception e){
최적화
그런데 시퀀스를 호출 할 때마다(객체를 저장할 때마다) 쿼리를 날리면 성능이 좋지 않을 것이다.
→ allocationSize를 늘려서 해결 (default값은 50이다. 50~100정도가 적정)
allocationSize = 50 일 경우
- 최초 객체 저장시 시퀀스를 1~50까지 확보해둔다.
- allocationSize값이 51이 되는 순간 next call이 호출되고, DB에 쿼리를 날려 시퀀스를 100으로 확보한다.
미리 값을 올려두는 방식이라, 웹 서버 여러 대가 동시에 호출하더라도 문제없다.
예를 들어 처음 서버A가 next call을 호출하면 1~50의 값은 서버A가 사용하고, 서버B가 next call을 호출하면 51~100의 값을 사용한다.
권장하는 식별자 전략
- 기본 키 제약 조건: Not null, 유일해야 함, 변하면 안된다.
- 미래까지 이 조건을 만족하는 자연키는 찾기 어렵다.
=> Long형 + 대체키(시퀀스,uuid) + 키 생성전략(auto_increment)을 사용하자
'Web > JPA' 카테고리의 다른 글
[Inflearn] JPA - 값 타입(작성전) (0) | 2022.05.11 |
---|---|
[Inflearn] JPA - 객체지향 쿼리 언어 (0) | 2022.03.31 |
[Inflearn] JPA - 영속성관리 (0) | 2022.03.22 |
[Inflearn] JPA 시작하기 + JPQL이란? (0) | 2022.03.21 |
[Inflearn] ORM, JPA란 무엇인가 (0) | 2022.03.21 |