JPA 사용하는데 가장 중요한 것은 엔티티와 테이블을 정확하게 맵핑하는 것, 이는 매핑 어노테이션을 어느 정도 숙지가 필요하다는 거다.
- 객체와 테이블 맵핑 : @Entity, @Table
- 기본키 맵핑 : @Id
- 필드와 컨럼 맵핑 : @Column
- 연관관계 맵핑 : @ManyToOne, @OneToMany, @OneToOne, @JoinColumn
객체와 테이블 맵핑
@Entity
JPA 를 이용해서 테이블을 맵핑할 클래스에는 필수로 넣어야하는 애노테이션.
속성값 name을 지정하지 않은 클래스명을 그대로 사용한다. JPA가 엔티티 객체를 생성할 때 기본 생성자를 사용하기 때문에 기본 생성자는 반드시 있어야 한다.
* 자바는 생성자가 아예 없는 경우 자동으로 기본 생성자를 만드느데 만일 다른 생성자가 있다면 기본 생성자를 직접 만들어 주어야 한다.
@Table
엔티티와 맵핑 될 테이블을 지정하기 위한 애노테이션.
JPA 는 DB스키마 자동 생성 기능을 지원한다. 앞서서 @Entity @Table 과 같은 맵핑 정보를 읽고 스키마를 생성하는데 운영환경이 아닌 테스트 또는 개발 환경에서 사용을 권장한다 (DDL 을 완벽하게 제공하지 않을 수도 있기 때문에)
기본키 맵핑
@Id : 테이블의 기본키 pk 를 엔티티의 식별자 값으로 맵핑하는 애노테이션
@GeneratedValue기본키 값의 자동 생성 전략을 선택 할 수 있다. 만일 직접 기본키를 생성하는 경우엔 해당 애노테이션은 붙여주지 않아도 된다. 그렇다면 왜 굳이 자동 생성 전략을 선택 할 수 있게하느냐하면 DB 벤더 사 마다 지원하는 방식이 다르기 때문에 DB 벤더 사에 맞는 전략을 선택할 수 있도록 다양한 전략이 생기게 된 것이다.
* @Id 로 지정된 필드에만 사용가능한 것은 아니나, 기본키 이외의 컬럼에 다가 추가하여도 자동으로 값 증가를 해주지는 않는 듯함(실제 테스트 한 경우 그러했음)
- IDENTITY 전략
기본 키 생성을 DB에 위임하는 전략. Mysql 에서 auto_increment 와 같이 실제 DB 에 데이터가 insert 될 때 기본 키 값을 얻을 수 있다.
위의 이유로 실제 JPA 가 이 전략을 사용하면 기본 키 값을 얻기 위해 DB를 추가로 조회하게 되는데, JPA 내부에서 DB에 한 번 접근하여 insert 와 생생성된 기본 키값을 받을 수 있도록 JDBC3 에서 만든 api를 사용하여 최적화를 하였다고 한다. 엔티티는 식별자가 필수이기 때문에 이 전략을 선택하는 경우 em.persist() 호출 즉시, insert 쿼리가 DB 에 날라가게 된다. (트랜잭션을 지원하는 쓰기 지연이 동작하지 않음)
[내부 동작]
엔티티 생성 -> em.persist() -> insert 쿼리 DB에 전달 -> 식별자 조회 후 엔티티에 할당 -> 영속성 컨텍스트에 저장 -> 영속 상태의 엔티티 반환
사용 가능한 DB : Mysql
- SEQUENCE 전략
DB 시퀀스는 유일한 값을 순서대로 생성하는 특별한 오브젝트이다. 이 시퀀스를 통해 기본키를 생성하는 전략이 바로 SEQUENCE 전략이다. 추가적으로 시퀀스 정보가 필요하기 때문에 @SequenceGenerator 를 사용하여 시퀀스 맵핑이 필요.
SEQUENCE 전략을 사용하게되면 IDENTITY 전략을 사용하는 것과 내부 동작 방식이 다르다. em.persist() 호출 시 DB 시퀀스를 사용해 식별자 조회 후 엔티티에 할당하고 그 다음에 영속성 컨텍스트에 저장한다.
IDENTITY 전략의 경우에는 persist() 시에 DB에 바로 저장이 되었으나, SEQUENCE 전략의 경우 트랜잭션 커밋 후 플러시가 일어나면 엔티티가 DB에 저장된다.
[내부 동작]
엔티티 생성 -> em.persist() -> 시퀀스에게서 식별자 조회 후 엔티티에 할당 -> 영속성 컨텍스트에 저장 -> 영속 상태의 엔티티 반환
* @SequenceGenerator 는 시퀀스 호출 시 증가하는 값이 50이 기본값으로 설정되어있기 때문에 1씩 증가하도록 만들었다면 이 속성도 반드시 1로 설정해 주어야 한다. 이게 50 이 기본값인 이유는 최적화 이슈로 그러한 것인데,,,, 대충 설명하면 미리 50 까지의 식별자값은 땡겨 받고 내부 엔티티에서 땡겨받은 식별자 다쓰고 또 시퀀스에 한번에 땡겨받고 하는 식으로 DB에 접근하는 횟수를 줄이고자 이러한 방식이 사용될 수 있다고 한다.
사용 가능한 DB : Oracle
- TABLE 전략
키 생성 전용 테이블을 하나 생성하고, 여기에 이름과 값으로 사용할 컬럼을 만들어 사용하는 전략이다. 생성된 테이블을 마치 DB 시퀀스 처럼 동작하도록 하는 전략이라고 할 수 있다. 이 전략은 Table을 사용하기 때문에 모든 DB에 적용이 가능한 전략이다.
* 참고로 키생성 용도로 사용할 테이블은 직접 만들어야 한다
동작 방식 자체가 비슷하다 보니 아무래도 내부적인 동작 방식도 SEQUERNCE 전략과 거의 동일하나, TABLE 전략의 경우 기본 키값으 update 해줘야해서 1번 더 DB 와 통신해야하는 단점이 있다. 이것 역시 SEQUENCE 전략과 동일한 방식으로 최적화가 가능하다.
- AUTO 전략
선택한 DB의 dialect 에 따라 위의 3가지 전략 중 하나를 자동으로 선택 해주는 전략이다.
필드와 컬럼 맵핑
@Column
필드-컬럼을 맵핑하는 애노테이션으로 생략이 가능하며 생략 시에는 자바 기본 타입(int, float, boolean 등) 은 null 이 들어갈 수 없기 때문에 자동으로 not null 로 컬럼이 생성되고, Integer, String 과 같은 객체 타입의 경우 null 이 들어 갈 수 있기 때문에 nullable 한 컬럼으로 생성된다. 또한 @Column의 nullable 속성의 기본값은 true 이기 때문에 기본 타입에 추가하는 경우 nullable 하게 컬럼이 만들어 진다.
@Enumerated : 자바의 enum 타입을 맵핑할 때 사용
enum UserType {
ADMIN, GUEST
}
- @Enumerated(EnumType.STRING) 을 사용하는 경우
enum 의 name 값이 그래도 DB에 저장된다. enum 순서가 변경이 되어도 상관이 없다. 상대적으로 저장된 데이터의 크기가 큰편.
- @Enumerated(EnumType.ORDINAL) 을 사용하는 경우
enum 의 순서가 DB 에 저장된다 (ADMIN : 0, GUEST : 1) 이렇게 되면 enum 의 순서 변경이 불가능하다. 저장되는 데이터의 크기가 작다.
@Temporal : 날짜 타입을 맵핑할 때 사용
@Lob : BLOB, CLOB 타입 매핑
@Transient : 이 필드는 따로 컬럼에 맵핑하지 않고 객체에 임시로 어떤 값을 보관하고 싶은 경우에 사용한다. 당연한 소리지만 DB 에 저장도 조회도 안된다.
@Access
JPA 가 엔티티에 접근하는 방식을 지정하는 애노테이션. 필드에 직접 접근할 지, 접근자(getter)를 사용하여 접근할 지 선택 할 수 있다. 이 애노테이션이 생략된 경우에는 @Id의 위치를 보고 자동으로 판단하여 설정된다
- @Access(AccessType.FEILD) : 필드에 직접 접근하며 private 여도 접근이 가능하다.
- @Access(AccessType.PROPERTY) : 접근자 사용
//FEILD 방식
public class Member {
@Id
private String id;
...
}
//PROPERTY 방식
public class Member {
private String id;
...
@Id
public String getId() { return this.id; }
}
물론 @Access 는 식별자가 아닌 다른 필드에도 설정이 가능하기 때문에 아래의 코드처럼 하나의 엔티티에 Feild 방식와 property 방식을 혼용하여 사용도 가능하다.
class Member {
@Id
private String id;
@Column
private int age;
@Transient
private String firstName;
@Transient
private String lastName;
....
@Access(AccessType.PROPERTY)
public String getName() { return firstName + lastName; }
}
'스터디 > JPA' 카테고리의 다른 글
[JPA] 객체지향쿼리 언어 (0) | 2023.11.06 |
---|---|
[JPA] 데이터 타입 (1) | 2023.11.03 |
[JPA] 연관 관계 매핑 - 심화편 (0) | 2023.10.29 |
[JPA] 연관 관계 매핑 (1) | 2023.10.29 |
[JPA] 기본 개념 및 영속성 컨텍스트 (0) | 2023.10.27 |