-
[Spring] 영속성 컨텍스트Backend/Spring 2022. 8. 22. 15:38728x90
Entity, EntityManagerFactory, EntityManagerFactory
Entity
RDB의 Table과 매핑되는 객체이다.
@Entity @Table(name = "customers") // table 명이 클래스명과 다를경우 public class Customer { @Id // Id PK private long id; private String firstName; private String lastName; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } }
EntityManagerFactory
Entity를 관리하는 EntityManager를 생산하는 공장이다.
Thread Safe하다. 하나의 빈에 대해 여러 쓰레드가 접근해도 안전하게 동작한다.
EntityManager
EntityManager는 Entity를 저장하고, 수정하고, 삭제하고, 조회하는 (CRUD) 등 Entity와 관련된 모든 일을 처리한다.
Thread Safe하지 않다. 여러 Thread에서 동시에 접근할 경우 동시성 이슈가 발생할 수 있다.
- 엔티티 매니저는 트랜잭션을 시작할 때, 커넥션을 획득한다.
영속성 컨텍스트(Persistence Context)
영속성 컨텍스트란?
JPA를 이용하는데 가장 중요한 요소로 엔티티를 영구 저장하는 환경이라는 뜻이다.
엔티티 매니저는 엔티티를 영속성 컨텍스트에 보관하고 관리한다.
- 엔티티는 엔티티매니저에 의해 영속성 컨텍스트에서 관리된다.
특징
- 영속성 컨텍스트와 식별자 값
- 영속성 컨텍스트 안에서 관리되는 엔티티는 식별자 값을 반드시 가져야한다.
- key-value로 엔티티를 관리하기 때문이다. (1차 캐시 관리를 위함)
- 영속성 컨텍스트와 데이터 베이스 저장
- JPA는 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 DB에 반영한다.(FLUSH)
- 플러시(flush)는 영속성 컨텍스트의 변경 내용을 DB에 동기화하는 작업인데, 이때 등록, 수정, 삭제 등의 엔티티 변경사항을 DB에 반영한다.
- 영속성 컨텍스트가 엔티티를 관리함으로써 얻는 이점
- 1차캐시
- 동일성 보장 (1차 캐시로 인한 현상)
- 트랜잭션을 지원하는 쓰기 지원
- 변경 감지 (durty checking)
- 지연 로딩
엔티티 생명주기
- 비영속 (new / transient) : 영속성 컨텍스트와 전혀 관계가 없는 상태
- 영속 (managed) : 영속성 컨텍스트에 저장된 상태
- 준영속 (detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
- 삭제 (removed) : 삭제된 상태
- new로 생성된 entity가 엔티티 매니저에 의해 persist를 하게 되면 managed(영속) 상태가 됨
- flush가 이루어지면 DB에 연산이 이루어짐
- 관리되던 영속 객체를 detach하게 되면 준영속 상태로서 더이상 관리되지 않는 상태가 됨
- 영속 컨텍스트를 clear해주게 되면 관리되던 영속 객체를 모두 지워버려 영속성 컨텍스트 안에서 관리되면 entity들이 모두 준영속 상태로 빠져나오게 됨
- close는 영속성 컨텍스트를 완전히 종료시켜 관리되던 entity들을 모두 준영속 상태로 만들고 영속성 컨텍스트가 종료됨
- 준영속 객체를 merge를 통해 영속화 시킬 수 있음
- remove를 통해서 영속화된 객체를 삭제하고 다시 persist를 통해서 영속화 시킬 수 있음
비영속
// 객체가 영속성컨텍스트, 데이트베이스와 무관한 상태이다. Customer customer = new Customer(); customer.setId(1L); customer.setFirstName("honggu"); customer.setLastName("kang");
영속
Customer customer = new Customer(); customer.setId(1L); customer.setFirstName("honggu"); customer.setLastName("kang"); // customer객체가 영속성 컨텍스트에서 관리된다. // em = EntityManager em.persist(customer);
준영속
// 영속상태의 customer객체(엔티티)를 영속성컨텍스트에서 분리한다. em.detach(customer); // 영속상태의 모든 객체를 영속성컨텍스트에서 분리한다. em.clear() // 영속성컨텍스트를 종료한다. em.close()
삭제
// customer 엔티티를 영속성컨텍스트에서 분리하고, DB에서도 삭제한다. em.remove(customer)
Customer 엔티티를 통한 영속성 컨텍스트 이해
저장
EntityManager em = emf.createEntityManager(); // 1)엔티티 매니저 생성 EntityTransaction transaction = em.getTransaction(); // 2)트랜잭션 획득 transaction.begin(); // 3)트랙잰셕 begin Customer customer = new Customer(); // 4-1)비영속 customer.setId(1L); customer.setFirstName("honggu"); customer.setLastName("kang"); em.persist(customer); // 4-2)영속화 transaction.commit(); // 5)트랜잭션 commit // 트랜잭션이 커밋이 되는 순간 쿼리가 수행된다. flush DB와 동기화가 된다.
조회
1차 캐시를 이용한 조회
@Test void 조회_1차캐시_이용() { EntityManager em = emf.createEntityManager(); EntityTransaction transaction = em.getTransaction(); transaction.begin(); Customer customer = new Customer(); customer.setId(1L); customer.setFirstName("honggu"); customer.setLastName("kang"); em.persist(customer); transaction.commit(); Customer entity = em.find(Customer.class, 1L); // 1차 캐시에서 조회한다. log.info("{} {}", entity.getFirstName(), entity.getLastName()); }
- em에서 find를 할 경우 1차 캐시에 정보가 있으면 1차 캐시에서 조회됨
- DB에 질의하지 않고 1차 캐시에서 결과를 반환
DB를 이용한 조회
@Test void 조회() { EntityManager em = emf.createEntityManager(); EntityTransaction transaction = em.getTransaction(); transaction.begin(); Customer customer = new Customer(); customer.setId(2L); customer.setFirstName("guppy"); customer.setLastName("hong"); em.persist(customer); transaction.commit(); em.clear(); //영속성 컨텍스트를 초기화 한다. Customer entity = em.find(Customer.class, 2L); // DB 에서 조회한다. SELECT ... log.info("{} {}", entity.getFirstName(), entity.getLastName()); em.find(Customer.class, 2L); // SELECT Query 수행되지 않는다. 1차캐시 사용 }
- 1차 캐시에 고객 정보가 없기 때문에 영속성 컨텍스트는 DB에 질의하여 결과를 가져와 1차 캐시에 캐싱함
- DB에 질의하여 1차 캐시에 저장한 후에 결과를 반환한다.
수정
@Test void 수정() { EntityManager em = emf.createEntityManager(); EntityTransaction transaction = em.getTransaction(); transaction.begin(); Customer customer = new Customer(); customer.setId(1L); customer.setFirstName("honggu"); customer.setLastName("kang"); em.persist(customer); transaction.commit(); // 엔티티를 영속화한후, 커밋을해서 flush()를 통해 DB에 저장. transaction.begin(); Customer entity = em.find(Customer.class, 1L); entity.setFirstName("guppy"); entity.setLastName("hong"); // em.update(entity) ??!! transaction.commit(); // flush -> UPDATE ... }
- 변경감지 - durty checking
- JPA는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 복사해서 저장해 두는데 이것을 스냅샷이라 한다.
- 플러시 시점에 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾는다.
- 만일 스냅샷과 비교하여 변경된 내용이 있을 경우 update Query를 수행한다.
- 변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용이 된다.
삭제
@Test void 삭제() { EntityManager em = emf.createEntityManager(); EntityTransaction transaction = em.getTransaction(); transaction.begin(); Customer customer = new Customer(); customer.setId(1L); customer.setFirstName("honggu"); customer.setLastName("kang"); em.persist(customer); transaction.commit(); transaction.begin(); Customer entity = em.find(Customer.class, 1L); em.remove(entity); transaction.commit(); // flush -> DELETE .. }
728x90'Backend > Spring' 카테고리의 다른 글
[Spring] 연관 관계 매핑 (0) 2022.08.25 [Spring] 단일 엔티티 매핑 (0) 2022.08.24 [Spring] Spring Frameworks를 활용한 데이터 레이어(RDB) 접근 방법 (0) 2022.08.19 [SpringBoot Error] java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES) 에러 해결하기 (0) 2022.08.18 [Spring] SPA와 CORS에 대해서 (0) 2022.08.17