영속성 - 1
영속성 컨텍스트
영속성 컨텍스트(Persistence Context)은 애플리케이션과 데이터베이스 사이에서 엔티티와 레코드의 괴리를 해소하는 기능과 객체를 보관하는 기능을 수행한다.
엔티티 객체가 영속성 컨텍스트에 들어오면 JPA는 엔티티 객체의 매핑 정보를 데이터베이스에 반영하는 작업을 수행한다. 이처럼 엔티티 객체가 영속성 컨텍스트에 들어와 JPA의 관리 대상이 되는 시전부터는 해당 객체를 영속 객체(Persistence Object)라고 부른다.
영속성 컨텍스트는 세션 단위의 생명주기를 가진다. 데이터베이스에 접근하기 위한 세션이 생성되면 영속성 컨텍스트가 만들어지고, 세션이 종료되면 영속성 컨텍스트도 없어진다. 엔티티 매니저는 이러한 일련의 과정에서 영속성 컨텍스트에 접근하기 위한 수단으로 사용된다.
엔티티의 생명주기
비영속(New)
- 영속성 컨텍스트에 추가되지 않은 엔티티 객체의 상태를 의미한다.
1 2
//객체 생성 Board board = new Board(); // 비영속 상태
영속(Managed)
- 영속성 컨텍스트에 의해 엔티티 객체가 관리되는 상태이다.
1
2
3
4
5
6
7
8
9
10
//객체 생성
Board board = new Board(); // 비영속 상태
board.setTitle("Title1");
board.setContent ("Content1")
//객체를 저장한 상태(영속)
EntityManagerFactory emf = Persistence.createEntityManagerFactory();
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(board);
준영속(Detached)
- 영속성 컨텍스트에 의해 관리되던 엔티티 객체가 컨텍스트와 분리된 상태이다.
1
em.detach(board); //혹은 em.clear(), em.close() 사용하여 준영속으로 만들 수 있음
삭제(Removed)
- 데이터베이스에서 레코드를 삭제하기 위해 영속성 컨텍스트에 삭제 요청을 한 상태이다.
1
em.remove(board);
영속성 컨텍스트의 이점
1. 1차 캐시
영속성 내부에는 1차 캐시가 존재한다. 영속 상태의 엔티티를 이곳에 저장하기 때문에 만약 엔티티를 조회했을 때 1차 캐시에 엔티티가 존재한다면 DB를 찾아보지 않아도 된다. 말 그대로 캐시로써의 기능과 장점을 가지고 있다.
2. 영속 엔티티의 동일성 보장
1
2
3
4
Board a = em.find(Board.class, "board1");
Board b = em.find(Board.class, "board1");
System.out.println(a==b) // true
1차 캐시로 반복 가능한 읽기(Repeatable Read) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공해 줄 수 있다.
3. 트랜잭션을 지원하는 쓰기 지연
1
2
3
4
5
6
7
8
9
10
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
em.persist(boardA);
em.persist(boardB);
//커밋하는 순간 데이터베이스에 SQL이 보내진다
transaction.commit();
em.persist()로 객체를 영속성 컨텍스트에 저장해도 DB에 바로 Insert 쿼리를 날리지 않는다.
SQL 쿼리들을 모아놓았다가 flush 될 때(영속성 컨텍스트의 변경내용을 DB에 반영할 때) 모아둔 쿼리를 모두 날린다. 이를 쓰기 지연이라고 한다.
4. 변경 감지(Dirty Checking)
1
2
3
4
5
6
7
8
9
10
11
12
13
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속 엔티티 조회
Board boardA = em.find(Board.class, "boardA");
// 영속 엔티티 데이터 수정
BoardA.setTitle("Update Title1");
memberA.setContent("Update Content1");
transaction.commit(); // [트랜잭션] 커밋
영속성 컨텍스트에서 엔티티를 조회해서 해당 엔티티를 수정하고자 하면 조회한 엔티티를 다시 업데이트하는 코드가 있어야 할 것 같지만, 그러한 코드가 없어도 영속성 컨텍스트내의 스냅샷과 엔티티를 비교해 변경된 엔티티가 있으면 Update 쿼리를 자동으로 생성한다.
물론 이 Update 쿼리도 쓰기 지연이 될 수 있다.
5. 지연 로딩(Lazy Loading)
지연로딩은 연관 관계 매핑되어 있는 엔티티를 조회 시 우선 프록시 객체를 반환하고, 실제로 필요할 때 쿼리를 날려 가져오는 기능이다.
즉, 필요할 때 데이터를 가져오는 기능이다.
영속성 전이
사실 오늘 영속성이라는 주제를 쓰게된 계기가 어떤 책에서는 영속성 컨텍스트만 설명하고 다른 책에서는 영속성 전이를 설명하고 있어서 이것을 같은 개념을 두고 명칭만 다르게 두고 있는지 실제로 영속성에 대해서 다른 개념들을 설명하는지 이해가 안되어 이것을 정리하고자 쓰게 되었다.
영속성 전이란 엔티티의 상태를 변경할 때 해당 엔티티와 연관된 엔티티의 상태 변화를 전파시키는 옵션이다.
결국 정리하자면 영속성 컨텍스트는 엔티티를 저장하는 환경을 의미하고, 영속성 전이는 엔티티의 영속 상태가 변경되었을 때 다른 연관된 엔티티의 영속 상태도 같이 변경되는 현상이라 정리하였다.
| 옵션 | 정보 |
|---|---|
| CascadeType.ALL | 부모 엔티티의 영속성 상태 변화를 자식 엔티티에 모두 적용 |
| CascadeType.PERSIST | 부모 엔티티가 영속화될 때 자식 엔티티도 영속화 |
| CascadeType.MERGE | 부모 엔티티가 병합될 때 자식 엔티티도 병합 |
| CascadeType.REMOVE | 부모 엔티티가 삭제될 때 자식 엔티티도 삭제 |
| CascadeType.REFRESH | 부모 엔티티가 refresh 되면 연관된 자식 엔티티도 REFRESH |
| CascadeType.DETACH | 부모 엔티티가 detach 되면 연관된 자식 엔티티도 detach상태로 변경 |
영속성 전이 옵션을 무분별하게 사용할 경우 삭제되지 말아야 할 데이터가 삭제될 수 있으므로 조심해서 사용해야 한다.
영속성 전이 옵션은 단일 엔티티에 완전히 종속적이고 부모 엔티티와 자식 엔티티의 라이프 사이클이 유사할 때 cascade 옵션을 잘 활용하자.