자바 ORM 표준 JPA 프로그래밍 - 기본편 수업을 듣고 정리한 내용입니다.
🔔 JPA에서 가장 중요한 2가지
- 객체와 관계형 데이터베이스 매핑하기(
ORM
) : DB, 객체 등 설계와 관련된 부분이다.- 영속성 컨텍스트 : 실제
JPA
가 내부적으로 어떻게 동작하는지와 관련된 부분
✔️ 엔티티 매니저 팩토리와 엔티티 매니저
JPA
를 사용하기 위해서는 엔티티 매니저 팩토리와 앤티티 매니저에 대해 알아야 한다.EntityManagerFactory
가 생성된다.EntityManage
를 생성하여 커넥션 풀을 사용해서 DB에 접근한다.
📣 영속성 컨텍스트
- JPA를 이해하는데 가장 중요한 용어
- 뜻 : “엔티티를 영구 저장하는 환경”
EntityManager.persist(entity);
: 영속성 컨텍스트에 엔티티를 저장한다는 것이다.- 영속성 컨텍스트는 논리적인 개념이며 눈에 보이지 않는다.
- 엔티티 매니저를 통해서 영속성 컨텍스트에 접근한다.
new/transient
) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태managed
) : 영속성 컨텍스트에 관리되는 상태detached
) : 영속성 컨텍스트에 저장되었다가 분리된 상태removed
) : 삭제된 상태
✔️ 비영속
JPA
에 관계없이 그냥 객체 생성, 초기화만 된 상태
✔️ 영속
✔️ 준영속, 삭제
- 1차 캐시
- 동일성(
identity
) 보장- 트랜잭션을 지원하는 쓰기 지연 (
transactional write-behind
)- 변경 감지(
Dirty Checking
)- 지연 로딩(
Lazy Loading
)
member
객체를 생성하고 persist
하면 영속성 컨텍스트 내부의 1차 캐시에 저장된다.key-value로
저장되며, key
는 member1
, value
는 member
객체가 담긴다.
✔️ 1차 캐시에서 조회
JPA
는 엔티티 조회할 때 1차 캐시부터 찾는다.
✔️ 데이터베이스에서 조회
member2
객체를 1차 캐시에 저장한다.member2
를 반환한다.
REPEATABLE READ
) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
JPA
는 커밋할 때 INSERT SQL
을 보낸다.
em.persist(memberA);
로 memberA
를 1차 캐시에 넣는다.JPA
가 해당 엔티티를 분석해서 INSERT SQL
생성해서 쓰기 지연 SQL
저장소에 쌓아둔다.em.persist(memberB);
도 동일하게, 1차 캐시에 넣고 INSERT SQL
생성해서 쓰기 지연 SQL
저장소에 쌓아둔다.
transaction.commit();
하는 시점에 쓰기 지연 SQL
저장소에 쌓인 SQL
문들이 한꺼번에 날아간다. (flush
)
✏️ 쓰기 지연을 사용하는 이유
- 버퍼링 기능으로 인해 쿼리를 여러 번 날리지 않고 최적화가 가능하기 때문이다!
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
//em.update(member) 이런 코드가 있어야 하지 않을까?
transaction.commit(); // [트랜잭션] 커밋
transaction.commmit();
을 하면 내부적으로 flush
가 실행된다.
→ 엔티티와 스냇샷을 비교해서 변경된 데이터를 감지한다.
(스냅샷: 엔티티 값을 읽어 온 최초 시점의 엔티티 데이터)
→ 변경 사항이 있다면 UPDATE SQL
을 쓰기 지연 SQL 저장소 쌓아둔다.
→ 쓰기 지연 SQL 저장소의 UPDATE SQL
을 DB에 반영하고 커밋한다.
//삭제 대상 엔티티 조회
Member memberA = em.find(Member.class, “memberA");
em.remove(memberA); //엔티티 삭제
영속성 컨텍스트의 변경내용을 데이터베이스에 반영하는 것
➡️ 영속성 컨텍스트의 쿼리들을 데이터베이스에 날리는 것이다!
✔️ 플러시 발생
✔️ 영속성 컨텍스트를 플러시하는 방법
em.flush()
: 직접 호출JPQL
쿼리 실행 : 플러시 자동 호출
✔️ JPQL 쿼리 실행시 플러시가 자동으로 호출되는 이유
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//중간에 JPQL 실행
query = em.createQuery("select m from Member m", Member.class);
List<Member> members= query.getResultList();
JPA
에서는 JPQL
쿼리를 수행하기 전에 flush
를 실행해서 DB와 영속성 컨텍스트간에 동기화를 해준다.member
들이 조회가 안되는 경우를 막는다.
✔️ 플러시 모드 옵션
em.setFlushMode(FlushModeType.COMMIT)
FlushModeType.AUTO
: 커밋이나 쿼리를 실행할 때 플러시 (기본 값)FlushModeType.COMMIT
: 커밋할 때만 플러시
✏️ 플러시 정리
- 영속성 컨텍스트를 비우지 않는다.
- 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
- 트랜잭션이라는 작업 단위가 중요하다. → 커밋 직전에만 동기화 하면 된다.
detached
)
✔️ 준영속 상태로 만드는 방법
em.detach(entity)
: 특정 엔티티만 준영속 상태로 전환em.clear()
: 영속성 컨텍스트를 완전히 초기화em.close()
: 영속성 컨텍스트를 종료
참고