JPA(Java Persistence API), Hibernate 란?
JPA란?
- 자바 ORM 기술에 대한 표준 명세로, Java에서 제공하는 API 이다.
- 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스이다.
JPA는 인터페이스이며, 특정 기능을 하는 라이브러리가 아니라는 것을 꼭 알아둬야 할 것 같다.
여기서 ORM이란 또 무엇인가..?
ORM(Object-Relation Mapping)은 쉽게 말해서 객체와 테이블을 연결해주는 작업이다.
JPA의 장점으로는
- 개발자가 비즈니스 로직에 집중 할 수 있으며, 객체지향 개발이 가능하다. 즉 빠른 개발이 가능하다
- 테이블 생성, 변경, 관리가 쉽다.
- 로직을 쿼리에 집중하기 보다는 객체자체에 집중 할 수 있다.
단점으로는
- 효율적으로 쉽게 사용하기엔 어렵고 배워야할 것이 많다.
- 잘 이해하고 있지 않으면 성능상 문제가 있을 수 있다.
JPA 동작과정
JPA는 애플리케이션과 JDBC 사이에서 동작한다.
개발자가 JPA를 사용하게 되면, JPA내부에서 JDBC API를 이용하여 DB와 통신한다.
결국 JPA를 사용한다고 JDBC를 사용을 안하는 것이 아니다.
그렇다면 왜 JPA를 써야하는지에 대한 의문이 생길 것이다. 물론 장점은 있지만 뭔가 크게 와닿지 않는 것 같다.
왜 JPA를 사용하는가 ?
Java는 객체지향관점의 원어이고, 데이터베이스는 보통 관계형 데이터베스를 사용한다.
객체지향관점을, 관계형 데이터베이스의 적용함에 있어 다소 이질감이 발생한다.
예를들어 자바에서는 상속의 개념을 가지고 있지만 데이터베이스는 그렇지않다.
하지만 많은 개발자들이 이런 이질감을 극복하기 위해 노력해왔고 이에 JPA가 탄생한 것이다.
실무적인 관점에서 JPA를 사용하는 큰 이유는 생산성이다.
장점에서 설명했듯이 SQL 쿼리를 JPA가 대신해준다. 따라서 생산성이 올라가는 것이고, DB구조가 변경되어 필드 하나를 추가해야 된다고 가정했을 때 MyBatis라면, 잘짜여진 SQL을 수정하는 과정을 반복하며, 많은 시간을 사용할 것이다.
하지만 JPA를 사용하면 객체 필드 하나만 추가하면 되는 것이다.
하이버네이트(Hibernate)란?
객체관계맵핑 프레임워크 중에 하나이면서 JPA라는 명세의 구현체이다.
즉, JPA와 하이버네이트는 마치 자바의 인터페이스와 해당 인터페이스를 구현한 클래스와 같은 관계이다.
위 사진은 JPA와 하이버네이트의 상속 및 구현 관계를 나타낸 것이다.
JPA의 핵심인 EntityManagerFactory, EntityManager, EntityTrasaction을 하이버네이트에서는 각각 SessionFactory, Session, Transaction으로 상속받고 각각 Impl로 구현하고 있음을 확인할 수 있다.
JPA를 사용하기 위해서 반드시 Hibernate를 사용할 필요가 없다.
JPA에서 중요한 개념이 하나 빠졌는데 영속성이다.
JPA에서 중요한 두가지가 있는데
하나는 객체와 관계형 데이터베이스 매핑하는것이 있고, 나머지 하나가 영속성 컨텍스트이다.
영속성 컨텍스트란 엔티티를 영구 저장하는 환경 이다.
애플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의 데이터베이스 같은 역할을 한다. 엔티티 매니저를 통해 엔티티를 저장하거나 조회하면 엔티티 매니저는 영속성 컨텍스트에 엔티티를 보관하고 관리한다.
em.persist(member);
- 엔티티 매니저를 사용해 회원 엔티티를 영속성 컨텍스트에 저장한다는 의미!
영속성 컨텍스트의 특징
- 엔티티 매니저를 생성할 때 하나 만들어진다.
- 엔티티 매니저를 통해서 영속성 컨텍스트에 접근하고 관리할 수 있다.
엔티티의 생명주기
- 비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태
- 영속(managed): 영속성 컨텍스트에 저장된 상태
- 준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태
- 삭제(removed): 삭제된 상태
비영속
엔티티 객체를 생성했지만 아직 영속성 컨텍스트에 저장하지 않은 상태를 비영속(new/transient)라 한다.
Member member = new Member();
영속
엔티티 매니저를 통해서 엔티티를 영속성 컨텍스트에 저장한 상태를 말하며 영속성 컨텍스트에 의해 관리된다는 뜻이다.
em.persist(member);
준영속
영속성 컨텍스트가 관리하던 영속 상태의 엔티티 더이상 관리하지 않으면 준영속 상태가 된다.
특정 엔티티를 준영속 상태로 만드려면 em.datach()를 호출하면 된다.
영속성 컨텍스트와 데이터베이스 저장
JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 데이터 베이스에 반영하는데 이를 flush라 한다.
영속성 컨텍스트가 엔티티를 관리하면 다음과 같은 장점이 있다.
- 1차 캐시
- 동일성 보장
- 트랙잭션을 지원하는 쓰기 지연
- 변경 감지
- 지연 로딩
1차 캐시
영속성 컨텍스트 내부에는 캐시가 있는데 이를 1차 캐시라고 한다. 영속 상태의 엔티티를 이곳에 저장한다. 1차 캐시의 키는 식별자 값(데이터베이스의 기본 키)이고 값은 엔티티 인스턴스이다. 조회하는 방법은 다음과 같다.
//em.find(엔티티 클래스 타입, 식별자 값);
Member member = em.find(Member.class, "member1");
조회의 흐름
1. 1차 캐시에서 엔티티를 찾는다
2. 있으면 메모리에 있는 1차 캐시에서 엔티티를 조회한다.
3. 없으면 데이터베이스에서 조회한다.
4. 조회한 데이터로 엔티티를 생성해 1차 캐시에 저장한다. (엔티티를 영속상태로 만든다)
5. 조회한 엔티티를 반환한다.
영속 엔티티의 동일성 보장
영속성 컨텍스트는 엔티티의 동일성을 보장한다.
트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
엔티티 매니저는 트랜잭션을 커밋하기 직전까지 내부 쿼리 저장소에 INSERT SQL을 모아둔다. 그리고 트랜잭션을 커밋할 때 모아둔 쿼리를 DB에 보낸다. 이것을 트랜잭션을 지원하는 쓰기 지연이라 한다.
변경 감지
JPA로 엔티티를 수정할 때는 단순히 엔티티를 조회해서 데이터를 변경하면 된다.
변경감지의 흐름
1. 트랙잭션을 커밋하면 엔티티 매니저 내부에서 먼저 플러시가 호출된다.
2. 엔티티와 스냅샷을 비교하여 변경된 엔티티를 찾는다.
3. 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소에 저장한다.
4. 쓰기 지연 저장소의 SQL을 플러시한다.
5. 데이터베이스 트랜잭션을 커밋한다.
참조
suhwan.dev/2019/02/24/jpa-vs-hibernate-vs-spring-data-jpa/
velog.io/@neptunes032/JPA-%EC%98%81%EC%86%8D%EC%84%B1-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%9E%80