목차
728x90
6. JPA 내부구조
JPA에서 가장 중요한 2가지
- 객체와 관계형 데이터베이스 매핑하기(Object Relational Mapping)
- 영속성 컨텍스트
영속성 컨텍스트
엔티티 매니저 팩토리와 엔티티 매니저

- 엔티티를 영구 저장하는 환경이라는 뜻
- 영속성 컨텍스트는 논리적인 개념으로 눈에 보이지 않습니다.
- 엔티티 매니저를 통해서 영속성 컨텍스트에 접근
- J2SE 환경에서는 엔티티 매니저와 영속성 컨텍스트가 1:1
- JSEE, 스프링 프레임워크 같은 컨테이너 환경에서는 엔티티 매니저와 영속성 컨텍스트가 N:1
엔티티 생명주기
영속성 컨테스트의 장점
- 1차 캐시
- 동일성(identity) 보장
- 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
- 변경 감지 (Dirty Checking)
- 지연 로딩 (Lazy Loading)
엔티티 조회, 1차 캐시

Member
member = new Member(1L, team, "MEBER A");
// 비영속
em.persist(member);
// 영속
- 위의 사진과 같이 persist로 엔티티를 영속화하면
@Id
를 가지고 1차 캐시에서 해당 Entity를 저장해놓습니다. - 영속화되어 있는 (즉, 1차 캐시에 있는) 엔티티를 조회하면 DB에 접근하지 않고 바로 1차 캐시에서 엔티티를 가져오게 됩니다. (SELECT SQL이 나가지 않음)
Member
member = new Member(1L, team, "MEBER A");
// 비영속
em.persist(member);
// 영속
em.find(Member.class, member.getId());

- 데이터베이스에서 조회할 경우는 어떻게 될까요?

