엔티티 매핑
객체 - 테이블
@Entity
- @Entity가 붙은 클래스는 JPA가 관리한다. 따라서 JPA를 사용할 테이블과 매핑할 클래스는 필수로 사용해야 한다.
- 기본 생성자 필수 (public, protected)
- final(필드, 클래스), enum, interface, inner class X
name
- JPA에서 사용하고 구분할 엔티티 이름
- 가급적으로 기본값을 사용한다 (클래스 이름)
@Table
name
catalog
schema
uniqueConstraints(DDL)
데이터베이스 스키마 자동 생성
- DDL을 애플리케이션 실행 시점에 자동 생성
- 테이블 중심이 아닌 객체 중심
- 생성된 DDL은 운영서버가 아닌 개발서버에서 사용
- 데이터베이스에 맞는 적절한 DDL 생성
오라클의 경우 varchar2, mysql은 varchar
속성
hibernate.hdm2ddl.auto
- create : 기존테이블 삭제 후 다시 생성 (DROP + CREATE)
- create-drop : create와 같지만 종료시점에 테이블 DROP (Test Case 사용시)
- update : 변경된 부분만 반영 (운영DB 사용X)
- validate : 엔티티와 테이블이 정상 매핑되었는지만 확인
- none : 기능 사용 안 함
주의
운영 장비에는 절대 create, create-drop, update 사용 금지, 보통 WAS 계정을 나누어 운영
- 개발 초기 단계는 create, update
- 테스트 서버는 update, validate (여러 명이 사용하는 서버)
- 스테이징과 운영 서버는 validate, none
DDL 생성 기능
제약조건 추가 - @Column
@Column(nullable = false, length = 10)
회원 이름 필수, 10자 이하
유니크 제약조건 추가
@Table(uniqueConstraints = {@UniqueConstraint(name = "NAME_AGE_UNIQUE", columnNames = {"NAME", "AGE"})})
DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직(런타임)에는 영향을 주지 않는다.
필드 - 컬럼
private Integer age;
DB에서 Integer와 맞는 적절한 타입 설정
@Column
name
@Column(name = "name")
private String username;
DB컬럼명은 name, 변수명은 username
- 필드와 매핑할 테이블의 컬럼 이름 (기본값 : 객체의 필드 이름)
insertable, updatable
nullable
- null 값의 허용 여부 설정. false로 설정하면 DDL 생성 시에 not null 제약조건이 붙는다.
unique
- @Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약 조건을 걸 때 사용
- 제약조건 이름이 랜덤으로 나오므로 알아보기 힘들기 때문에 보통 @Table을 사용한다.
columnDefinition
- 데이터 베이스 컬럼 정보를 직접 줄 수 있다.
varchar(100) default 'EMPTY'
와 같이 직접 작성 가능
precision, scale
- BigDecimal, BigInteger에 사용
- precision : 소수점을 포함한 전체 자릿수
- scale : 소수외 자리수
- double, float 적용 안 됨
@Enumerated
@Enumerated(EnumType.STRING)
private ROleType roleType;
DB에서는 ENUM 타입이 존재하지 않기 때문에 선언해주어야 함
value
- ORDINAL : enum 순서(0부터) 저장 -> enum이 수정되어 순서가 바뀌면 뒤죽박죽 돼서 절대 쓰지 말것
- STRING : enum 이름 저장
@Temporal
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
JAVA의 datetime은 날짜+시간, DB는 날짜, 시간, 날짜+시간이기 때문에 매핑 정보를 주어야 함
- 날짜 타입을 매핑할 때 사용
- 현재는 아래와 같이 데이터타입만으로 가능해짐
private LocalDate testLocalDate;
private LocalDateTime testLocalDateTime;
- localDate : 년, 월
- localDateTime : 년, 월, 일
@Lob
@Lob
private String description;
- 큰 내용을 넣을 때
- DB의 BLOB, CLOB 타입과 매핑
- 지정할 수 있는 속성이 없다.
- CLOB : String, char[]
- BLOB : byte[]
@Transient
@Transient
private int temp;
- 컬럼에 생성하지 않으려고 할 때, DB와 매핑하지 않을 때
기본 키 매핑
@Id
@Id
private String id;
@GeneratedValue
@ID
@GeneratedValue
private Long id;
IDENTITY 전략
strategy = GenerationType.IDENTITY
- 기본 키 생성을 DB에 위임
- 주로 MySQL(AUTO_INCREMENT), PostgreSQL, SQL Server 등에서 사용
- id에 값을 넣지 않고 DB에 insert
- DB에 들어왔을 때 id값이 생성 - 이 때 id값을 알 수 있음
- 영속성 컨텍스트에 넣으려면 pk 값이 있어야하는데, DB에 들어가야 알 수 있는 문제 발생
- 이러한 문제를 해결하기 위해 울며 겨자먹기.. 로 트랜잭션이 커밋되는 시점이 아닌 그 전에 바로 쿼리를 실행하여 id 값을 만들고 가져온다.
- 한 트랜잭션 안에서 이와 같은 쿼리를 많이 날려도 성능에 차이가 별로 없다.
SEQUENCE 전략
strategy = GenerationType.SEQUENCE
@SequenceGenerator(name = "CUSTOM_SEQ_GENERATOR")
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
}
- 시퀀스 또한 DB에 넣을 때 값을 알 수 있어서 가져와야한다.
- 영속성 컨텍스트에 넣으려면 pk가 필요해서 시퀀스를 가져와야 한다.
- 시퀀스 테이블에서 해당 시퀀스의 현재 값을 보고 다음 값을 가져온다.
- 시퀀스 테이블에 이전값이 있기 때문에 쿼리를 먼저 실행할 필요 없이 시퀀스 테이블만 참조하면 된다.
- 테이블을 여러번 조회해야 하기 때문에 성능이 안 좋을 수 있는데, allocationSize 속성을 통해 미리 시퀀스를 가져오며 해결할 수 있다.
TABLE 전략
- 키 생성 전용 테이블을 하나 만들어서 DB 시퀀스를 흉내내는 전략
- 장점 : 모든 DB에 적용 가능
- 단점 : 테이블을 직접 사용하여 락이 걸리거나 최적화가 되어있지 않기 때문에 성능이 안 좋음
create Table MY_SEQUENCES (
sequence_name varchar(255) not null,
next_val bigint,
primary key (sequence_name)
)
@Entity
@TableGenerator(
name = "MEMBER_SEQ_GENERATOR",
table = "MY_SEQUENCES",
pkColumnValue = "MEMBER_SEQ", allocationSize = 1)
public class Member {}
권장 식별자 전략
- 기본 키는 not null, 변하면 안 된다.
- 미래까지 이 조건을 만족하는 자연키는 찾기 힘들다. 대리키(대체키)를 사용한다. ex) 주민번호도 기본 키로 적절하지 않음
- Long 타입 + 대체키 + 키 생성전략
- 비즈니스를 키로 가져오는 것은 권장하지 않는다.