목차
- 객체와 테이블 매핑 : @Entity, @Table
- 기본 키 매핑 : @Id
- 필드와 컬럼 매핑 : @Column
- 연관관계 매핑 : @ManyToOne, @JoinColumn
객체와 테이블 매핑
@Entity
- @Entity가 붙은 클래스는 JPA가 관리, 엔티티라 한다.
- JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수이다.
- 주의
- 기본 생성자 필수(파라미터가 없는 public 또는 protected 생성자)
- final 클래스, enum, interface, inner 클래스 --> 사용x
- 저장할 필드에 final 사용 x
@Table, enumtype
- @Table은 엔티티와 매핑할 테이블을 지정한다. 생략하면 매핑한 엔티티 이름을 테이블 이름으로 사용한다.
- createdDate, lastModifiedDate : 자바의 날짜 타입은 @Temporal을 사용해서 매핑한다.
- roleType : 자바의 enum을 사용하려면 @Enumerated 어노테이션으로 매핑해야한다.
- 회원을 설명하는 필드는 길이 제한이 없다. 따라서 데이터 베이스의 VARCHAR 타입 대신 CLOB 타입으로 저장해야 한다. @Lob을 사용하면 CLOB, BLOB 타입을 매핑할 수 있다.
package jpabook.start;
import javax.persistence.*;
import java.util.Date;
@Entity
@Table (name="MEMBER")
public class Member {
@Id
@Column (name = "ID")
private String id;
@Column (name - "NAME")
private String username;
private Integer age;
//== 추가 ==
@Enumerated (EnumType. STRING)
private RoleType roleType; // 1
@Temporal (TemporalType. TIMESTAMP)
private Date createdDate; // 2
@Temporal (TemporalType. TIMESTAMP)
private Date lastModifiedDate; // 2
@Lob
private String description; //3
}
//Getter, Setter
package jpabook.start;
public enum RoleType {
ADMIN, USER
}
DDL 생성 기능
- 제약조건 추가 : 회원 이름 필수, 10자 초과 X
@Column(nullable = false, length =10)
- 유니크 제약조건 추가
@Table(uniqeConstraints = (@UniqueConstraint(name = "NAME_AGE_UNIQUE", columnNames ={"NAME", "AGE"})))
- DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.
@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) | 소수의 자릿수 설정 (기본 값 : precision=19) |
@Enumerated
- 자바 enum 타입을 매핑 할 때 사용
- 주의 ORDINAL 사용 X
- EnumType.ORDINAL : enum 순서를 데이터 베이스에 저장
- enum의 순서대로 0,1,2,3... 저장 된다.(Integer값 저장)
- 따라서 엄청난 혼란을 줄 수 있다. 예를 들면 ADMIN이 0번 이었는데, GUEST가 0번으로 바뀌면 이전에 저장 되어있던 0번 값(=ADMIN)과 새로 저장된 값 GUEST(=0번 값)이 헷갈리게 된다.
- enum의 순서대로 0,1,2,3... 저장 된다.(Integer값 저장)
- EnumType.STRING : enum 이름을 데이터베이스에 저장 (*항상 이렇게 사용)
- enum 설정 값 이름 그대로를 저장한다.
- EnumType.ORDINAL : enum 순서를 데이터 베이스에 저장
@Temporal
날짜 타입(java.util.Date, java.util.Calendar) 매핑할 때 사용한다.
- 참고 : LocalDate, LocalDateTime을 사용할 때는 생략 가능(최신 하이버네이트 지원)
- TemporalType.DATE : 날짜, 데이터베이스, date 타입과 매핑 (예 : 2013-10-11)
- TemporalType.TIME : 시간, 데이터베이스, time 타입과 매핑 (예 : 11:11:11)
- TemporalType.TIMESTAMP : 날짜와 시간, 데이터베이스, timestamp 타입과 매핑(예 : 2013-10-11 11:11:11)
아래와 같이 적으면 어노테이션이 필요없다!
private LocalDate localDate;
private LocalDateTime localDateTime;
@Lob
데이터베이스 BLOB, CLOB 타입과 매핑
@Lob에는 지정할 수 있는 속성이 없고, 대신에 매핑하는 필드 타입이 문자면, CLOB으로 매핑하고 나머지는 BLOB로 매핑한다.
@Transient
객체 임시로 어떤 값을 넣고 싶을 때 사용하고 데이터베이스에는 반영이 안된다.
데이터 베이스 스키마 자동 생성
사실상 운영에선 사용하진 않고, 개인적으로 개발할때 정도 사용한다.
- DDL을 애플리케이션 실행 시점에 자동 생성
- 테이블 중심 -> 객체 중심
- 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL 생성
xml 다음과 같이 입력하면 초기 실행 시 자동으로 테이블을 생성한다.
<property name="hibernate.hbm2ddl.auto" value ="create"/>
위의 value 속성은 개발 단계마다 다르게 생성할 수있다.
- 개발 초기 단계에는 create 또는 update
- 테스트 서버는 update 또는 validate(엔티티와 테이블이 정상 매핑되었는지만 확인)
- 스테이징과 운영서버는 validate 또는 none
- 운영 장비에는 절대 create, create-drop, update 사용하면 안된다.
기본 키 매핑
기본 키 매핑 어노테이션
- @Id
- @GeneratedValue
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
기본 키 매핑 방법
- 직접 할당 : @Id만 사용
- 자동 생성(@GeneratedValue)
- IDENTITY : 데이터베이스에 위임(DB에 따라서 알아서(임의로) 해줌), MYSQL, PostgreSQL, SQL Server, DB2에서 사용
- MySQL의 AUTO_INCREMENT
- SEQUENCE : 데이터베이스 시퀀스 오브젝트 사용, ORACLE
- @SequenceGenerator 필요
- TABLE : 키 생성용 테이블 사용, 모든 DB에서 사용
- @TableGenerator 필요
- IDENTITY : 데이터베이스에 위임(DB에 따라서 알아서(임의로) 해줌), MYSQL, PostgreSQL, SQL Server, DB2에서 사용
기본 키 직접 할당 전략
@Id 적용 가능 자바 타입
- 자바 기본형
- 자바 래퍼wrapper형
- String
- java.util.Date
- java.sql.Date
- java.math.BigDecimal
- java.math.BigInteger
sequenceName 속성의 이름으로 BOARD_SEQ 를 지정했는데 JPA는 이 시퀸스 생성기를 실제 데이터베이스 BOARD_SEQ 시퀀스와 매핑한다. sequenceName을 따로 설정하지 않으면 hibernate_sequence와 같이 자동으로 설정된다.
@Entity
@SequenceGenerator(
name = "BOARD_SEQ_GENERATOR".
sequenceName = ”BOARD_SEQ”, //매핑할 데이터베이스 시퀀스 이름
initialvalue = 1,
allocationsize = 1)
public class Board {
@IdQGeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "BOARD_SEQ_GENERATOR")
private Long id;
...
}
call next value for MEMBER_SEQ란?
영속성 컨텍스트에 의해 JPA는 항상 PK값을 알아야한다. 그래서 MEMBER_SEQ값에서 id값을 조회한다. 그러고 나서 em.persist를 해준다.
commit 하는 시점에 insert쿼리가 날라간다.
SquenceGenerator.allocationSize의 기본값이 50인 이유는 최적화 때문이다 allocationSize 값이 50이면 시퀀스를 한 번에 50 증가 시킨 다음에 1~50까지는 메모리에서 식별자를 할당한다. 이 최적화 방법은 시퀀스 값을 선점 하므로 여러 JVM이 동시에 동작 해도 기본 키 값이 충돌하지 않는 장점이 있다. 반면에 데이터베이스에 직접 접근해서 데이터를 등록할 때 시퀀스 값이 한번에 많이 증가한다는 점을 염두해 두어야 한다. 참고로 앞서 설명한 hibernate.id.new_generator_mappings 속성을 true로 설정해야 지금까지 설명한 최적화 방법이 적용된다.
TABLE 전략(운영에서 잘 쓰이지 않음)
- 장점 : 모든 데이터베이스에 적용 가능
- 단점 : 성능
@Entity
@TableGenerator(
name = "BOARD_SEQ_GENERATOR",
table = ”MY_SEQUENCES",
pkColumnValue = ”BOARD_SEQ”,
allocationsize = 1)
public class Board {
@Id
@GeneratedValue(
strategy = GenerationType.TABLE,
generator = '' BOARD_SEQ_GENERATOR''
)
private Long id;
...
}
TABLE 전략과 최적화 TABLE 전략은 값을 조회하면서 SELECT 쿼리를 사용하고 다음 값으로 증가시키기 위해 UPDATE 쿼리를 사용한다. 이 전략은 SEQUENCE 전략과 비교해서 데이터베이스와 한번 더 통신하는 단점이 있다. TABLE 전략을 최적화하려면 TableGenerator.allocationSize를 사용하면 된다.
권장하는 식별자 전략
- 기본 키 제약 조건 : null 아님, 변하면 안된다.
- 미래까지 이 조건에 만족하는 자연키는 찾기 어렵다. 대리키(대체키)를 사용하자.
- 예를 들어 주민등록번호도 기본 키로 적절하지 않다.
- 권장 : Long형 + 대체키 + 키 생성전략 사용
AUTO 전략
GenerationType.AUTO는 선택한 데이터베이스 방언에 따라 IDENTITY, SEQUENCE, TABLE 전략 중 하나를 자동으로 선택한다. AUTO 전략의 장점은 데이터베이스를 변경해도 코드를 수정할 필요가 없다는 것이다. AUTO를 사용할 때 SEQUENCE나 TABLE 전략이 선택되면 시퀀스나 키 생성용 테이블을 미리 만들어 두어야 한다.
IDENTITY 전략
기본 키 생성을 데이터베이스에 위임
IDENTITY 전략은 지금 설명한 AUTO INCREMENT를 사용한 예제처럼 데이터베이스에 값을 저장하고 나서야 기본 키 값을 구할 수 있을 때 사용한다.
private static void logic (EntityManager em) {
Board board = new Board(); em.persist (board);
System.out.println("board.id = " + board.getId());
}
//출력: board.id = 1
문제점 : IDENTITY 전략은 데이터를 데이터베이스에 INSERT한 후에 기본 키 값을 조회할 수 있다.
티티가 영속 상태가 되려면 식별자가 반드시 필요하다. 그런데 IDENTITY 식별자 생성 전략은 엔티티를 데이터베이스에 저장해야 식별자를 구할 수 있으므로 em.persist()를 호출하는 즉시 INSERT SQL 이 데이터베이스에 전달된다. 따라서 이 전략은 트랜잭션을 지원하는 쓰기 지연이 동작하지 않는다. 왜냐하면 JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행합니다. 그리고 AUTO_INCREMENT는 데이터베이스에 INSERT SQL을 실행한 이후에 ID 값을 알 수 있습니다.
'Back-end > JPA' 카테고리의 다른 글
객체지향 쿼리 언어1 - 기본 문법 (0) | 2022.09.24 |
---|---|
연관관계 매핑 기초 (0) | 2022.08.22 |
[JPA] JPA 영속성 컨텍스트 (0) | 2022.05.05 |
[JPA] Batch Insert (0) | 2022.05.05 |
JPA 소개 (0) | 2022.03.14 |