영속성 컨택스트 - 내부 동작 방식
✅영속성 컨텍스트
JPA에서 가장 중요한 2가지
- 객체와 관계형 데이터베이스 매핑하기 (설계와 관련)
- 영속성 컨택스트 (실제 JPA가 내부에서 동작하는지 이해하기)
고객의 요청이 들어올 때마다
EntityManagerFactory
를 통해EntityManager
를 생성한다.
EntityManager
는 내부적으로 DatabaseConnection을 통해 DB를 사용한다.
📢영속성 컨택스트란 무엇인가❓
영속성 컨택스트
: 엔티티를 영구 저장하는 환경 이라는 의미이다.
EntityManager.persist(entity);
의 의미 : DB에 엔티티를 저장한다는게 아니라 영속성 컨택스트에 엔티티를 영속화(저장)한다는 의미이다.
📢영속성 컨택스트는 논리적인 개념으로 눈에 보이지 않는다.
영속성 컨택스트에 접근하기 위해 EntityManager
를 통해야한다.
J2SE 환경에서
EntityManager
를 생성하면 1대1 관계의PersistenceContext
(영속성 컨택스트) 가 생성된다.
👉 쉽게 말해EntityManager
안에 눈에 보이지 않은 공간인PersistenceContext
이 존재한다.
📢엔티티의 생명주기
비영속
(new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태 (객체를 처음 생성한 상태)영속
(managed) : 영속성 컨텍스트에 관리되는 상태 (EntityManager.persist(entity)
이후의 상태)준영속
(detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태삭제
(removed) : 삭제된 상태
✍비영속
member
객체만 생성한 상태 = JPA와 무관한 상태
✍영속
member
객체를 생성한 뒤,
EntityManager
를 생성하여em.persist(member);
코드를 실행하면
👉EntityManager
속영속성 컨택스트
안에member
객체가 들어가서 영속 상태가 된다.
package hellojpa;
import javax.persistence.*;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
// <persistence-unit name="hello"> 의 name 속성을 createEntityManagerFactory 의 파라미터로 전달한다.
// EntityManagerFactory는 어플리케이션 로딩 시점에 오직 1개만 만들어야 한다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// EntityManager는 쓰레드간 공유해선 안된다. 사용하고 버릴 것.
EntityManager em = emf.createEntityManager();
// JPA의 모든 데이터 변경은 트랜잭션 안에서 실행되어야 한다.
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
// 비영속 상태
Member member = new Member();
member.setId(1L);
member.setName("HelloJPA");
// 영속 상태 (이 때 DB에 저장되지 않음)
System.out.println("=== BEFORE ===");
em.persist(member);
System.out.println("=== AFTER ===");
tx.commit();
} catch (Exception e) {
// 비정상 종료시 rollback
tx.rollback();
} finally {
// EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
em.close();
}
// 어플리케이션이 끝나면 EntityManagerFactory를 닫는다다
emf.close();
}
}
=== BEFORE ===
와=== AFTER ===
사이에 쿼리가 생성되지 않는다.
👉영속 상태
가 된다고 해서 바로 DB에 쿼리가 날라가진 않는다.
👉tx.commit();
코드가 실행되는 시점에 DB에 쿼리가 날라간다.
✍준영속, 삭제
준영속 상태 👉 detach
실제 DB 삭제를 요청하는 상태
📢영속성 컨택스트의 이점
- 1차 캐시
- 동일성(identity) 보장
- 트랜잭션을 지원하는 쓰기 지연 (transactional write-behind)
- 변경 감지(Dirty Checking)
- 지연 로딩(Lazy Loading)
✍1차 캐시
member객체를 생성하고 persist하면
영속성 컨택스트 내부의1차 캐시
에 member의 pk가key
, member객체가value
가 되어 담긴다.
em.find(Member.class, "member1");
으로 member를 조회하면
JPA는 DB에서 찾기 전에1차 캐시
에서 pk가 member1인 member객체를 조회한다.
em.find(Member.class, "member2");
으로 member를 조회하면
find("member2")
으로1차 캐시
에서 member를 찾지만 없다.
👉 그럼 DB에서 조회하여 member2 객체를 1차 캐시에 저장한다.
👉 그리고 member2를 반환한다.
이후에 member2를 다시 조회하면1차 캐시
에 있는 member2 객체가 반환된다.
package hellojpa;
import javax.persistence.*;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
// <persistence-unit name="hello"> 의 name 속성을 createEntityManagerFactory 의 파라미터로 전달한다.
// EntityManagerFactory는 어플리케이션 로딩 시점에 오직 1개만 만들어야 한다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// EntityManager는 쓰레드간 공유해선 안된다. 사용하고 버릴 것.
EntityManager em = emf.createEntityManager();
// JPA의 모든 데이터 변경은 트랜잭션 안에서 실행되어야 한다.
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
// 비영속 상태
Member member = new Member();
member.setId(101L);
member.setName("HelloJPA");
// 영속 상태 (이 때 DB에 저장되지 않음)
System.out.println("=== BEFORE ===");
// 1차 캐시에 "101" : "member" 가 저장된다
em.persist(member);
System.out.println("=== AFTER ===");
// find 했지만 SELECT 쿼리가 DB로 날아가지 않는다.
// 1차 캐시에서 pk = 101에 해당하는 member를 조회하여 findMember에 넣는다.
Member findMember = em.find(Member.class, 101L);
System.out.println("findMember.id = " + findMember.getId());
System.out.println("findMember.name = " + findMember.getName());
tx.commit();
} catch (Exception e) {
// 비정상 종료시 rollback
tx.rollback();
} finally {
// EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
em.close();
}
// 어플리케이션이 끝나면 EntityManagerFactory를 닫는다다
emf.close();
}
}
select query가 나가지 않았는데 member 객체를 조회할 수 있다.
em.persist(member);
에서1차 캐시
에 "key=101" : "value=member" 가 저장된다
em.find(Member.class, 101L);
코드가 실행되면 DB에서 조회하기 전에1차 캐시
에서 key가 101인 member객체를 조회한다.
package hellojpa;
import javax.persistence.*;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
// <persistence-unit name="hello"> 의 name 속성을 createEntityManagerFactory 의 파라미터로 전달한다.
// EntityManagerFactory는 어플리케이션 로딩 시점에 오직 1개만 만들어야 한다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// EntityManager는 쓰레드간 공유해선 안된다. 사용하고 버릴 것.
EntityManager em = emf.createEntityManager();
// JPA의 모든 데이터 변경은 트랜잭션 안에서 실행되어야 한다.
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
System.out.println("=== BEFORE findMember1 ===");
Member findMember1 = em.find(Member.class, 101L);
System.out.println("=== AFTER findMember1 ===");
System.out.println("=== BEFORE findMember2 ===");
Member findMember2 = em.find(Member.class, 101L);
System.out.println("=== AFTER findMember2 ===");
tx.commit();
} catch (Exception e) {
// 비정상 종료시 rollback
tx.rollback();
} finally {
// EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
em.close();
}
// 어플리케이션이 끝나면 EntityManagerFactory를 닫는다다
emf.close();
}
}
Member findMember1 = em.find(Member.class, 101L);
을 실행하면 SELECT SQL을 통해 DB에서 pk=101의 member를 영속성 컨택스트에 올려놓는다.
Member findMember2 = em.find(Member.class, 101L);
을 실행하면 영속성 컨택스트 안의1차 캐시
에서 pk=101인 member를 조회하기 때문에 Select 쿼리가 나가지 않는다.
1차 캐시
는 하나의 트랜잭션 안에서만 활용할 수 있다. (1차 캐시 만으로 큰 성능 향상을 기대하기 어렵다)
✍영속 엔티티의 동일성 보장
1차 캐시
로 인해 영속 엔티티의 동일성이 보장된다.
✍트랜잭션을 지원하는 쓰기 지연
JPA는 commit할 때 INSERT SQL를 보낸다.
영속성 컨택스트 안에는
쓰기 지연 SQL 저장소
가 존재한다.
em.persist(memberA);
로 memberA를 영속성 컨택스트에 넣으면1차 캐시
에 memberA가 들어감과 동시에
memberA 엔티티를 분석하여 INSERT SQL을 생성한 뒤 이를쓰기 지연 SQL 저장소
에 쌓아둔다.
em.persist(memberA);
의 경우에도1차 캐시
에 넣고, INSERT SQL을쓰기 지연 SQL 저장소
에 차곡 차곡 쌓아둔다.
transaction.commit();
하는 시점에쓰기 지연 SQL 저장소
에 쌓인 SQL문들이 한꺼번에 날아간다 (flush)
package hellojpa;
import javax.persistence.*;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
// <persistence-unit name="hello"> 의 name 속성을 createEntityManagerFactory 의 파라미터로 전달한다.
// EntityManagerFactory는 어플리케이션 로딩 시점에 오직 1개만 만들어야 한다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// EntityManager는 쓰레드간 공유해선 안된다. 사용하고 버릴 것.
EntityManager em = emf.createEntityManager();
// JPA의 모든 데이터 변경은 트랜잭션 안에서 실행되어야 한다.
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member member1 = new Member(150L, "A");
Member member2 = new Member(160L, "B");
// 쓰기 지연 SQL 저장소에 INSERT SQL이 쌓인다.
em.persist(member1);
em.persist(member2);
System.out.println("=== BEFORE commit ===");
// commit 시점에 쓰기 지연 SQL 저장소에 쌓인 SQL문들이 날아간다.
tx.commit();
System.out.println("=== AFTER commit ===");
} catch (Exception e) {
// 비정상 종료시 rollback
tx.rollback();
} finally {
// EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
em.close();
}
// 어플리케이션이 끝나면 EntityManagerFactory를 닫는다다
emf.close();
}
}
commit 시점에 쓰기 지연 SQL 저장소에 쌓인 SQL문들이 날아간다.
✍변경 감지 (Dirty Checking) : 엔티티 수정
JPA는
더티 체킹 (변경 감지)
를 통해 DB의 값이 변경된다.
HOW ❓ 👉 영속성 컨택스트안에 비밀이 숨어있다.
tx.commit();
하면 내부적으로flush
가 실행된다
👉 엔티티와스냅샷
을 비교하여 변경된 데이터를 감지한다. (스냅샷 : 엔티티 값을 읽어온 최초 시점의 엔티티의 데이터)
👉 데이터가 변경되었다면 UPDATE SQL을쓰기 지연 SQL 저장소
에 쌓아둔다
👉쓰기 지연 SQL 저장소
의 UPDATE SQL을 DB에 반영하고
👉 commit한다.
이 과정이변경 감지 (더티 체킹)
이다.
package hellojpa;
import javax.persistence.*;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
// <persistence-unit name="hello"> 의 name 속성을 createEntityManagerFactory 의 파라미터로 전달한다.
// EntityManagerFactory는 어플리케이션 로딩 시점에 오직 1개만 만들어야 한다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// EntityManager는 쓰레드간 공유해선 안된다. 사용하고 버릴 것.
EntityManager em = emf.createEntityManager();
// JPA의 모든 데이터 변경은 트랜잭션 안에서 실행되어야 한다.
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member member = em.find(Member.class, 150L);
member.setName("ZZZZ");
// em.persist(member); -> 이걸 해줄 필요가 없다!!
System.out.println("=== BEFORE commit ===");
// commit 시점에 쓰기 지연 SQL 저장소에 쌓인 SQL문들이 날아간다.
tx.commit();
} catch (Exception e) {
// 비정상 종료시 rollback
tx.rollback();
} finally {
// EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
em.close();
}
// 어플리케이션이 끝나면 EntityManagerFactory를 닫는다다
emf.close();
}
}
member.setName("ZZZZ");
으로 member의 값만 변경했을 뿐인데 UPDATE SQL이 날아갔다.
게다가em.persist(member)
코드를 실행하지 않았음에도 불구하고
👉 Java Collection을 다루듯 값만 변경했는데도 UPDATE SQL이 나간다.
✍엔티티 삭제
em.remove(memberA);
하면 DELETE SQL이 나간다.
✅ 플러시
플러시란 영속성 컨택스트의 변경 내용을 데이터베이스 반영하는 것이다.
다시 말해, 영속성 컨택스트의 Query들을 데이터베이스에 날리는 것
- 트랜잭션이 commit되면 자동적으로 플러시가 발생한다.
📢플러시가 발생한 뒤 생기는 일
변경 감지 (Dirty Checking)
이 일어난다
- 수정된 엔티티를
쓰기 지연 SQL 저장소
에 등록한다
쓰기 지연 SQL 저장소
의 등록, 수정, 삭제 Query를 데이터베이스에 전송한다.
플러시란 영속성 컨택스트의 변경 내용을 데이터베이스 반영하는 것이다.
다시 말해, 영속성 컨택스트의 Query들을 데이터베이스에 날리는 것
- 트랜잭션이 commit되면 자동적으로 플러시가 발생한다.
변경 감지 (Dirty Checking)
이 일어난다- 수정된 엔티티를
쓰기 지연 SQL 저장소
에 등록한다 쓰기 지연 SQL 저장소
의 등록, 수정, 삭제 Query를 데이터베이스에 전송한다.
👉 플러시를 해도 영속성 컨택스트의 1차 캐시가 지워지지 않는다.
다만 쓰기 지연 SQL 저장소
에 담긴 쿼리가 데이터베이스에 반영될 뿐이다.
📢영속성 컨택스트를 플러시하는 방법
em.flush()
를 직접 호출한다. (사실상 직접 호출할 일이 없다)- 트랜잭션을 commit하면 자동으로 플러시가 호출된다.
- JPQL Query를 실행하면 자동으로 플러시가 호출된다.
✍ em.flush() 호출
package hellojpa;
import javax.persistence.*;
public class JpaMain {
public static void main(String[] args) {
// <persistence-unit name="hello"> 의 name 속성을 createEntityManagerFactory 의 파라미터로 전달한다.
// EntityManagerFactory는 어플리케이션 로딩 시점에 오직 1개만 만들어야 한다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// EntityManager는 쓰레드간 공유해선 안된다. 사용하고 버릴 것.
EntityManager em = emf.createEntityManager();
// JPA의 모든 데이터 변경은 트랜잭션 안에서 실행되어야 한다.
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
Member member = new Member(200L, "member200");
em.persist(member);
em.flush();
System.out.println("=== BEFORE commit ===");
// commit 시점에 쓰기 지연 SQL 저장소에 쌓인 SQL문들이 날아간다.
tx.commit();
} catch (Exception e) {
// 비정상 종료시 rollback
tx.rollback();
} finally {
// EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
em.close();
}
// 어플리케이션이 끝나면 EntityManagerFactory를 닫는다
emf.close();
}
}
em.persist(member);
한 시점에 쿼리가쓰기 지연 SQL 저장소
에 담겨있다.
em.flush()
하는 순간 쿼리가 DB에 바로 반영된다.
✍ JPQL 쿼리 실행
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
// 영속성 컨택스트에 memberA, B, C가 저장된다.
// 이 때까지 데이터베이스에 날아가는 쿼리가 없다.
//중간에 JPQL 실행
query = em.createQuery("select m from Member m", Member.class);
List<Member> members= query.getResultList();
- 일단 JPQL의 쿼리를 실행하기 위해서 데이터베이스에 원하는 정보가 있어야 한다.
createQuery
실행 전에 데이터베이스에 INSERT Query가 나가지 않았다 (데이터베이스에 원하는 정보가 저장되지 않은 상태)
👉 JPA는 JPQL 쿼리를 실행하기 전에 무조건 플러시를 호출하여 JPQL 쿼리가 실행되기 전쓰기 지연 SQL 저장소
의 쿼리들을 데이터베이스에 날린다..
📢플러시 모드 옵션
FlushModeType.AUTO
: 커밋이나 쿼리를 실행할 때 플러시 (기본값, 권장)FlushModeType.COMMIT
: 커밋할 때만 플러시 (쿼리를 실행할 때는 플러시를 실행하지 않는다)
- JPQL 쿼리 실행시 플러시가 자동 호출되는 것을 막을 수 있다.
📢플러시 정리
- 영속성 컨텍스트를 비우지 않는다.
- 영속성 컨텍스트의 변경내용을 데이터베이스에 동기화한다.
- 트랜잭션이라는 작업 단위가 중요 👉 커밋 직전에만 동기화 하면 된다.
✅ 준영속 상태
- 영속 상태 :
persist로 영속성 컨택스트에 저장한 상태
, find 하였는데 1차 캐시에 없어서 DB에 조회한 후 1차 캐시에 저장한 상태
- 준영속 상태 :
영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)된 것
- 영속성 컨택스트가 제공하는 기능(변경 감지 등)을 사용하지 못한다.
📢준영속 상태로 만드는 방법
em.detach(entity)
: 특정 엔티티만 준영속 상태로 전환
em.clear()
: 영속성 컨텍스트를 완전히 초기화
em.close()
: 영속성 컨텍스트를 종료
✍ em.detach(entity)
package hellojpa;
import javax.persistence.*;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
// <persistence-unit name="hello"> 의 name 속성을 createEntityManagerFactory 의 파라미터로 전달한다.
// EntityManagerFactory는 어플리케이션 로딩 시점에 오직 1개만 만들어야 한다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// EntityManager는 쓰레드간 공유해선 안된다. 사용하고 버릴 것.
EntityManager em = emf.createEntityManager();
// JPA의 모든 데이터 변경은 트랜잭션 안에서 실행되어야 한다.
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
// 150L의 member는 영속상태가 된다.
Member member = em.find(Member.class, 150L);
// 값이 변경되어 Dirty Checking 이 일어난다.
member.setName("AAAAA");
// member는 영속 상태에서 준영속 상태로 전환된다.
// JPA에서 더이상 관리하지 않기 때문에 트랜잭션 commit 시에도 아무 일도 일어나지 않는다.
em.detach(member);
System.out.println("=== BEFORE commit ===");
// commit 시점에 쓰기 지연 SQL 저장소에 쌓인 SQL문들이 날아간다.
tx.commit();
} catch (Exception e) {
// 비정상 종료시 rollback
tx.rollback();
} finally {
// EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
em.close();
}
// 어플리케이션이 끝나면 EntityManagerFactory를 닫는다
emf.close();
}
}
- 영속 상태 :
persist로 영속성 컨택스트에 저장한 상태
,find 하였는데 1차 캐시에 없어서 DB에 조회한 후 1차 캐시에 저장한 상태
- 준영속 상태 :
영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)된 것
- 영속성 컨택스트가 제공하는 기능(변경 감지 등)을 사용하지 못한다.
em.detach(entity)
: 특정 엔티티만 준영속 상태로 전환em.clear()
: 영속성 컨텍스트를 완전히 초기화em.close()
: 영속성 컨텍스트를 종료package hellojpa;
import javax.persistence.*;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
// <persistence-unit name="hello"> 의 name 속성을 createEntityManagerFactory 의 파라미터로 전달한다.
// EntityManagerFactory는 어플리케이션 로딩 시점에 오직 1개만 만들어야 한다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// EntityManager는 쓰레드간 공유해선 안된다. 사용하고 버릴 것.
EntityManager em = emf.createEntityManager();
// JPA의 모든 데이터 변경은 트랜잭션 안에서 실행되어야 한다.
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
// 150L의 member는 영속상태가 된다.
Member member = em.find(Member.class, 150L);
// 값이 변경되어 Dirty Checking 이 일어난다.
member.setName("AAAAA");
// member는 영속 상태에서 준영속 상태로 전환된다.
// JPA에서 더이상 관리하지 않기 때문에 트랜잭션 commit 시에도 아무 일도 일어나지 않는다.
em.detach(member);
System.out.println("=== BEFORE commit ===");
// commit 시점에 쓰기 지연 SQL 저장소에 쌓인 SQL문들이 날아간다.
tx.commit();
} catch (Exception e) {
// 비정상 종료시 rollback
tx.rollback();
} finally {
// EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
em.close();
}
// 어플리케이션이 끝나면 EntityManagerFactory를 닫는다
emf.close();
}
}
find
에 의해 select query가 나간 뒤, 영속성 컨택스트 속 1차 캐시에 엔티티가 저장된다.em.detach(member)
로 인해 준영속 상태가 된 member객체는setName("AAAAA")
로 값을 변경했지만 update query는 나가지 않는다. (JPA가 더 이상 관리하지 않기 때문)
👉 member와 관련된 모든 내용이 영속성 컨택스트에서 빠지게 된다.
✍ em.clear()
package hellojpa;
import javax.persistence.*;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
// <persistence-unit name="hello"> 의 name 속성을 createEntityManagerFactory 의 파라미터로 전달한다.
// EntityManagerFactory는 어플리케이션 로딩 시점에 오직 1개만 만들어야 한다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// EntityManager는 쓰레드간 공유해선 안된다. 사용하고 버릴 것.
EntityManager em = emf.createEntityManager();
// JPA의 모든 데이터 변경은 트랜잭션 안에서 실행되어야 한다.
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
// 150L의 member는 영속상태가 된다.
Member member = em.find(Member.class, 150L);
// 값이 변경되어 Dirty Checking 이 일어난다.
member.setName("AAAAA");
// EntityManager 내의 영속성 컨택스트 전체가 지워진다.
em.clear();
System.out.println("=== AFTER em.clear() ===");
// 150L의 member를 다시 조회한다.
// 영속성 컨택스트에 member2가 없기 때문에 1차 캐시에 저장된다.
Member member2 = em.find(Member.class, 150L);
System.out.println("=== BEFORE commit ===");
// commit 시점에 쓰기 지연 SQL 저장소에 쌓인 SQL문들이 날아간다.
tx.commit();
} catch (Exception e) {
// 비정상 종료시 rollback
tx.rollback();
} finally {
// EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
em.close();
}
// 어플리케이션이 끝나면 EntityManagerFactory를 닫는다
emf.close();
}
}
✍em.close()
package hellojpa;
import javax.persistence.*;
import java.util.List;
public class JpaMain {
public static void main(String[] args) {
// <persistence-unit name="hello"> 의 name 속성을 createEntityManagerFactory 의 파라미터로 전달한다.
// EntityManagerFactory는 어플리케이션 로딩 시점에 오직 1개만 만들어야 한다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// EntityManager는 쓰레드간 공유해선 안된다. 사용하고 버릴 것.
EntityManager em = emf.createEntityManager();
// JPA의 모든 데이터 변경은 트랜잭션 안에서 실행되어야 한다.
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
// 150L의 member는 영속상태가 된다.
Member member = em.find(Member.class, 150L);
// 값이 변경되어 Dirty Checking 이 일어난다.
member.setName("AAAAA");
// EntityManager 내의 영속성 컨택스트 전체가 지워진다.
em.close();
System.out.println("=== AFTER em.close() ===");
// 150L의 member를 다시 조회해도 영속성 컨택스트가 닫혀서 저장할 곳이 없다.
Member member2 = em.find(Member.class, 150L);
System.out.println("=== BEFORE commit ===");
tx.commit();
} catch (Exception e) {
// 비정상 종료시 rollback
tx.rollback();
} finally {
// EntityManager는 쓰레드간 공유해선 안되므로 사용을 다하면 꼭 닫아줘야 한다.
em.close();
}
// 어플리케이션이 끝나면 EntityManagerFactory를 닫는다
emf.close();
}
}
em.close()
시점 이후 try 구문 내 코드가 실행되지 않는다.
Author And Source
이 문제에 관하여(영속성 컨택스트 - 내부 동작 방식), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@guswns3371/영속성-컨택스트저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)