Member member2=new Member(2L,team,"MEBER B"); // 비영속
****em.persist(member); // 영속
em.flush();
em.clear();
Member member1=new Member(1L,team,"MEBER A"); // 비영속
**e**m.persist(member1); // 영속
em.find(Member.class,member1.getId()); // 1차 캐시에서 가져온다.
em.find(Member.class,member2.getId()); // DB에서 가져와 1차캐시에 저장을 한다.
영속 엔티티의 동일성 보장
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
class MemberTest {
@DisplayName("동일성 체크")
@Test
void identityTest() {
final EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
final EntityManager em = emf.createEntityManager();
final EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
final Team team = new Team(1L, "Team A");
em.persist(team);
final Member memberA = new Member(1L, team, "Member A");
em.persist(memberA);
final Member findMemberA = em.find(Member.class, memberA.getId());
assertThat(memberA).isEqualTo(findMemberA);
transaction.commit();
} catch (final Exception e) {
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
}
엔티티 등록 - 트랜잭션을 지원하는 쓰기 지연
@DisplayName("쓰기 지연 테스트")
@Test
void transactionalWriteBehindTest(){
final EntityManagerFactory emf=Persistence.createEntityManagerFactory("hello");
final EntityManager em=emf.createEntityManager();
final EntityTransaction transaction=em.getTransaction();
transaction.begin();
try{
final Team team=new Team(1L,"Team A"); // 준영속 상태
em.persist(team); // 영속 상태
System.out.println("====== SQL이 나가는가?========");
final Member memberA=new Member(1L,null,"Member A"); // 준영속 상태
em.persist(memberA); // 영속 상태
transaction.commit();
}catch(final Exception e){
transaction.rollback();
}finally{
em.close();
}
emf.close();
}
====== SQL이 나가는가?========
Hibernate:
insert
into
Team
(name, id)
values
(?, ?)
Hibernate:
insert
into
Member
(name, team_id, id)
values
(?, ?, ?)


엔티티 수정 - 변경 감지
@
DisplayName
("변경감지 테스트")
@Test
void dirtyCheckingTest() {
final EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
final
EntityManager em = emf.createEntityManager();
final
EntityTransaction transaction = em.getTransaction();
transaction.begin();
try
{
final Team team = new Team(1L, "Team A");
// 준영속 상태
em.persist(team);
// 영속 상태
final Member memberA = new Member(1L, null, "Member A");
// 준영속 상태
em.persist(memberA);
// 영속 상태
em.flush();
final
Member member = em.find(Member.class, memberA.getId());
member.setName
("Member B");
em.flush
();
final
Member findMember = em.find(Member.class, member.getId());
assertThat
(findMember.getName()).isEqualTo("Member B");
transaction.commit();
} catch (final Exception e) {
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
update
Member
set name=?,
team_id=?
where id = ?
- 따로 entityManger.update()와 같은 메소드 호출 없이 엔티티의 값이 변경되었다면 자동으로 Update Query를 실행해준다.

- JPA의 사상은 자바 컬렉션처럼 사용하는 것이기 때문에 이러한 것을 만들게 되었다.
- 스냅샷이 있어 메모리를 2배로 가지게 된다. 이걸 처리하는 옵션도 모두 존재하기 때문에 걱정할 필요는 없다.
플러시(flush)
- 영속성 컨텍스트의 변경내용을 데이터베이스에 반영
- 변경 감지(Dirty Checking)
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송
영속성 컨텍스트를 플러시 하는 방법
- entityManager.flush() - 직접 호출
- 트랜잭션 커밋 - 플러시 자동 호출
- JPQL 쿼리 실행 - 플러시 자동 호출
플러시 모드 옵션
- entityManager.setFlushMode()
플러시는!
- 영속성 컨텍스트를 비우지 않음.(비우는 것은 clear())
- 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
- 트랜잭션이라는 작업 단위가 중요 → 커밋 직전에만 동기화 하면 됨.
출처
728x90
728x90
'Lecture > [Tacademy] JPA 프로그래밍 기본기 다지기' 카테고리의 다른 글
8. Spring Data JPA와 QueryDSL 이해 (0) | 2020.12.17 |
---|---|
7. JPA 객체지향 쿼리 (0) | 2020.12.17 |
5. 양방향 매핑 (0) | 2020.12.15 |
4. 연관관계 매핑 (0) | 2020.12.15 |
3. 필드와 컬럼 매핑 (0) | 2020.12.13 |
728x90
6. JPA 내부구조
JPA에서 가장 중요한 2가지
- 객체와 관계형 데이터베이스 매핑하기(Object Relational Mapping)
- 영속성 컨텍스트
영속성 컨텍스트
엔티티 매니저 팩토리와 엔티티 매니저

- 엔티티를 영구 저장하는 환경이라는 뜻
- 영속성 컨텍스트는 논리적인 개념으로 눈에 보이지 않습니다.
- 엔티티 매니저를 통해서 영속성 컨텍스트에 접근
- J2SE 환경에서는 엔티티 매니저와 영속성 컨텍스트가 1:1
- JSEE, 스프링 프레임워크 같은 컨테이너 환경에서는 엔티티 매니저와 영속성 컨텍스트가 N:1
엔티티 생명주기
영속성 컨테스트의 장점
- 1차 캐시
- 동일성(identity) 보장
- 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
- 변경 감지 (Dirty Checking)
- 지연 로딩 (Lazy Loading)
엔티티 조회, 1차 캐시

Member
member = new Member(1L, team, "MEBER A");
// 비영속
em.persist(member);
// 영속
- 위의 사진과 같이 persist로 엔티티를 영속화하면
@Id
를 가지고 1차 캐시에서 해당 Entity를 저장해놓습니다. - 영속화되어 있는 (즉, 1차 캐시에 있는) 엔티티를 조회하면 DB에 접근하지 않고 바로 1차 캐시에서 엔티티를 가져오게 됩니다. (SELECT SQL이 나가지 않음)
Member
member = new Member(1L, team, "MEBER A");
// 비영속
em.persist(member);
// 영속
em.find(Member.class, member.getId());

- 데이터베이스에서 조회할 경우는 어떻게 될까요?

Member member2=new Member(2L,team,"MEBER B"); // 비영속
****em.persist(member); // 영속
em.flush();
em.clear();
Member member1=new Member(1L,team,"MEBER A"); // 비영속
**e**m.persist(member1); // 영속
em.find(Member.class,member1.getId()); // 1차 캐시에서 가져온다.
em.find(Member.class,member2.getId()); // DB에서 가져와 1차캐시에 저장을 한다.
영속 엔티티의 동일성 보장
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
class MemberTest {
@DisplayName("동일성 체크")
@Test
void identityTest() {
final EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
final EntityManager em = emf.createEntityManager();
final EntityTransaction transaction = em.getTransaction();
transaction.begin();
try {
final Team team = new Team(1L, "Team A");
em.persist(team);
final Member memberA = new Member(1L, team, "Member A");
em.persist(memberA);
final Member findMemberA = em.find(Member.class, memberA.getId());
assertThat(memberA).isEqualTo(findMemberA);
transaction.commit();
} catch (final Exception e) {
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
}
엔티티 등록 - 트랜잭션을 지원하는 쓰기 지연
@DisplayName("쓰기 지연 테스트")
@Test
void transactionalWriteBehindTest(){
final EntityManagerFactory emf=Persistence.createEntityManagerFactory("hello");
final EntityManager em=emf.createEntityManager();
final EntityTransaction transaction=em.getTransaction();
transaction.begin();
try{
final Team team=new Team(1L,"Team A"); // 준영속 상태
em.persist(team); // 영속 상태
System.out.println("====== SQL이 나가는가?========");
final Member memberA=new Member(1L,null,"Member A"); // 준영속 상태
em.persist(memberA); // 영속 상태
transaction.commit();
}catch(final Exception e){
transaction.rollback();
}finally{
em.close();
}
emf.close();
}
====== SQL이 나가는가?========
Hibernate:
insert
into
Team
(name, id)
values
(?, ?)
Hibernate:
insert
into
Member
(name, team_id, id)
values
(?, ?, ?)


엔티티 수정 - 변경 감지
@
DisplayName
("변경감지 테스트")
@Test
void dirtyCheckingTest() {
final EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
final
EntityManager em = emf.createEntityManager();
final
EntityTransaction transaction = em.getTransaction();
transaction.begin();
try
{
final Team team = new Team(1L, "Team A");
// 준영속 상태
em.persist(team);
// 영속 상태
final Member memberA = new Member(1L, null, "Member A");
// 준영속 상태
em.persist(memberA);
// 영속 상태
em.flush();
final
Member member = em.find(Member.class, memberA.getId());
member.setName
("Member B");
em.flush
();
final
Member findMember = em.find(Member.class, member.getId());
assertThat
(findMember.getName()).isEqualTo("Member B");
transaction.commit();
} catch (final Exception e) {
transaction.rollback();
} finally {
em.close();
}
emf.close();
}
update
Member
set name=?,
team_id=?
where id = ?
- 따로 entityManger.update()와 같은 메소드 호출 없이 엔티티의 값이 변경되었다면 자동으로 Update Query를 실행해준다.

- JPA의 사상은 자바 컬렉션처럼 사용하는 것이기 때문에 이러한 것을 만들게 되었다.
- 스냅샷이 있어 메모리를 2배로 가지게 된다. 이걸 처리하는 옵션도 모두 존재하기 때문에 걱정할 필요는 없다.
플러시(flush)
- 영속성 컨텍스트의 변경내용을 데이터베이스에 반영
- 변경 감지(Dirty Checking)
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송
영속성 컨텍스트를 플러시 하는 방법
- entityManager.flush() - 직접 호출
- 트랜잭션 커밋 - 플러시 자동 호출
- JPQL 쿼리 실행 - 플러시 자동 호출
플러시 모드 옵션
- entityManager.setFlushMode()
플러시는!
- 영속성 컨텍스트를 비우지 않음.(비우는 것은 clear())
- 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화
- 트랜잭션이라는 작업 단위가 중요 → 커밋 직전에만 동기화 하면 됨.
출처
728x90
728x90
'Lecture > [Tacademy] JPA 프로그래밍 기본기 다지기' 카테고리의 다른 글
8. Spring Data JPA와 QueryDSL 이해 (0) | 2020.12.17 |
---|---|
7. JPA 객체지향 쿼리 (0) | 2020.12.17 |
5. 양방향 매핑 (0) | 2020.12.15 |
4. 연관관계 매핑 (0) | 2020.12.15 |
3. 필드와 컬럼 매핑 (0) | 2020.12.13 |