일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- g1
- JWT
- Spring
- java
- apache
- oauth2
- 스프링부트
- 권한
- 리팩토링
- 클린코드
- tomcat
- OAuth
- Refactoring
- 스프링
- clean code
- 스프링 부트
- 페이스북
- 페이징
- Producer
- spring boot
- load balancing
- jvm
- GC
- RabbitMQ
- gdg
- Security
- 비동기
- 시큐리티
- JPA
- assertj
Archives
- Today
- Total
허원철의 개발 블로그
JPA - Persistence Context (영속성 컨텍스트) 본문
이번 글은 JPA을 개념적으로 이해해본 내용에 대한 글입니다.
JPA를 개념적인 측면에서 제대로 이해하고 사용한 것은 아니였습니다. 대략적으로 사용해보고, 여러 삽질을 겪어보고, 그 때 마다 레퍼런스문서를 찾아보고 구글링을 통해 해답을 얻었습니다(참 바보같은 짓이죠...ㅠ). 그러다보니 개념적인 부분을 많이 놓치고 있는 것 같아 한 번 정리를 하고자합니다.
※ KSUG에서 발표하신 자료를 우연히 보게되어 내용을 조금씩 정리하고 제가 알고 있는 개념을 추가해서 작성한 것 입니다. ( 참고 : 2015 - KSUG Conference 영상 )
JPA가 어떻게 생겨났는가...?
- EJB라는 것이 존재했는데 너무 어렵고 쓰기 힘들었다고 합니다. 그래서 어느 개발자가 하이버네이트를 만들었고..EclipseLink, OpenJpa 등등이 생기면서 이걸 하나의 공통적인 스펙(인터페이스)으로 만들자고 하여 생긴 것이 JPA 입니다.
그래서 JPA가 뭐하는 것인가..?
- JPA는 xBatis랑은 달리 OOP(Object-Oriented Programming : 객체지향 프로그래밍)의 강점을 살리고자 하는 ORM(Object Relational Mapping) 프레임워크 입니다.
※ 물론, xBatis로 OOP의 강점을 살리면서 할 수 있지만, mapper 성격이 강하기 때문에 그렇게 까지는 안합니다.
JPA를 하기에 앞서 알아야할 개념
1) Persistence Context (영속성 컨텍스트) ★★★
: Server Side와 Database Side 사이에 엔티티를 저장하는 논리적인 영역이라고 할 수 있습니다.
- 이런 영역이 있음으로서 이룰 수 있는 이점?
① 1차 캐시
- 이해하기 쉽게 설명을 하자면, 영속성 컨텍스트에 Map과 같은 객체가 존재하여 임시로 객체를 저장해줍니다.
② 동일성 보장
- Collection에서 객체를 빼오듯이 같은 객체를 반환하게 되면 새로운 객체가 나오는 것이 아니라 동일한 객체가 반환됩니다.
③ 변경 감지
- 영속성 상태의 객체는 객체의 데이터가 변경이 되면, 자동 update 됩니다.
- EntityManager에서 flush가 되고, commit이 됩니다.
※ flush가 언제되나??
⑴ 강제 Flush : EntityManager.flush()
⑵ 트랜잭션이 끝났을 때 :영속성 컨텍스트는 트랜잭션으로 범위로 만들어지기 때문입니다.
⑶ JPQL Query 실행 : JPQL은 실제 Database Side에서 데이터를 가져오기 때문에 동기화를 위해 JPQL Query가 실행 전에 flush 됩니다.
④ 트랜잭션으로 인한 쓰기 지연
- 영속성 컨텍스트는 트랜잭션 범위 안에서 동작합니다.
- J2EE 스펙의 기본전략입니다.
※ 각각의 Thread는 서로 다른 트잭잭션을 이용하기 때문에 서로 다른 영속성 컨텍스트를 가집니다.
⑤ 지연로딩
- N + 1 쿼리라고 하는데, 엔티티와 관계가 맺어진 엔티티의 데이터를 가져 올 수 있습니다.
※ 지연로딩의 경우, 오히려 성능 저하의 원인이 될 수 있으므로 충분한 설정을 거쳐야 합니다.
2) EntityManagerFactory
: EntityManager를 관리하기 위한 객체입니다.
3) EntityManager
: Database Side에 엔티티를 반영하기 위한 객체입니다.
① 비영속(new / transist)
- 일반적인 인스턴스이라고 볼 수 있습니다. ( new Entity() )
② 영속(managed)
- EntityManager에 persist를 통해 인트스턴스를 영속성 컨텍스트에 넣어진 상태입니다.
③ 준영속(detached)
- 영속성 컨텍스트로부터 나온 객체입니다.
- detach, clear, close 등으로 가능합니다.
- 거의 비영속 상태라고 볼 수 있습니다
- 지연로딩이 불가능합니다.
- 식별자(@Id)가 존재합니다.
- merge 로 다시 영속성 상태로 만들 수 있습니다.
④ 삭제(removed)
- Database Side에서 지워졌지만 영속성 컨텍스트에서는 존재하는 상태입니다.
예제로 배워보는 Persistence Context
- 예시를 위해 영속성으로 쓰일 데이터와 준영속성으로 쓰일 데이터를 준비합니다.
1 2 3 4 5 6 7 8 9 | // 비영속성 데이터 private Customer getPersistenceContextCustomer() { return new Customer("heo won chul", "010-xxxx-xxxx", "developer"); } // 준영속성 데이터 private Customer getNotPersistenceContextCustomer() { return new Customer(10L,"heo won chul", "010-xxxx-xxxx", "developer"); } | cs |
1) 간단한 insert를 해보도록 하겠습니다. Test Case이므로 Rollback이 되기 때문에, flush는 덤으로 해보도록 하겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @Transactional @Test public void test_insertA() { // org.hibernate.PersistentObjectException: detached entity passed to persist // em.persist(getNotPersistenceContextCustomer()); em.persist(getPersistenceContextCustomer()); em.flush(); } @Transactional @Test public void test_insertB() { em.merge(getNotPersistenceContextCustomer()); // 영속성 컨텍스트에 추가 em.flush(); } | cs |
※ 준영속성 엔티티를 persist() 할 경우, PersistentObjectException이 발생합니다
2) 영속성 컨텍스트의 존재를 보기 위해 간단한 Test Case를 살펴보도록 하겠습니다.
① flush (O) / clear (O)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @Transactional @Test public void test_insertClearAndFindAndUpdateClear() { em.merge(getNotPersistenceContextCustomer()); // Persistence Context 추가 em.flush(); // Database 동기화 em.clear(); // Persistence Context 초기화 Customer customer = em.find(Customer.class, 10L); System.out.println(customer); customer.setBigo("Developer"); em.merge(customer); // Persistence Context 추가 em.flush(); // Database 동기화 em.clear(); // Persistence Context 초기화 Customer result = em.find(Customer.class, 10L); System.out.println(result); } | cs |
② flush (O) / clear (X)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @Transactional @Test public void test_insertAndFindAndUpdate() { em.merge(getNotPersistenceContextCustomer()); // Persistence Context 추가 em.flush(); // Database 동기화 Customer customer = em.find(Customer.class, 10L); System.out.println(customer); customer.setBigo("Developer"); em.merge(customer); // Persistence Context 추가 em.flush(); // Database 동기화 Customer result = em.find(Customer.class, 10L); System.out.println(result); } | cs |
① - Console
1 2 3 4 5 6 7 8 | Hibernate: select customer0_.idx as idx1_0_0_, customer0_.bigo as bigo2_0_0_, customer0_.name as name3_0_0_, customer0_.tel as tel4_0_0_ from customer customer0_ where customer0_.idx=? Hibernate: call next value for hibernate_sequence Hibernate: insert into customer (bigo, name, tel, idx) values (?, ?, ?, ?) Hibernate: select customer0_.idx as idx1_0_0_, customer0_.bigo as bigo2_0_0_, customer0_.name as name3_0_0_, customer0_.tel as tel4_0_0_ from customer customer0_ where customer0_.idx=? Customer(idx=10, name=heo won chul, tel=010-xxxx-xxxx, bigo=developer) Hibernate: update customer set bigo=?, name=?, tel=? where idx=? Hibernate: select customer0_.idx as idx1_0_0_, customer0_.bigo as bigo2_0_0_, customer0_.name as name3_0_0_, customer0_.tel as tel4_0_0_ from customer customer0_ where customer0_.idx=? Customer(idx=10, name=heo won chul, tel=010-xxxx-xxxx, bigo=Developer) | cs |
② - Console
1 2 3 4 5 6 7 8 | Hibernate: select customer0_.idx as idx1_0_0_, customer0_.bigo as bigo2_0_0_, customer0_.name as name3_0_0_, customer0_.tel as tel4_0_0_ from customer customer0_ where customer0_.idx=? Hibernate: call next value for hibernate_sequence Hibernate: insert into customer (bigo, name, tel, idx) values (?, ?, ?, ?) Customer(idx=10, name=heo won chul, tel=010-xxxx-xxxx, bigo=developer) Hibernate: update customer set bigo=?, name=?, tel=? where idx=? Customer(idx=10, name=heo won chul, tel=010-xxxx-xxxx, bigo=Developer) | cs |
이 처럼 중간에 영속성 컨텍스트에서 1차 캐시역할을 하기 때문에 find를 하더라도 값이 존재하면 실제 Database에서 값을 가져오는 것이 아니라, 영속성 컨텍스트에서 가져와 처리를 할 수 있습니다.
참고
'web' 카테고리의 다른 글
Spring Boot - MyBatis (396) | 2017.04.13 |
---|---|
Spring Boot + JPA + Time package Issue (437) | 2017.03.22 |
Spring Boot - Security + JWT (423) | 2017.02.13 |
Spring Boot - QueryDSL (410) | 2017.01.25 |
Spring Boot - jar로 Deploy(배포)하기 (405) | 2017.01.19 |
Comments