자바 ORM 표준 JPA 프로그래밍 정리

이성희

Updated:

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을 작성

장점

  1. 컴파일 시점에 오류를 발견할 수 있다.
  2. IDE를 사용하면 코드 자동완성을 지원한다.
  3. 동적 쿼리를 작성하기 편하다.

단점

  1. 복잡해서 사용하기 불편하고 코드가 한눈에 들어오지 않는다.
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=?

Tags:

Categories:

Updated:

Leave a comment