[24.09.12] TIL

yy·2024년 9월 11일

개발일지

목록 보기
102/122

JAVA, Spring 공부중

JPA

  • 엔티티 매니저 팩토리를 통해 엔티티 매니저를 각 요청마다 생성하게 되는데 엔티티 매니저를 사용하여 DB커넥션을 사용하여 DB를 사용하게 됨.

엔티티 매니저 팩토리

엔티티 매니저

JPA 가장 중요한 2가지

객체와 RDB 매핑하기



영속성 컨텍스트

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

  • EntityjManager.persist(entity); 를 사용하면 entity객체를 DB에 저장하는게 아니라 영속성 컨텍스트를 통해서 entity를 영속한다는 뜻임. entity를 영속 컨텍스트에 저장한다는 것.



엔티티의 생명주기

  1. 비영속(new/ transient) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태.
  • JPA와 전혀 관계없이 그냥 객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.set Name("memberName");

  1. 영속(managed) : 영속성 컨텍스트에 관리되는 상태
  • 엔티티 매니저 안에는 영속 컨텍스트가 있음. 객체를 만들고 엔티티 매니저를 만들어 객체를 엔티티 매니저에 영속 시키면 영속 컨텍스트 안에서 객체가 영속 상태가 됨.
// 객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setName("memberName");

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

// 객체를 저장한 상태(영속) - 이 때 DB에 저장되는 것은 아님.
// 트랜잭션 커밋을 할 때  DB로 날라가는거임.
em.persist(member);
■ 영속상태의 경우

1) em.persist() 할 때
2) find로 해서 DB에서 찾아 영속 컨텍스트 내에 캐시가 생성되어 JPA가 관리하는 상태


  1. 준영속(detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
  • 영속성 컨텍스트가 제공하는 기능을 사용 못함 ( 변경감지, 업데이트할 때의 기능 등 사용 못함)
// 회원 엔티티를 영속성 컨텍스트에서 분리한 상태 (준영속 상태)
em.detach(entity) : 특정 엔티티만 준영속 상태로 전환
em.clear() : 영속성 컨텍스트 완전히 초기화
em.close() : 영속성 컨텍스트 종료

  1. 삭제(removed) : 삭제된 상태
// 영속성 컨텍스트에서 객체를 삭제한 상태(삭제)
em.remove(member);


영속 컨텍스트의 이점

  1. 1차 캐시에서 조회

영속 컨텍스트 안에 1차 캐시가 있어 (기본키, 객체)의 형태로 일시 저장이 된다. 맨 처음 DB에서 조회 후 그 다음부터는 1차 캐시에 있는 내용을 조회한다.

엔티티 매니저는 데이터 트랜잭션 단위로 만들고, 트랜잭션이 끝날 때 같이 종료를 시키기 때문에 큰 이점은 되지 않는다. 한 개의 요청이 있고, 요청이 끝나면 영속 컨텍스트를 지우기때문에 캐시의 효과는 미비함. 한 트랜잭션 안에서만 효과가 있음.


  1. 영속 엔티티의 동일성 보장
    Member a = em.find(Member.class, "member1");
    Member b = em.find(Member.class, "member1");

System.out.println(a == b); //동일성 비교 true. 같은걸로 취급됨.
1차 캐시로 반복 가능한 읽기(REPEATABLE READ) 등급의 트랜잭션 격리 수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공.


  1. 엔티티 등록할 때 트랜잭션을 지원하는 쓰기 지연
Entity em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();

em.persist(memberA);
em.persist(memberB);
//여기까지 insert sql을 DB로 보내지 않음. 쿼리가 영속 컨텍스트 안에 있는 쓰기 지연 SQL 저장소에 보관이 되었다가 나감. 

//커밋하는 순간 DB에 insert sql을 보냄.
transaction.commit();

// 이 옵션을 persistence.xml에 넣으면 사이즈 만큼 모아서 DB에 한번에 넣기가 가능.
<property name="hibernate.jdbc.batch_size" value="10"/>

  1. 엔티티 수정, 변경 감지
  • 변경감지 기능으로 엔티티 수정이 가능함.
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) 이런  코드는 필요하지 않다. 이유는 영속 컨텍스트 안에서 스냅샷과 비교 후 변경을 감지하여 update문을 자동 생성하기 때문.

transaction.commit();

1) 커밋을 하면 내부적으로 flush() 실행 후

2) 엔티티와 스냅샷을 비교함. 1차 캐시 내에는 1차 캐시와 스냅샷(영속 컨텍스트에 들어온 당시의 값을 저장해둠)이 있음. 값을 변경하게 되면 flush()가 실행이 되고 변경된 내용과 스냅샷을 비교함.

3) 바뀐 내용을 확인하면(변경 감지) 쓰기 지연 SQL 저장소에 update문을 생성해 넣어둠.



플러시 flush

  • 영속성 컨텍스트의 변경내용을 DB에 반영
  • 영속컨텍스트의 변경 내용과 DB의 내용을 맞추는 작업.
  • 영속성 컨텍스트를 비우지않음.
  • 트랜잭션이라는 작업 단위가 중요 -> 커밋 직적에만 동기화하면됨.

플러시 발생

  • 변경 감지
  • 수정된 엔티티 쓰기 지연 SQL저장소에 등록
  • 쓰기 지연 SQL 저장소의 쿼리를 DB에 전송(등록, 수정. 삭제 쿼리) : transaction.commit() 한 경우임

영속성 컨텍스트를 플러시하는 방법

  • 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 = qeury.getResultList();

위의 코드에서 em.persist(memberA);~em.persist(memberA);를 실행하고 em.createQuery를 실행하면 아직 DB에 member들을 저장을 안해서 불러올 수가 없음. 문제가 생길 것을 방지하고자 JPQL은 쿼리 날리면 무조건 flush()를 날려버림. 그래서 A~C가 조회될 수 있음.


플러시 모드 옵션

em.setFlushMode(FlushModeType.COMMIT)
  • FlushModeType.AUTO : 커밋이나 쿼리를 실행할 때 플러시(기본값)
  • FlushModeType.COMMIT : 커밋할 때만 플러시

profile
시간이 걸릴 뿐 내가 못할 건 없다.

0개의 댓글