[JPA] 엔티티 매핑
객체매핑 @Entity
@Entity가 붙은 클래스는 엔티티가 되고 JPA가 관리한다.
JPA를 사용해서 테이블과 매핑할 클래스는 @Entity가 필수이다.
@Entity 속성 : name
@Entity(name = " ")
- JPA에서 사용할 엔티티 이름을 지정하는 것이다.
- 디폴트 값은 현재 클래스 이름 그대로이다.
- 같은 클래스 이름이 없으면 되도록 기본값을 사용하는 것이 좋다.
!!!주의!!!
-
@Entity가 붙은 클래스는 기본 생성자가 필수이다.
-
final 클래스, enum, interface, inner클래스를 사용할 수 없다.
-
저장할 필드에 final을 사용할 수 없다.
테이블매핑 @Table
@Table은 엔티티와 매핑할 테이블을 지정하는 것이다.
@Table 속성 : name, catalog, schema, uniqueConstraints(DDL)
@Table(name = "DB테이블명")
- 데이터베이스 상 테이블이름이 USER이고 코드상으로는 Member를 사용해야한다면, Member 클래스 위에 @Table(name = "DB테이블명") 작성해주면 해당 엔티티는 테이블과 맵핑이 된다.
catalog
- 데이터베이스 catalog를 매핑한다.
schema
- 데이터베이스 schema를 매핑한다.
uniqueConstraints
- DDL 생성 시에 유니크 제약 조건을 생성한다.
데이터베이스 스키마 자동생성
- DDL을 애플리케이션 실행 시점에 자동 생성한다.
- 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL을 생성한다.
- 생성된 DDL은 개발장비에서만 사용하고 운영서버에서는 사용하지 않는다. (적절히 다듬은 후 사용)
<property name="hibernate.hbm2ddl.auto" value="속성값"/> // (persistence.xml)
- create : 기존 테이블 삭제 후 다시 생성한다. (DROP + CREATE)
- create-drop : create와 같으나 종료시점에 테이블을 DROP한다.
- update : 변경된 부분만 반영한다. (운영DB에서는 사용하면 안된다)
- validate : 엔티티와 테이블이 정상매핑되었는지만 확인해준다.
- none : 사용하지 않음
DDL생성기능
제약조건 추가 : 회원이름은 필수, 5자 초과하면 안됨
- @Column(nullable = false, length = 10)
유니크 제약조건 추가
- @Table(uniqueConstraints = {@UniqueConstraint(name = "NAME_AGE_UNIQUE" columnNames = {"NAME", "AGE"})})
유니크 제약조건 : 중복된 값을 허락하지않는다.
필드매핑 @Column
@Column은 필드명과 DB 테이블 컬럼명을 맵핑시켜준다.
@Column(name = "DB컬럼명")
속성 | 설명 | 기본값 |
name | 필드와 매핑할 테이블의 컬럼명을 작성하여 맵핑함. | 객체의 필드 이름 |
insertable, updatable |
등록, 변경 가능 여부 | true |
nullable(DDL) | null 값의 허용 여부를 설정한다. false로 설정하면 DDL 생성 시에 not null 제약조건이 붙는다. | |
unique(DDL) | @Table의 uniqueConstraints와 같지만 한 컬럼에 간단히 유니크 제약조건을 걸 때 사용한다. | |
columnDefinition(DDL) | 데이터베이스 컬럼 정보를 직접 줄 수 있다. | |
length(DDL) | 문자 길이 제약조건, String타입에만 사용한다. | 255 |
precision, scale(DDL) | BigDecimal, BigInteger 타입에서 사용한다. precision은 소수점을 포함한 전체 자릿수를, scale은 소수의 자릿수이다. *double, float타입에는 적용되지않는다. |
presision = 19, scale = 2 |
enum타입 매핑 @Enumerated
자바 enum 타입을 매핑할 때 사용한다.
@Enumerated 속성
@Enumerated(EnumType.STRING)
기본값은 EnumType.ORDINAL이다. 하지만 ORDINAL을 사용해선 안된다. 왜냐하면
예를 들어 enum클래스에 { 강아지, 고양이, 사자 }가 있다고 가정해보자.
지금까지 강아지를 저장하게 되면 0이 저장되어졌을 것이다. 하지만 enum클래스가 { 펭귄, 강아지, 고양이, 사자 } 로 변경된다면 펭귄이 0이 된다. 따라서 무조건 STRING으로 저장되어야한다.
날짜 타입 매핑 @Temporal
날짜타입을 매핑할 때 사용한다.
@Temporal 속성
@Temporal(TemporalType.DATE) // 날짜, 데이터베이스 date타입과 매핑 (2021-03-06)
@Temporal(TemporalType.TIME) // 시간, 데이터베이스 time타입과 매핑 (02:30:11)
@Temporal(TemporalType.TIMESTAMP) // 날짜와 시간, 데이터베이스 timestamp타입과 매핑(2021-03-06 05:10:10)
하지만 최신 하이버네이트는 LocalDate, LocalDateTime을 지원한다. 이 두가지를 사용할 경우에는 생략이 가능하다.
BLOB, CLOB 매핑 @Lob
데이터베이스 BLOB, CLOB 타입과 매핑한다.
매핑하는 필드 타입이 문자면 CLOB 매핑이 되고, 나머지는 BLOB매핑이 된다.
CLOB : String, char[], java.sql.CLOB
BLOB : byte[], java.sql.BLOB
***Lob : LOB는 Text, 그래픽, 이미지, 비디오, 사운드 등 구조화되지 않은 대형 데이터를 저장하는데 사용한다.
일반적으로 테이블에 저장되는 구조화된 데이터들은 크기가 작지만, 멀티미디어 데이터는 크기가 크다.
크기가 큰 데이터는 DB에 저장하기 힘들기 때문에 운영체제에 존재하는 파일을 데이터베이스가 접근하게된다.
1. CLOB : 문자 대형 객체 (Character)
2. BLOB : 이진 대형 객체 (Binary) - 이미지, 동영상, MP3 등
특정 필드를 컬럼에 매핑하지 않음 (매핑 무시) @Transient
필드가 테이블 컬럼과 매핑되지 않고, 데이터베이스에 저장도 조회도 되지 않는다. 메모리상에서만 임시로 값을 보관하고 싶을 때 사용한다.
기본키매핑 @Id
직접할당 : @Id // 내가 직접 id를 셋팅하고 싶은 경우
자동생성 : @GeneratedValue(strategy = "") // 디폴트 GenerationType.AUTO
- IDENTITY전략 : 데이터베이스에 위임, MYSQL
기본 키 생성을 데이터베이스에게 위임한다. 주로 MySQL, postgresql, sql server, db2에서 사용한다.
(MySQL의 AUTO_INCREMENT) // id값을 null로 하면 db가 알아서 AUTO_INCREMENT해준다.
JPA는 트랜잭션 커밋 시점에 insert sql을 실행한다.
AUTO_INCREMENT는 데이터베이스에 insert sql을 실행한 이후에 id값을 알 수 있다.
em.persist() 시점에 즉시 insert sql을 실행하고 db에서 식별자를 조회한다.
**한가지 문제!
IDENTITY전략은 db에 들어가기 전까지 기본키인 id를 모르는데, 기본키가 필수로 필요한 영속성 컨텍스트에 넣을 방법이 없다. IDENTITY전략에서만 예외적으로 persist를 호출하자마자, db에 쿼리를 보낸다.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
- SEQUENCE 전략 : 데이터베이스 시퀀스 오브젝트 사용, ORACLE (@SequenceGenerator가 필요하다)
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
//시퀀스에서 값을 가져온 다음 값을 세팅해서 db에 들어가게 된다. (기본시퀀스)
데이터베이스시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트이다.( 오라클의 시퀀스같음)
오라클, postgresql, db2, h2데이터베이스에서 사용한다.
@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;
// 이렇게 실행을 하게되면 MEMBER_SEQ라는 시퀀스를 생성함
- TABLE 전략 : 키 생성용 테이블 사용, 데이터 베이스 시퀀스를 흉내내는 전략이다. 모든 DB에서 사용한다. (@TableGenerator 필요)
시퀀스 테이블이 생성되고, 시퀀스네임과 NEXT_VAL의 컬럼이 생성된다. //운영에서는 쓰기에는 조금 부담스러움
*** 시퀀스 관련 : persist()할때마다 쿼리를 보내므로 성능이 떨어진다. 이러한 성능 최적화를 위해서는 allocationSize를 사용하면 된다. 테이블의 시퀀스값을 미리 많이 올려놓고, 웹서버에서 그것을 사용함.
연관관계매핑 @ManyToOne, @JoinColumn
권장하는 식별자 전략
- 기본 키 제약 조건 : null이 아니어야한다. 유일해야한다. 변하면 안된다.
- 미래까지 이 기본키 제약 조건을 만족하는 자연키는 찾기 어렵다. 따라서 대체키(비즈니스와 상관없는)를 사용하자!
- 주민등록번호도 기본키로 적절하지 않다. 법이 바뀌어서 주민번호도 저장하면 안되는 등 예상하지 못한 상황이 발생할 수 있다.
- 권장하는 것은 Long형 + 대체키 + 키 생성전략을 사용하는 것이 좋다.