들어가기에 앞서...
해당 포스팅은 JPA에 대해 두서없이 간략하게 정리한 내용입니다.
그렇기에 간략히 "이런 흐름인가 보다~" 정도로만 봐주시면 감사하겠습니다.
혹여나 틀린 내용이 있다면 댓글로 알려주세요
JPA란?
JPA(Jakarta Persistence API)란 Java 진영에서 제공하는 ORM(Object Relational Mapping) 기술
간단하게 말하자면 Java를 사용하면서 데이터를 RDB에 어떻게 관리할 것인가?에 대한 API
Jakarta EE application programming interface specification that describes the management of relational data in enterprise Java applications. - Jakarta Persistence Wikipedia
JPA 동작 구성
JPA의 동작은 크게 EntityManagerFactory를 통해 생성된 EntityManager를 사용하여 트랜잭션을 생성 후(EntityTransaction) 해당 트랜잭션 내에서 로직을 수행하게 된다. (조회의 경우 트랜잭션 없이 수행 가능)
참고로 Entity란 JPA에서 DB의 테이블과 매핑되는 클래스를 의미한다. 즉, Entity 객체는 DB로 관리하고자 하는 데이터가 된다.
위 동작에서 나온 EntityManagerFactory, EntityManager, EntityTransaction에 대해 간단하게 정리하자면
- EntityManagerFactory : 단어 그대로 EntityManager를 생성하는 공장이라고 생각하면 편하다.
- EntityManager : Entity를 관리한다 -> DB로 관리하고자 하는 데이터를 제어한다. (자세한 내용은 아래에서 정리)
- EntityTransaction : Transaction에 대한 처리 (트랜잭션 시작, 커밋, 롤백 등)
JPA의 목적을 떠올려 본다면 데이터를 어떻게 DB에서 처리할 것인가?라고 볼 수 있다.
이 목적을 기준으로 위 정리를 보자면 가장 중요한 부분은 EntityManager라고 볼 수 있음을 알 수 있을 것이다.
EntityManager 동작
EntityManager는 사실 Entity를 관리한다는 표현보다는 영속성 컨텍스트(Persistence Context)를 관리한다고 표현하는 것이 좀 더 정확하다고 볼 수 있다.
이는 JPA가 Entity를 관리할 때 영속성 컨텍스트를 통해 관리하기 때문이며, 실제 EntityManager의 동작 또한 영속성 컨텍스트에 Entity를 저장하거나 조회하는 요청을 보내는 것이기 때문
따라서 다시 한번 JPA의 목적을 떠올리면서 생각을 해보면 JPA에서 가장 중요하다고 볼 수 있는 부분은 Entity를 실제로 저장하고 조회할 수 있도록 관리하는 영속성 컨텍스트임을 알 수 있다.
영속성 컨텍스트(Persistence Context)
JPA에서 Entity에 대한 처리를 할 때 내부적으로 Entity 정보를 기록하고 있는 영역이 있으며, 이를 영속성 컨텍스트라고 한다.
쉽게 설명하자면 JPA가 Map collection을 내부적으로 갖고 있으며 이를 이용하여 Entity 정보를 기록하고 있는데 이때 사용되는 Map collection이 영속성 컨텍스트라고 이해하면 쉽게 이해할 수 있다. (쉬운 이해를 위한 설명이며 실제로는 더 복잡하다)
그 이유는 Map에서 Key 값으로 Value를 조회하는 것처럼 Entity의 ID 값(PK 값)으로 Entity를 조회를 하기 때문
// Entity class
@Entity
public class Item {
@Id
@GeneratedValue
private Long id;
private String name;
public Item(String name) {
this.name = name;
}
}
----
// Logic
public class Logic {
public void logic() {
// init & logic 생략
// 예외 처리 생략
tx.begin(); // 트랜잭션 시작
// Entity 생성
Item item = new Item("ItemA");
em.persist(item); // 영속성 컨텍스트에 등록
em.find(Item.class, 1L); // 영속성 컨텍스트에서 Id 값이 1인 Item entity 조회
// 만약 영속성 컨텍스트에서 찾을 수 없다면 DB에서 조회 => select 쿼리 발생
tx.commit(); // 트랜잭션 종료 + 영속성 컨텍스트 종료 => DB에 쿼리 발생 (insert, update, delete)
}
}
여기서 가장 중요한 점은 JPA는 Entity 조회 시 항상 영속성 컨텍스트에서 먼저 조회를 한다는 것과 Map과 동일하게 1개의 key(Id 값)에 1개의 value(Entity)가 존재한다는 것 (자세히는 다를 수 있으나 쉬운 이해 관점에서)
이렇게 2가지의 내용을 숙지한다면 Proxy에 대한 조회 동작에 대해 이해하기 쉬워진다. (Proxy(아직 정리를 하지 않아 링크가 없습니다)와 로딩을 참고)
즉, Entity 조회는 Id를 기준으로 조회를 하는데 우선적으로 영속성 컨텍스트에서 조회를 하게된다.
그럼 이후 동작은 자연스럽게 영속성 컨텍스트에 존재하는 경우 vs 존재하지 않는 경우로 나뉘게 될텐데 대략적으로 다음과 같이 동작한다고 보면 된다. (DB에는 데이터가 존재한다고 가정)
- 영속성 컨텍스트에 존재하는 경우 : 조회된 Entity를 반환
- 영속성 컨텍스트에 존재하지 않는 경우 : 조회 요청한 ID 값을 Key 값으로 설정 후 아래와 같이 동작 후 반환
- Entity 조회의 경우 : DB에서 데이터를 조회하여 Value에 저장
- Proxy 조회의 경우 : Proxy 객체를 생성하여 Vlaue에 저장
여기서 헷갈릴 수 있는 부분은 "영속성 컨텍스트에 Proxy 객체가 저장된 상태에서 Proxy 객체를 조회하여 초기화가 된 후 영속성 컨텍스트에서 해당 객체를 조회한다면 해당 객체는 Entity 객체일까 Proxy 객체일까?"와 이와 정반대인 "Entity 조회 후 Proxy 객체로 조회된 경우"에 대한 내용이다.
결론부터 말하자면 처음 영속성 컨텍스트에 저장된 객체가 조회되어 반환된다.
그렇기에 위에서 영속성 컨텍스트를 쉽게 이해하기 위해 Map이라고 생각하면 된다고 했던 이유가 이 때문이다.
영속성 컨텍스트 생명주기(Persistence Context Lifecycle)
영속성 컨텍스트는 기본적으로 트랜잭션 시작과 함께 생성되고 트랜잭션 종료와 함께 제거된다.
일반적으로 위와 같이 트랜잭션과 같은 생명주기를 사용하지만 설정을 통해 EntityManager instance와 생명주기를 같이 하도록 할 수 있다. 해당 내용에 대한 자세한 내용은 JPA 스펙 시트 Section 3.3 참고
'Develop > TIL(Today I Learned)' 카테고리의 다른 글
MockMvc & WebTestClient & REST Assured (0) | 2023.03.28 |
---|---|
Spring - 클래스 초기화 방법 비교 (PostConstruct, EventListener) (0) | 2023.03.06 |
Spring Converter & Formatter (0) | 2023.02.24 |
Spring MVC - 예외 처리(API) (0) | 2023.02.23 |
Spring MVC - 예외 처리(Error page) (0) | 2023.02.20 |