이 글은 김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편 강의를 듣고 정리한 글입니다.
DDL(Data Definition Language)
: 데이터 정의어
데이터베이스를 정의하는 언어. 데이터를 생성, 수정, 삭제하는 등의 데이터 전체의 골격을 결정하는 역할을 하는 언어.
(ex. CREATE, ALTER, DROP, TRUNCATE, ...)DML(Data Manipulation Language)
: 데이터 조작어. 정의된 데이터베이스에 입력된 레코드를 조회, 수정, 삭제하는 등의 역할을 하는 언어. 저장된 데이터를 실질적으로 처리하는데 사용한다.
(ex. SELECT, INSERT, UPDATE, DELETE, ...)DCL(Data Control Language)
: 데이터베이스에 접근하거나 객체에 권한을 주는 등의 역할을 하는 언어.
(ex. GRANT, ROVOKE, COMMIT, ROLLBACK, ...)
ex) H2 DB
ex) Oracle DB
<property name="hibernate.hbm2ddl.auto" value="create" />
가급적이면 쓰지 않을 것을 권함. 테스트 서버와 스테이징에서 validate정도는 괜찮은 것 같음. 운영같은 경우에 alter 쿼리가 잘못 날라가면 시스템이 중단 상태가 될 수 있기 때문에, 로컬 환경에서만 사용하고, 테스트 서버나 운영 서버에서는 스크립트를 직접 짜는 것을 권함.
@Column(nullable = false, length = 10)
@Table(uniqueConstraints = {@UniqueConstraint( name = "NAME_AGE_UNIQUE", columnNames = {"NAME", "AGE"} )})
@Entity
public class Member {
@Id
private Long id;
@Column(name = "name") // DB의 Column명은 name
private String username;
private Integer age;
@Enumerated(EnumType.STRING)
private RoleType roleType;
@Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
@Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
@Lob
private String description;
@Transient
private int temp;
public Member() {}
// Getter, Setter, ...
}
@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 타입에는 적용되지 않는다. 아주 큰 숫자나 정밀한 소수를 다루어야 할 때만 사용한다. | recision=19, scale=2 |
@Temporal
: 날짜 타입(java.util.Date, java.util.Calender) 매핑
참고: LocalDate, LocalDateTime을 사용할 때는 생략 가능
@Temporal(TemporalType.TIMESTAMP) private Date createdDate; @Temporal(TemporalType.TIMESTAMP) private Date lastModifiedDate; private LocalDate testLocalDate; private LocalDateTime testLocalDateTime;
@TemporalType
: DATE(날짜), TIME(시간), TIMESTAMP(날짜+시간)자바에서는 Date 데이터타입에 날짜, 시간이 포함되어 있지만, DB에는 3가지로 구분되어 있기 때문에 매핑 정보가 필요함.
@Enumerated
: enum 타입 매핑 (DB에는 Enum 타입이 없음)
속성 | 설명 | 기본값 |
---|---|---|
value | - EnumType.ORDINAL: enum 순서를 데이터베이스에 저장 - EnumType.STRING: enum 이름을 데이터베이스에 저장 | EnumType.ORDINAL |
주의! ORDINAL 사용 X
기본적으로 Integer 타입으로 생성됨
-> RoleType에 타입이 추가됐을 경우,
=> EnumType.STRING 필수!
@Lob
: CLOB(문자), BLOB(문자 외) 매핑, varchar을 넘어서는 큰 컨텐츠
@Transient
: 틀정 필드를 칼럼에 매핑하지 않음 (매핑 무시)
@Id
@GeneratedValue
@Id
만 사용@GeneratedValue
IDENTITY : 기본 키 생성을 데이터베이스에 위임
주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//...
}
JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행한다.
AUTO_INCREMENT는 데이터베이스에 INSERT SQL을 실행한 이후에 ID 값을 알 수 있다. → IDENTITY 전략은em.persist()
시점에 즉시 INSERT SQL 실행한다. → JPA가 DB에서 식별자를 조회한 후, 영속성 컨텍스트의 PK 값으로 쓰게 됨.
⇒ 즉, 모아서 INSERT하는것이 불가능 (일반적으로 성능에 크게 차이가 있지 않음)// code Member member = new Member(); member.setUsername("C"); System.out.println("============================"); em.persist(member); // 즉시 INSERT SQL 실행 System.out.println("member.id = " + member.getId()); System.out.println("============================"); tx.commit();
SEQUENCE : 데이터베이스 시퀀스 오브젝트(유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트) 사용
@SequenceGenerator
필요속성 | 설명 | 기본값 |
---|---|---|
name | 식별자 생성기 이름 | 필수 |
sequenceName | 데이터베이스에 등록되어 있는 시퀀스 이름 | hibernate_sequence |
initialValue | DDL 생성 시에만 사용됨, 시퀀스 DDL을 생성할 때 처음 1 시작하는 수를 지정한다. | 1 |
allocationSize | 시퀀스 한 번 호출에 증가하는 수 (성능 최적화에 사용됨) 데이터베이스 시퀀스 값이 하나씩 증가하도록 설정되어 있으면 이 값을 반드시 1로 설정해야 한다 | 50 |
catalog, schema | 데이터베이스 catalog, schema 이름 |
DB에서 시퀀스 값을 가져온 후, member의 ID에 값을 넣어주고 영속성 컨텍스트에 저장한다. 실제 트랜잭션을
commit()
하는 시점에 INSERT 쿼리를 호출하게 된다.
성능문제? ⇒ allocationSize 설정: 미리 50개를 증가시켜놓고, 웹 서버에서 50개를 씀.
ex) allocationSize = 50// code Member member1 = new Member(); member1.setUsername("A"); Member member2 = new Member(); member2.setUsername("B"); Member member3 = new Member(); member3.setUsername("C"); System.out.println("============================"); // DB MEMBER_SEQ : -49 em.persist(member1); // DB MEMBER_SEQ : 1->51(2번 호출) | APP 1 em.persist(member2); // APP 2 (DB 부르지 않고, MEM에서 호출) em.persist(member3); // APP 3 (DB 부르지 않고, MEM에서 호출) System.out.println("member.id = " + member1.getId()); System.out.println("member.id = " + member2.getId()); System.out.println("member.id = " + member3.getId()); System.out.println("============================");
TABLE : 키 생성용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내, 모든 DB에서 사용 가능하지만 성능 문제가 있다.
@TableGenerator
필요
속성 | 설명 | 기본값 |
---|---|---|
name | 식별자 생성기 이름 | 필수 |
table | 키생성 테이블명 | hibernate_sequence |
pkColumnName | 시퀀스 컬럼명 | sequence_name |
valueColumnName | 시퀀스 값 컬럼명 | next_val |
pkColumnValue | 키로 사용할 값 이름 | 엔티티 이름 |
initialValue | 초기 값, 마지막으로 생성된 값이 기준이다 | 1 |
allocationSize | 시퀀스 한 번 호출에 증가하는 수 (성능 최적화에 사용됨) | 50 |
catalog, schema | 데이터베이스 catalog, schema 이름 | |
uniqueConstraints(DDL) | 유니크 제약 조건을 지정할 수 있다. |
@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;
// ...
}
AUTO: 방언에 따라 자동 지정, 기본값