자바 ORM 표준 JPA 프로그래밍 정리
Intro
안녕하세요. 이성희 대리입니다.
자바 ORM 표준 JPA 프로그래밍 책을 보고 정리한 내용입니다.
기본키 매핑
직접 할당 : @Id
자동 생성 : @GeneratedValue
- IDENTITY : 데이터베이스에 위임
- Auto_Increment
- SEQUENCE : 데이터베이스 시퀀스 오브젝트 사용
- @SequenceGenerator 필요
- TABLE : 키 생성용 테이블 사용
- @TableGenerator 필요
- AUTO : 방언에 따라 자동 지정, 기본값
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Enumerated
enum(열거형)의 값을 엔티티의 필드 값으로 사용하기 위해 사용
EnumType
- STRING : enum 이름
- ORDINAL : enum 순서
@Enumerated(EnumType.STRING)
private OrderStatus status;
public enum OrderStatus {
ORDER, CANCEL
}
@MappedSuperclass
테이블과 매핑되지 않고 자식 클래스에 엔티티의 매핑 정보를 상속하기 위해 사용
@MappedSupperclass로 지정한 클래스는 엔티티가 아니므로 em.find()나 JPQL에서 사용할 수 없다
@MappedSuperclass
public class BaseEntity {
private Date createdDate;
private Date lastModifiedDate;
@Entity
@Table(name = "ORDERS")
public class Order extends BaseEntity {
@Inheritance
상속관계 매핑 전략
InheritanceType
- SINGLE_TABLE : 단일 테이블 전략으로 테이블 하나만 사용, 구분 컬럼으로 어떤 자식 데이터가 저장되었는지 구분
- 장점 : 조인이 필요 없으므로 일반적으로 조회 성능이 빠르다, 조회 쿼리가 단순하다
- 단점 : 자식 엔티티가 매핑한 컬럼은 모두 null을 허용해야 한다, 테이블이 커질 수 있어서 상황에 따라 조회 성능이 느려질 수 있다
- JOIN : 각각 테이블로 만들고 자식 테이블이 부모 테이블의 기본 키를 받아서 기본 키 + 외래 키로 사용
- 장점 : 테이블이 정규화된다, 저장공간을 효율적으로 사용한다
- 단점 : 조회할 때 조인이 많이 사용되므로 성능이 저하될 수 있다, 조회 쿼리가 복잡하다, 데이터를 등록할 INSERT SQL을 두 번 실행한다
@DiscriminatorColumn
부모 클래스 구분 컬럼 지정, default = DTYPE
@DiscriminatorValue
자식 클래스에 선언한다. 엔티티를 저장할 때 슈퍼타입의 구분 컬럼에 저장할 값을 지정한다. 어노테이션을 선언하지 않을 경우 기본값으로 클래스 이름이 들어간다.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item {
@Id @GeneratedValue
@Column(name = "ITEM_ID")
private Long id;
private String name;
private String price;
@Entity
@DiscriminatorValue("A")
public class Album extends Item {
private String artist;
private String etc;
@Entity
@DiscriminatorValue("B")
public class Book extends Item {
private String author;
private String isbn;
지연로딩, 즉시로딩
지연로딩 : 연관된 엔티티를 실제 사용할 때 데이터베이스를 조회
즉시로딩 : 연관된 엔티티를 즉시 조회, 하이버네이트는 가능하면 SQL 조인을 사용해서 한 번에 조회
기본설정값
- @ManyToOne, @OneToOne : 즉시로딩 (FetchType.EAGER)
- @OneToMany, @ManyToMany : 지연로딩 (FetchType.LAZY)
@Entity
public class Member {
private String name;
@OneToMany(mappedBy = "member", fetch = FetchType.EAGER)
private List<Order> orders = new ArrayList<Order>();
지연로딩
Member.getName();
Hibernate:
select
member0_.MEMBER_ID as MEMBER_I1_2_0_,
member0_.name as name2_2_0_
from
Member member0_
where
member0_.MEMBER_ID=?
Member.getOrders().size();
Hibernate:
select
orders0_.MEMBER_ID as MEMBER_I6_2_0_,
orders0_.ORDER_ID as ORDER_ID1_3_0_,
orders0_.ORDER_ID as ORDER_ID1_3_1_,
orders0_.MEMBER_ID as MEMBER_I6_3_1_
from
ORDERS orders0_
where
orders0_.MEMBER_ID=?
즉시로딩
Member.getName();
Hibernate:
select
member0_.MEMBER_ID as MEMBER_I1_2_0_,
member0_.name as name2_2_0_,
orders1_.MEMBER_ID as MEMBER_I6_2_1_,
orders1_.ORDER_ID as ORDER_ID1_3_1_,
orders1_.ORDER_ID as ORDER_ID1_3_2_,
orders1_.MEMBER_ID as MEMBER_I6_3_2_
from
Member member0_
left outer join
ORDERS orders1_
on member0_.MEMBER_ID=orders1_.MEMBER_ID
where
member0_.MEMBER_ID=?
영속성 전이
JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태여야한다.
영속성 전이를 사용하면 연관된 객체를 함께 저장하거나 삭제 가능하다.
CascadeType.PERSIST : 저장
CascadeType.REMOVE : 삭제
CascadeType.ALL : 모든 작업
CascadeType.MERGE : 병합
CascadeType.REFRESH : 데이터베이스로부터 인스턴스 값 다시 읽어 오기
CascadeType.DETACH : 영속성 컨텍스트에서 엔티티 제거
@Entity
public class Member {
private String name;
@OneToMany(mappedBy = "member", cascade = CascadeType.PERSIST)
private List<Order> orders = new ArrayList<Order>();
Member member = new Member();
member.setName("테스트1");
Order order1 = new Order();
order1.setStatus(OrderStatus.ORDER);
order1.setMember(member);
Order order2 = new Order();
order2.setStatus(OrderStatus.CANCEL);
order2.setMember(member);
// 부모 저장, 연관된 자식들 저장
em.persist(member);
임베디드 타입(복합 값 타입)
@Embeddable : 값 타입을 정의하는 곳에 표시
@Embedded : 값 타입을 사용하는 곳에 표시
@Entity
public class Delivery {
//...
//private String city;
//private String street;
@Embedded
private Address address;
@Embeddable
public class Address {
private String city;
private String street;
JPQL
테이블이 아닌 객체를 대상으로 검색하는 객체지향 쿼리다.
SQL을 추상화해서 특정 데이터베이스 SQL에 의존하지 않는다.
String jpql = "SELECT m from Member as m where m.name = '테스트2'";
List<Member> resultList = em.createQuery(jpql, Member.class).getResultList();
Hibernate:
select
member0_.MEMBER_ID as MEMBER_I1_2_,
member0_.name as name2_2_
from
Member member0_
where
member0_.name='테스트2'
Criteria
프로그래밍 코드로 JPQL을 작성
장점
- 컴파일 시점에 오류를 발견할 수 있다.
- IDE를 사용하면 코드 자동완성을 지원한다.
- 동적 쿼리를 작성하기 편하다.
단점
- 복잡해서 사용하기 불편하고 코드가 한눈에 들어오지 않는다.
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> query = cb.createQuery(Member.class);
Root<Member> rm = query.from(Member.class);
CriteriaQuery<Member> cq = query.select(rm).where(cb.equal(rm.get("name"), "테스트2"));
List<Member> resultList2 = em.createQuery(cq).getResultList();
Hibernate:
select
member0_.MEMBER_ID as MEMBER_I1_2_,
member0_.name as name2_2_
from
Member member0_
where
member0_.name=?
Leave a comment