김영한님의 자바 ORM 표준 JPA 프로그래밍 강의를 수강하면서 정리했습니다.
SQL 중심 개발의 문제점
- 패러다임의 불일치
- 객체 -> SQL 변환 -> SQL -> RDB
- 진정한 의미의 계층 분할이 어렵다.
- 객체답게 모델링 할수록 매핑 작업만 늘어난다.
- 객체를 자바 컬렉션에 저장하듯이 DB에 저장할 수 없을까? 의 고민의 해결 방안이 JPA 다.
JPA
- Java Persistence API
- 자바 진영의 ORM 기술 표준
- ORM
- Object-relational mapping(객체 관계 매핑)
- 객체는 객체대로 설계
- 관계형 데이터베이스는 관계형 데이터베이스대로 설계
- ORM 프레임워크가 중간에서 매핑
- JPA는 애플리케이션과 JDBC 사이에서 동작
- JPA는 인터페이스의 모음이다.
- JPA 2.1 표준 명세를 구현한 3가지 구현체
- 하이버네이트, EclipseLink, DataNucleus
JPA 동작 순서
유저 회원가입 상황이라면?
- JPA 에게 유저 정보를 넘긴다.
- JPA가 해당 엔티티를 분석하고,
- JPA가 INSERT SQL 생성하고,
- JPA가 JDBC API 사용해서 DB에 넘긴다.
패러다임의 불일치를 해결해준다.
JPA를 왜 사용해야 할까?
- SQL 중심적인 개발에서 객체 중심으로 개발
- 생산성
- 유지보수
- 패러다임 불일치 해결
- 성능
- 데이터 접근 추상화와 벤더 독립성
- 표준
ORM은 객체와 RDB 두 기둥 위에 있는 기술이다.
JPA 구동 방식
- JPA에는
Persistence
라는 클래스가 있다. 여기가 시작점이다.
- 그리고 설정 정보를 META-INF/persistence.xml 에서 읽어서
EntityManagerFacotry
라는 클래스를 생성한다.
- 필요할 때마다
EntityManagerFacotry
에서 EntityManager 를 찍어낸다.
주의
- 엔티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에서 공유
- 엔티티 매니저는 쓰레드 간에 공유X
- JPA의 모든 데이터 변경은 트랜잭션 안에서 실행
영속성 컨텍스트
JPA에서 가장 중요한 2가지
- 객체와 관계형 데이터베이스 매핑하기
- 영속성 컨텍스트
- "엔티티를 영구 저장하는 환경" 이라는 뜻
EntityManager.persist(entity)
- 영속성 컨텍스트는 논리적인 개념
- 엔티티 매니저를 통해서 영속성 컨텍스트에 접근
엔티티의 생명주기
- 비영속(new/transient)
- 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
- 영속(managed)
- 준영속(detached)
- 삭제(removed)
영속성 컨텍스트의 이점
- 1차 캐시
- 영속 엔티티의 동일성 보장
- 엔티티를 등록할 때 트랜잭션을 지원하는 쓰기 지연
- 변경 감지(Dirty Checking)
플러시
- 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하는 것
플러시가 발생하면?
- 변경 감지
- 수정된 엔티티를 쓰기 지연 SQL 저장소에 등록
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송
영속성 컨텍스트를 플러시하는 방법
em.flush()
- 트랜젝션 커밋
- JPQL 쿼리 실행
플러시 정리
- 영속성 컨텍스트를 비우지 않음
- 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화
- 트랜젝션이라는 작업 단위가 중요
준영속 상태
- 영속 -> 준영속
- 영속 상태의 엔티티가 영속성 컨텍스트에서 분리
- 영속성 컨텍스트가 제공하는 기능을 사용 못함
객체와 테이블 매핑
@Entity
- @Entity 가 붙은 클래스는 JPA가 관리
- JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수
- 기본 생성자 필수
- 파라미터가 없는 public 또는 protected 생성자
- final 클래스, enum, interface, inner 클래스 사용 X
- 저장할 필드에 final 사용 X
기본 키 매핑
- 직접 할당
- 자동 생성
@GeneratedValue
만 사용
- IDENTITY
- 기본키 생성을 데이터베이스에 위임
- 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용
- ex) MySQL의 AUTO_INCREMENT
- JPA 는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행
- AUTO_INCREMENT 는 데이터베이스에 INSERT SQL을 실행한 이후에 ID값을 알 수 있음
- 즉 디비에 값이 들어가야 ID값을 알 수 있다는 문제가 있다. 그게 무슨 문제냐면 영속성 컨텍스트에서 관리되려면 PK값이 있어야 하는데 DB에 넣기 전까지 영속성 컨텍스트의 1차캐시 부분에서 @Id 값을 알 수가 없다는 문제가 있다.
- 위 문제를 해결하기 위해 IDENTITY 전략만 em.persist() 시점에 즉시 INSERT SQL 실행하고 DB에서 식별자를 조회함.
- SEQUENCE
- 데이터베이스 시퀀스 오브젝트 사용
- ORACLE
@SequenceGenerator
필요
- TABLE
- 키 생성용 테이블 사용
- 모든 DB에서 사용
- 단점으로 성능 이슈가 있다.
@TableGenerator
필요
- AUTO
권장하는 식별자 전략
- 기본 키 제약 조건
- null 아님
- 유일
- 변하면 안된다.
- 미래까지 변하지 않는 조건을 만족하는 자연키를 찾기 어려움(-> 그래서 대리키를 사용하자)
- 권장: Long 타입 + 대체키 + 키 생성전략 사용
- 10억이 넘어도 정상적으로 동작해야하기 때문에 Long 타입 사용
데이터 중심 설계의 문제점
@Entity
public class Order {
@Column(name = "ORDER_ID")
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
** @Column(name = "MEMBER_ID")
private Long memberId;**
- 객체 설계를 테이블 설계에 맞춘 방식
- 테이블의 외래키를 객체에 그대로 가져옴
- 객체 그래프 탐색이 불가능
- 참조가 없으므로 UML도 잘못됨