객체지향 쿼리 JPQL과 JPA 매핑 어노테이션
JPQL
JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어를 제공
SQL과 문법 유사, SELECT, FROM, WHERE, GROUP BY, HAVING, JOIN 지원
JPQL은 엔티티 객체를 대상으로 쿼리
SQL은 데이터베이스 테이블을 대상으로 쿼리
//검색
String jpql = "select m From Member m where m.name like '%hello%'";
List<Member> members = em.createQuery(jpql, Member.class).getResultList();
JPA의 모든 데이터변경은 트랜잭션 안에서 실행되어야 한다.
JPA의 매핑 어노테이션
@Column
@Temporal
@Enumerated
@Lob
@Transient
@Column
가장 많이 사용됨
name:필드와 매핑할 테이블의 컬럼 이름
insertable, updatable: 읽기 전용
nullable: null 허용여부 결정, DDL 생성시 사용
unique: 유니크 제약 조건, DDL 생성시 사용
columnDefinition, length, precision, scale (DDL)
@Temporal
날짜 타입 매핑
@Temporal(TemporalType.DATE)
private Date date; //날짜
@Temporal(TemporalType.TIME)
private Date time; //시간
@Temporal(TemporalType.TIMESTAMP)
private Date timestamp; //날짜와 시간
@Enumerated
열거형 매핑
EnymType.ORDINAL: 순서를 저장(기본값)
EnumType.STRING: 열거형 이름을 그대로 저장, 가급적 이걸 사용
@Enumerated(EnumType.STRING)
private RoleType roleType;
@Lob
CLOB, BLOB 매핑
CLOB: String, char[], java.sql.CLOB
BLOB: byte[], java.sql.BLOB
@Lob
private String lobString;
@Lob
private byte[] lobByte;
@Transient
이 필드는 매핑하지 않는다.
애플리케이션에서 DB에 저장하지 않는 필드
식별자 매핑 어노테이션
@Id
@GeneratedValue
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Id(직접 매핑)
@GeneratedValue
기본키를 자동으로 생성해주는 어노테이션 입니다.
GenerationType
IDENTITY: 데이터베이스에 위임, MYSQL
예를 들어 MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성해준다.
IDENTITY 전략은 AUTO_INCREMENT처럼 데이터베이스에 값을 저장하고 나서야 기본 키 값을 구할 수 있을 때 사용한다.
주의점
IDENTITY 전략을 사용하면 식별자를 데이터베이스에서 지정하기 전까지는 알 수 없기 때문에,
em.persist()를 하는 즉시 INSERT SQL이 데이터베이스에 전달됩니다.
따라서 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않습니다.
SEQUENCE: 데이터베이스 시퀀스 오브젝트 사용해서 기본키를 생성, ORACLE
- @SequenceGenerator 필요
@Entity
@SequenceGenerator(
name = "BOARD_SEQ_GENERATOR",
sequenceName = "BOARD_SEQ",
initialValue = 1,
allocationSize = 1
)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "BOARD_SEQ_GENERATOR")
@Column(name = "MEMBER_ID")
private Long id;
}
@SequenceGenerator 속성
속성 | 기능 | 기본값 |
name | 식별자 생성기 이름 | 필수 |
sequenceName | 데이터베이스에 등록되어 있는 시퀀스 이름 | hibernate_sequence |
initalValue | DDL생성 시에만 사용된다. 시퀀스 DDL을 생성할때 처음 시작하는 수를 지정한다. |
1 |
allocationSize | 시퀀스를 한번 호출할 때 증가하는 수 (성능 최적화에 사용된다.) |
50 |
catalog, schema | 식별자 생성기의 catalog, schema 이름 |
IDENTITY와 SEQUENCE 차이점
시퀀스 전략은 em.persist()를 호출할 때 먼저 데이터베이스 시퀀스를 사용해서 식별자를 조회합니다.
그리고 조회한 식별자를 엔티티에 할당한 후, 해당 엔티티를 영속성 컨텍스트에 저장합니다.
이후 트랜잭션 커밋 시점에 플러시가 발생하면 엔티티를 데이터베이스에 저장합니다.
IDENTITY 전략은 먼저 엔티티를 데이터베이스에 저장한 후에 식별자를 조회하여, 엔티티의 식별자에 할당한 후, 영속성 컨텍스트에 저장합니다.
TABLE: 키 생성용 테이블 사용, 모든 DB에서 사용가능(시퀀스를 지원하지않는 DB에서도 가능)
- @TableGenerator 필요
@Entity
@TableGenerator(
name = "BOARD_SEQ_GENERATOR",
table = "MY_SEQUENCE",
pkColumnValue = "BOARD_SEQ",
initialValue = 1,
allocationSize = 50
)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.TABLE,
generator = "BOARD_SEQ_GENERATOR")
@Column(name = "MEMBER_ID")
private Long id;
}
SELECT * FROM MY_SEQUENCE;
pkColumnValue에 입력한 BOARD_SEQ가 조회된다.
@TableGenerator 속성
속성 | 기능 | 기본값 |
name | 식별자 생성기 이름 | 필수 |
table | 키생성 테이블명 | hibernate_sequences |
pkColumnName | 시퀀스 컬럼명 | sequence_name |
valueColumnName | 시퀀스 값 컬럼명 | next_val |
pkColumnValue | 키로 사용할 값 이름 | 엔티티 이름 |
initialValue | 초기 값. 마지막으로 생성된 값이 기준 | 0 |
allocationSize | 시퀀스를 한번 호출할 때 증가하는 수 (성능 최적화에 사용된다.) |
50 |
catalog, schema | 식별자 생성기의 catalog, schema이름 | |
uniqueConstraints (DDL) | 유니크 제약 조건을 지정할 수 있다. |
AUTO: 방언에 따라 자동 지정, 기본값
데이터베이스는 종류도 많고 기본 키를 만드는 방법도 다양합니다.
persistence.xml에서 hibernate.hbm2ddl.auto: create 을 사용중이라면 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택합니다.
@GeneratedValue의 strategy의 기본값은 AUTO입니다.
권장하는 식별자 전략
기본 키 제약 조건
1. null이 아니다.
2. 유일하다.
3. 변하면 안된다.
미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 그 대신 대리키/대체키를 사용하자.
자연키 (Natural Key)
비즈니스적으로 의미가 있는 키
Ex) 전화번호, 주민등록번호 등
대리키/대체키 (Generate Value)
비즈니스적으로 상관없는 키
Ex) Generate Value, 랜덤 값, 유휴 값 등
예를 들어, 주민등록번호도 기본 키로 적절하지 않다.
Why? 갑자기 개인정보 보호의 목적으로 DB에 주민등록번호를 저장하지 말라 조건이 들어온다. 이때, 주민등록번호를 pk로 사용하고 있는 테이블뿐만 아니라 해당 테이블의 pk를 fk로 JOIN하고 있는 다른 테이블에서도 문제가 생긴다.
즉, 주민등록번호를 fk로 참조하고 있는 모든! 테이블을 마이그레이션 해야 한다.
권장하는 식별자 구성 전략
(Long형) + (대체키) + (적절한 키 생성 전략) 사용
Long Type (아래 참고)
대체키 사용: 랜덤 값, 유휴 ID 등 비즈니스와 관계없는 값 사용
AUTO_INCREMENT 또는 Sequnce Object 사용
[참고] Q. id 값(pk 값)은 어떤 타입을 써야 할까?
● int
0이 있다.
● Integer
10억 정도 까지만 가능하다.
● Long
채택!
Long의 크기가 Integer의 2배지만 애플리케이션 전체로 봤을 때의 영향은 작다고 볼 수 있다.
오히려 10억이 넘어갔을 때 해당 id 값을 타입을 변경하는 것이 더 어렵다.