JPA (Java Persistence API)
※ persistence - 영속성
자바 진영의 ORM 기술 표준으로 인터페이스의 모음(즉, 실제로 동작하는 것이 아님),
JPA 인터페이스를 구현한 대표적인 오픈소스가 Hibernate.
ORM(Object Relational Mapping) - 객체 관계 매핑
객체와 데이터베이스의 관계를 매핑
객체는 객체대로 설계하고, 관계형 데이터베이스는 관계형 데이터베이스대로 설계
MVC 패턴에서 모델을 기술하는 도구, 객체와 모델 사이의 관계를 기술하는 도구
JPA는 ORM 프레임워크 중 하나
- 사용하는 이유 : 데이터베이스 접근을 프로그래밍 언어의 관점에서 맞출 수 있고, 객체 간의 관계를 바탕으로 SQL을 자동으로 생성하여 불일치를 해결함, SQL 문을 직접 작성하지 않고 엔티티를 객체로 표현 가능 등
Hibernate
ORM 프레임워크 (open source SW)
’Gavin King’과 시러스 테크놀로지스 출신 동료들이 EJB2 스타일의 Entity Beans 이용을 대체할 목적으로 개발
EJB
과거의 자바 표준( Entity Bean ), 과거의 ORM
단점 : 코드가 매우 지저분, API 복잡성이 높다(인터페이스를 많이 구현해야 함), 속도 느림
등장 배경
- 객체와 관계형 데이터베이스 간의 패러다임 불일치
- 객체 모델과 관계형 데이터베이스 모델은 지향하는 목적이 상이함
- 객체 지향 언어의 추상화, 상속, 다형성 등의 개념이 RDB에는 없음
- RDB는 데이터 중심으로 구조화 되어 있음
- 객체 지향 중심으로 설계할수록 이 패러다임의 간극 극대화
- 객체 모델링보다 SQL에 의존적인 데이터 중심의 모델로 변화
- 객체 모델과 관계형 데이터베이스 모델은 지향하는 목적이 상이함
- 이러한 패러다임 불일치를 해결하고자 등장
객체 지향 프로그래밍의 장점
소프트웨어의 생산성 향상. → 상속을 통해 소프트웨어의 재사용이 용이하고, 캡슐화를 통해 유지보수가 쉽다.
대형 프로젝트에 적합 → 클래스 단위로 모듈화 개발로 업무 분담
실세계에 대한 쉬운 모델링. 소프트웨어에 요구되는 사항들을 객체들의 상호작용으로 묘사할 수 있다.
보안성 향상. 캡슐화 특징으로 실제 구현되는 부분을 외부에 드러나지 않도록 하여 정보를 은닉할 수 있다.
JPA를 사용하는 이유
1. SQL이 아닌 객체 중심으로 개발
2. 생산성
◎ 반복적인 CRUD의 자동화 (Creat, Read or Retrieve, Update, Delete or Destroy)
저장 : jpa.persist(member)
조회: Member member = jpa.find(memberId)
수정: member.setName("변경할 이름")
삭제: jpa.remove(member)
◎ 페이징 기능 지원
◎ DB 수정이 간편
3. 유지보수
3. 애플리케이션과 데이터베이스 간의 성능 향상
4. 객체와 RDB 간의 패러다임의 불일치 해결
엔티티 매니저 팩토리와 엔티티 매니저
※ Entity : database와 table과 매핑되는 객체
엔티티 매니저 = 엔티티를 저장하는 가상의 데이터베이스
- 테이블과 매핑한 엔티티를 관리(저장, 수정, 삭제, 조회)하는 관리자
- 엔티티 매니저는 필요할 때마다 엔티티 매니저 팩토리에서 생성한다. → 비용이 거의 들지 않음
- 여러 스레드가 동시에 접근하면 동시성 문제가 발생하기 때문에, 스레드 간에 절대 공유되어서는 안 됨
- 데이터베이스와 연결이 필요한 시점(ex. 트랜잭션을 시작할 때)에 커넥션을 얻어서 사용
엔티티 매니저 팩토리
- 엔티티 매니저를 만드는 공장
- 엔티티 매니저 팩토리는 하나만 생성하여 애플리케이션 전체에서 공유함. → 공장 만들기, 비용이 많이 발생함
영속성 컨텍스트
- Persistence Context = 엔티티를 영구 저장하는 환경
- 엔티티 매니저로 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리.
- 영속성 컨텍스트는 엔티티 매니저를 생성할 때 만들어짐
- 영속성 컨텍스트에 저장하는 것을 영속화
엔티티 생명주기
- 비영속 : 영속성 컨텍스트와 전혀 관계가 없는 상태
- 영속 : 영속성 컨텍스트에 저장된 상태
- 준영속 : 영속성 컨텍스트에 저장되었다가 분리된 상태
- 삭제 : 삭제된 상태
영속성 컨텍스트의 이점
- 1차 캐시
- 엔티티를 저장(영속화)하면 1차 캐시에 저장된다
- 엔티티를 조회 시 1차 캐시에서 우선 찾는다.
- 1차 캐시에 없으면 DB를 조회하고, 1차 캐시에 저장한다.
- 조회 시 성능상의 이점
- 영속 엔티티의 동일성 보장
- 변경 감지
- dirty checking : 엔티티가 변경되면 변경사항을 데이터베이스에 자동으로 반영한다. (jpa에는 update 메소드가 없음)
- 엔티티를 영속성 컨텍스트에 저장할 때 현재 상태를 복사 = 스냅샷
- flush 시점에 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾음
- 변경된 엔티티가 있으면 수정 쿼리를 생성하여 쓰기 지연 저장소에 저장
- 쓰기 지연 저장소의 SQL을 데이터베이스에 플러시하고 트랜잭션을 커밋
※ 변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에서만 적용됨.
비영속, 준영속 상태에서는 아무리 엔티티 값을 변경해도 자동으로 반영되지 않음
주의 사항
트랜잭션
- service 단에서 같은 트랜잭션으로 묶어줘야 함 (영속성 컨텍스트가 동작하는 기준은 트랜잭션이 일어난 시점)
- 트랜잭션이 다르면 다른 영속성 컨텍스트를 사용
- 참고 - 선언적 트랜잭션(@Transactional)을 사용하는 경우에 대한 예시
- service class 상단에 읽기용 transeactional을 공통으로 선언 → 등록이나 수정이 일어나는 메소드에 @Transactional을 새로 부여하여 엔티티에 대한 변경이 가능하도록 함
연관관계 설정
- @ManyToOne, @OneToOne인 XToOne 관계에서 패치 타입 설정은 LAZY(지연 로딩)로 해야 함. (default : EAGER = 즉시로딩)
- EAGER로 되어 있을 시 성능 문제가 발생하는 주요 원인이 될 수 있음
- N + 1의 문제 발생
- EAGER는 즉시 로딩으로 연관관계 엔티티까지 함께 로딩함
- 이때 '조회하는 쿼리' + '조회한 엔티티의 개수(N)' 만큼 연관 관계를 조회하는 쿼리가 동시에 실행됨
- 조회 성능에 매우 치명적
- N + 1의 문제 발생
- EAGER로 되어 있을 시 성능 문제가 발생하는 주요 원인이 될 수 있음
- LAZY로 설정해도 연관 엔티티의 하위 범위까지 탐색하게 되면 N + 1의 문제가 발생
- 이를 해결하기 위해서는 fetch join을 사용
OSIV (Open Session In View)
- 하이버네이트에서 사용하는 용어, JPA에서는 Open EntityManager In View라고 하는 게 맞지만 관례상 OSIV라고함
- 직역 - view에서 session을 열어 놓는다 = jpa에서 영속성 컨텍스트를 데이터베이스 커넥션 시작 시점부터 api response를 보낼 때까지 유지하는 것
- 장점 : 뷰단에서 지연로딩을 사용하여 엔티티를 조회할 수 있음
- 단점 : 데이터베이스 커넥션 유지 시간이 길기 때문에 트래픽이 많은 서비스에서는 커넥션이 모자랄 수 있음
- 기본 설정 true, 실시간 트래픽이 중요한 어플리케이션이라면 false로 바꾸는 것을 권장
- 컨트롤러에서는 영속성 컨텍스트를 사용할 수 없기 때문에, 컨트롤러에 지연로딩을 사용하는 코드가 있다면 Lazy Exception
- service 또는 repository 계층에서 지연로딩 등 뷰에 필요한 엔티티를 미리 로딩해 두는 방법으로 수정 필요