[김영한 - JPA 기본] #3 내부 동작방식

JPA 에서 가장 중요한 것 ?

  • 객체와 관계형 데이터베이스 매핑하기 (Object Relational Mapping)

  • 영속성 컨텍스트 (Persiste Context)

EntityMangerFactory

EntityMangerFactory는 사용자 요청이 있을 때마다

EntityManger를 생성하여 커넥션풀에 접근해서 DB로부터 DML을 수행한다

영속성 컨텍스트 (Persiste Context)

  • 엔티티를 영구 저장하는 환경

  • 1차 캐시라고 생각해도 좋다

  • 논리적인 개념

  • EntityManger를 통해 접근한다

  • 스프링같은 경우 사용자 요청마다 만들어진 EntityManger들이 하나의 영속성 컨텍스트에 접근한다

    • 즉 N : 1 관계

엔티티 생명 주기

  • 비영속 (new/transient)

    • persistence context 에 속하지 않은 상태
  • 영속 (managed)

    • persistence context 에 속한 (관리되는) 상태
  • 준영속 (detached)

    • persistence context 에 속하였다가 분리된 상태
  • 삭제 (removed)

    • DB와 1차 캐시 모두에서 삭제된 상태

persistence context 의 이점

  • 1차 캐시

    • DB 읽기 전에 읽어옴
  • 동일성(ientity) 보장

    • 반복적 읽기 (Reapeatable Read 지원 )
    • DB가 아닌 App 수준에서 지원
  • 트랜잭션을 지원하는 쓰기 지연

    • commit() 하기 전까지는 DB에 영향을 미치지 않는다
  • 변경 감지 (Dirty Checking)

    • update시 감지를 함
    • 그렇다고 정말 update란 구문이 필요한 게 아닌 자바 구문의 settter를 통해 감지함
    • 스냅샷과의 비교를 통해서 변경되었음을 감지
    • commit()을 할때 flush()가 호출되는데 이때 스냅샷과 Entity를 비교해서 Entity가 변경되었다면 update 쿼리 발생
    • 스냅샷: 1차 캐시에 처음 저장된 순간의 상태 (사진같은 느낌)
  • 지연 로딩

    • 다른 entity를 조회하는 순간에 읽어오는 기능이 있음 (예 member객체를 통한 team 조회)

플러시 flush

  • persistence context 의 (변경 내용)을 DB에 반경

flush 가 호출이 될 때

  • flush() 로 직접 호출
  • 자동 호출
    • 트랜잭션 커밋
    • JPQL 쿼리 실행
      • 실험해보았지만 실제로 호출되는지(동기화) 확인을 하지 못하였다
em.persist(new Member(101L, "user A"));
em.persist(new Member(102L, "user B"));
em.persist(new Member(103L, "user C"));

TypedQuery<Member> query = em.createQuery("select m from Member m", Member.class);
List<Member> members = query.getResultList();
members.forEach(System.out::println);

flush 특징

  • persistence context DB와 동기화

  • 동기화를 했다 하여 1차 캐시 (persistence context) 를 비우지 않음

  • 트랜잭션이라는 작업 단위를 만들어준다!

flush 모드 옵션

  • 건들 일도 없으며 수정하지 않는 것이 좋다
em.setFlushMode(FlushModeType.AUTO);
  • 커밋이나 쿼리를 실행할 떄 (기본값)
em.setFlushMode(FlushModeType.COMMIT);
  • 커밋할때만

준영속 상태

  • 영속 상태에서 분리되는 상태

    • 더티 체킹 등의 기능을 받지 못함
  • 영속 상태에 등록되는 2가지

    • persist()
    • find()
  • API
    • detach(오브젝트) 특정 오브젝트 준영속화
  • 예)
em.detach(member);
  • clear() 모든 entity 준영속화

커스텀으로 만들어본 JPA Manager

실행부

PhilzManger philzManger = new PhilzManger();
philzManger.persist(new Member(1L, "user A"));
philzManger.persist(new Member(2L, "user B"));

Member member = philzManger.find(1L);
Member member2 = philzManger.find(1L);

System.out.println("(member == member2) = " + (member == member2));

핵심 매니저 (philz는 제 닉네임입니다)

class PhilzManger {

		public static final String NOT_FOUND_MEMBER_EXCEPTION = "[ERROR] 존재하지 않는 Member 입니다.";

		private static Map<Long, Member> members = new HashMap<>();

		private void persist(Member member) {
			members.put(member.getId(), member);
		}

		private Member find(Class<T> clazz, Long id) {

			Member findMember = members.get(id);
			if (findMember != null) {
				return findMember;
			}
			throw new IllegalArgumentException(NOT_FOUND_MEMBER_EXCEPTION);
		}
	}

실행결과

(member == member2) = true

트랜잭션과 읽기 쓰기

트랜잭션이 없어도 읽기는 (find) 가능하며 쓰기 (persist)는 불가능한 것으로 보인다.




© 2020.12. by 따라쟁이

Powered by philz