[Spring] JPA의 영속성 컨텍스트와 생명주기
Spring Framework를 이용하여 웹 애플리케이션이나 서버 애플리케이션을 개발할 때 사용하는 DB 라이브러리가 있습니다. 보통 JDBC 드라이버를 이용하여 DB Connection을 수행하고, 여러 개의 커넥션이 연결하고 끊어지기를 반복하기 위해 재사용할 수 있도록 하는 Connection Pool(DBCP, HikariCP), 그리고 각 도메인에 대한 CRUD를 객체 지향적인 방법으로 프로그래밍 할 수 있도록 하는 ORM이 있습니다.
JPA는 여기서 Java 언어의 표준 ORM에 속합니다. Spring boot를 사용하고 있는 요즘에는 Spring 프레임워크에 맞춰져 있는 Spring Data JPA를 사용하는데요. 그런데, Spring Data JPA를 사용하면서 객체에 있는 데이터들이 스스로 변화를 감지하고, 객체의 데이터가 자동으로 변하고, 생성, 삭제가 이루어지는데, 도대체 이게 어떻게 구현되어 있길래 가능한 것일까요?
이번 포스트는 이러한 재밌는 궁금증을 초반부로 시작하여, JPA의 생명주기와 영속성 컨텍스트란 무엇인지에 대한 이야기를 다뤄보고자 합니다.
2020/05/23 - [Programming/Spring] - [Spring boot] Hibernate, JPA 그리고 Spring Data JPA
[Spring boot] Hibernate, JPA 그리고 Spring Data JPA
지난 포스트에서 다뤄 본 JDBC와 Spring JDBC는 자바와 데이터베이스를 연결하기 위한 최초 인터페이스였고, 이들 코드를 개선하기 위해 리팩토링하는 작업들, 연결 세션, 그리고 관심점의 분리 등 �
blog.neonkid.xyz
혹시 JPA나 Spring Data JPA, Hibernate가 무엇인지 잘 모르시겠다면, 위의 글을 읽어보신 후, 진행해주시기 바랍니다.
영속성 컨텍스트
영속성 컨텍스트란, Entity를 영구적으로 저장하는 환경을 말합니다. Spring으로 한 번 쯤 JDBC를 이용하여 개발해보신 분들이 계신다면 DB에 있는 데이터가 어떤식으로 읽히고 쓰는 건지를 이해하실 겁니다. 간단하게 요약하자면, 다음과 같습니다.
* 커넥션 생성 -> 커넥션 연결 -> Statement 생성 -> Statement 실행 -> Transcation 커밋 -> 커넥션 연결 끊기
그렇다면 ORM에서는 어떻게 처리해야 할까요? SQL 쿼리를 사용하지 않고 객체 지향적으로 처리한다고 생각해본다면, 먼저 객체를 생성하고, 그 다음 DB 처리가 이루어져야 합니다. 그렇습니다. 영속성 컨텍스트는 DB 처리가 되기 이전, 객체를 먼저 생성하는 단계 즉, 그 이후의 단계 처리를 위해 존재하는 컨텍스트입니다. 논리적인 개념에 속하며 Spring에서는 EntityManager라는 객체를 통해 사용합니다.
그런데, 영속성 컨텍스트는 Java에서 뭘 사용하냐에 따라 사용할 수 있는 것이 제한되어 있습니다. J2SE의 경우 EntityManager와 영속성 컨텍스트가 1:1 관계를 갖지만, Spring과 같은 J2EE의 경우 같은 트렌잭션 범위에 있는 EntityManager는 동일한 영속성 컨텍스트를 사용합니다.
즉, Spring의 IoC 컨테이너의 기능인 의존성 주입처럼 재활용한다는 이야기인 것이죠.
Entity의 Lifecycle
그렇다면 각 Entity들이 어떨 때, 영속성 컨텍스트에 들어가고, DB에 반영이 될까요? 먼저 객체를 생성하는 것부터 시작하여 어떤식으로 DB에 들어가는지 순으로 생명주기에 대해 알아보도록 하겠습니다.
- 비영속(new/transient)
- 영속성 컨텍스트에 들어가기 전 상태입니다.
// 객체를 생성만 한 상태
Cafe cafe = new Cafe();
cafe.setName("Americano");
cafe.setPrice(3000);
이 때는 DB에 아무런 영향이 가지 않습니다. 단지 새로운 데이터를 생성하기 위해 객체 지향 적인 프로그래밍 방법을 사용한 상태라고 보시면 될 것 같습니다.
- 영속 (managed)
- 영속성 컨텍스트에 저장된 상태
- Entity가 영속성 컨텍스트에 의해 관리되지만 DB에는 역시 아무런 영향이 없음
- 컨텍스트에 미리 객체에 대한 정보를 DB 형태로 저장
- Transaction과 관련된 commit 메소드가 호출되었을 때 DB에 적용
// 객체를 생성만 한 상태
Cafe cafe = new Cafe();
cafe.setName("Americano");
cafe.setPrice(3000);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
// 객체를 저장한 상태
em.persist(cafe);
JPA에서는 기본적으로 캐시를 사용합니다. DB에서 반대로 객체로 가져올 때는 캐시에 저장되며 차후 다시 호출될 때 1차 캐시에 이미 불러왔던 내용이 있다면, 캐시를 사용해서 불러오게 됩니다. 이 때 영속성 컨텍스트를 사용하며 DB에 저장할 때 역시 커밋 메소드가 호출되기 전까지는 영속성 컨텍스트가 그 정보를 가지고만 있는 상태가 됩니다.
- 준영속 (detached)
- 영속성 컨텍스트에서 분리된 상태
// 카페 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(cafe);
더 이상 해당 객체에 대한 변경 사항이 없을 경우에는 EntityManager에서 준영속 상태로 돌려야 합니다.
- 삭제 (Removed)
- DB에서 삭제된 상태
// 객체를 삭제
em.remove(cafe);
삭제는 DB에서도 반영이 되게 됩니다. 물론 remove 이후에 다시 영속 후, commit을 올리면 그 정보가 다시 돌아옵니다만 준영속이랑 관계 없이 삭제를 하게 되면 DB에 반영되므로 사용에 유의해야 합니다.
마치며...
여기까지 JPA의 영속성 컨텍스트와 생명주기에 대해 간단히 알아봤습니다. JPA가 표준 ORM으로써 객체 지향 프로그래밍 방식을 통해 SQL 쿼리를 직접 만들지 않고도 객체 생성을 통해 DB 처리를 해준다고는 하지만 구체적으로 어떤식으로 동작하는지에 대해 아는 것도 스프링 프레임워크를 다루는 개발자로써 알아야겠다고 생각했습니다.
다음 포스트에서는 영속성 컨텍스트의 구조와 이점에 대해 좀 더 심층적으로 다루도록 하겠습니다.
'Programming > Spring' 카테고리의 다른 글
[Spring] JPA의 플러시(flush) (0) | 2020.07.02 |
---|---|
[Spring] JPA 영속성 컨텍스트 구조로 보는 이점 (0) | 2020.06.24 |
[Spring boot] 애플리케이션을 Docker 이미지로 만들기 Old & New (0) | 2020.06.04 |
[Spring boot] DAO와 DTO (6) | 2020.05.27 |
[Spring Data] Hibernate, JPA 그리고 Spring Data JPA (0) | 2020.05.23 |