JPA 더티 체킹(Dirty Checking)
이번에 Mybatis 대신해서 JPA로 개인 프로젝트를 진행하려고 해서 JPA에 대해서 공부 중입니다.
JPA는 Mybatis와 다르게 ORM(Object-Relational-Mapping)입니다.
ORM(Object-Relational-Mapping)?
ORM은 객체(Object)와 DB 테이블을 Mapping 함으로써 RDM 테이블을 객체지향적으로 관리할 수 있게 해 줍니다.
객체와 RDB의 중간에서 ORM이 매핑을 해주기 때문에 별도의 쿼리문을 작성하지 않습니다.
하지만 복잡한 쿼리문이 필요한 경우에는 직접 작성해서 사용 가능합니다.
Mybatis는 SQL Mapper로 객체를 매핑하는 것이 아닌 쿼리를 매핑합니다. 즉 우리가 작성한 쿼리문이 필요합니다.
더티 체킹(Dirty Checking)?
JPA를 공부하면서 흥미로운 것을 새롭게 알았는데 그것이 바로 더티 체킹입니다.
처음에 코드를 보면서 Update 쿼리를 날리지 않았는데 DB 데이터가 업데이트되는 것을 보고 상식적으로 말이 되는 건가 싶었습니다.
더티 체킹에서 더티는 "엔티티 데이터의 변경된 부분"으로 해석을 하고 더티 체킹의 의미로는
"변경된 부분을 체크해서 DB에 반영한다"입니다.
즉 객체의 데이터가 변경이 되면 처음 조회한 데이터와 비교를 해서 수정된 데이터가 있다면 자동으로 Update 쿼리문을
작성해서 날립니다.
하지만 더티 체킹이 일어나는 환경으로는 밑의 2가지가 충족되어야만 합니다.
1. 영속 컨텍스트 안에 객체가 존재해야 합니다.
2. Transaction 안에서 객체를 변경해야 합니다.
ㄴ Transaction을 사용하는 방법으로는 @Transational 어노테이션을 사용하는 방법과
우리가 직접 EntityTransaction 사용해서 범위를 지정해주면 됩니다.
영속 컨텍스트란?
- 어플리케이션과 데이터베이스 사이에 존재하는 논리적인 개념으로 엔티티를 저장하는 환경을 의미합니다.
- 엔티티매니저를 통해서만 접근이 가능합니다.
- J2EE 환경에서는 여러 엔티티매니저가 하나의 영속성컨텍스트를 공유합니다.
- 영속성 컨텍스트에 존재하는 엔티티는 플러시 호출시 데이터베이스에 반영됩니다.
- entityManger.flush()로 플러시 직접 호출
- 트랜젝션 커밋(commit) 시 플러시 자동 호출
- JPQL 쿼리 실행 시 플러시 자동 호출
- 영속성 컨텍스트의 장점
- 1차 캐시: 엔티티 조회 시 영속성 컨테스트에 존재하면 바로 리턴, 없으면 데이터베이스 조회 후 리턴.
- 동일성(==) 보장: 조회 시 항상 같은 엔티티 인스턴스를 리턴(주소값이 같습니다.)
- 트랜잭션을 지원하는 쓰기 지연(Transactional write-behind): 트랜젝션 커밋 될 때까지 내부 쿼리저장소에 모아뒀다가 한 번에 실행
- 변경감지(Dirty Checking): 엔티티의 스냅샷을 유지하면서 엔티티의 변경사항을 체크한다. update쿼리가 항상 같습니다.
- 지연로딩(Lazy Loading): 연관된 엔티티를 모두 불러오는 것이 아니라, 실제 호출될 때 로딩되도록 지원(프록시 객체 사용)
엔티티매니저(EntityManager)
em.find(); // 엔티티 조회
em.persist(); // 엔티티 저장
em.remove(); // 엔티티 삭제
em.flush(); // 영속성 컨텍스트 내용을 데이터베이스에 반영
em.detach(); // 엔티티를 준영속 상태로 전환
em.merge(); // 준영속 상태의 엔티티를 영속상태로 변경
em.clear(); // 영속성 컨텍스트 초기화
em.close(); // 영속성 컨텍스트 종료
find() 메서드는 영속 컨텍스트에서 엔티티를 검색하고 없을 경우 DB에서 데이터를 찾아 영속 컨텍스트에 저장합니다.
persist() 메서드는 엔티티를 영속 컨텍스트에 저장 후 INSERT 쿼리를 실행합니다.
remove() 메서드는 엔티티 클래스를 영속 컨텍스트에서 삭제 후 DELETE 쿼리를 실행합니다.
주의점
영속성 컨텍스트를 관리하는 EntityManager를 통해서 엔티티 저장을 하고 DB에도 반영시키고 싶을 경우에는무조건 Transaction 안에서 실행이 되어야 합니다. Transaction 안에서 저장, 삭제, 수정을 하여야 commit() 하는 시점에쿼리가 실행되어 DB에 반영이 됩니다.
Transaction 커밋 전까지 메모리에 SQL을 유지하며 쓰기 지연 후 커밋 시 DB에 일괄 반영합니다.
수정 같은 경우는 EntityManager에서는 엔티티를 조회하면 해당 엔티티의 조회 상태 그대로 스냅샷을 만들어놓습니다.
그리고 Transaction이 끝나는 시점에는 이 스냅샷과 비교해서 다른 점이 있다면 Update 쿼리를 데이터베이스로 전달합니다.