자바 ORM 표준 JPA 프로그래밍 : 교보문고
자바 ORM 표준 JPA 프로그래밍 - 기본편 : 인프런
1차 cache 그림 1
1차 cache 그림 2
이전 포스팅대로 프로젝트 설정까지 마무리 후 새로운 자바 파일을 만들고
main method 를 정의해서 다음과 같이 작성했다.EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello"); EntityManager entityManager = entityManagerFactory.createEntityManager(); EntityTransaction tx = entityManager.getTransaction(); tx.begin();
해당 main method 를 동작시키면 어떤 일이 일어날까
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
항상 적을때마다 뭔가 애매한 영속성 context 이다. 애매하다고 한 이유는
다 영어로 적어야할지, 영속성 Context 이렇게 적어야할지 전부 한글로 적을지 참 애매하다.
해당 강의 내용과 책 내용을 보며 정리한 내용이다.
무지성 JPA 사용 중에도 자주 듣던 말이다. 영속성 컨텍스트 1차 캐시니 뭐니 하는 말들을 들으며 알아보는 것들을 귀찮아한 내 자신이 한탄스럽다. 알고보면 이해 못 할것도 아닌데 말이다.
Entity 를 영구 저장하는 환경
이름 | 특징 |
---|---|
비영속 | Entity 로 선언된 객체가 새로 생성되는 경우 |
영속 | Persistence Context 에서 관리되는 상태 |
준영속 | 영속성 Context 에서 분리된 상태 |
삭제 | 말 그대로 삭제된 상태 |
@Entity
// Reflection 등을 JPA 에서 사용하기 때문에 기본 생성자가 존재해야한다.
public class Member
{
public Member(Long id, String name)
{
this.id = id;
this.name = name;
}
public Member(){}
@Id
private Long id;
private String name;
public Long getId(){ return this.id;}
public String getName(){return this.name;}
public void setId(Long id){this.id = id;}
public void setName(String name){this.name = name;}
}
---------------------------------------------
public static void main(String[] args)
{
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
try
{
// 비영속 상태
Member member = new Member();
member.setId(10L);
member.setName("Persist Context");
System.out.println(" Before Invoke Persistence ");
// 영속 상태
entityManager.persist(member);
System.out.println(" After Invoke Persistence ");
tx.commit();
}
catch (Exception e)
{
tx.rollback();
}
finally
{
entityManager.close();
entityManagerFactory.close();
}
}
영속 상태가 되면 바로 DB 에 저장이 될까? 그렇지 않았다.
만약 Entity 가 영속 상태가 됐을 때 DB 에 저장이 되면
Before Invoke Persistence 가 선언 된 이후
Insert Query 문이 작성되고 After Invoke Persistence
이 작성되어야 할 것이다.
실행시켜보자
콘솔에 내가 정의한 문자열이 모두 출력 되고 나서 Query 문이 발생하여 DB 에 데이터가 저장이 됐다.
persist 로 인해 영속성에서 관리된다고 DB 에 저장되는 것이 아닌
tx.commit();
해당 트랜잭션이 종료 될 때 DB에 적용이 된다.
DB 와 Application 사이의 중간계층 역할을 수행한다
SUMMARY
영속성 Context 는 Entity 를 영구적으로 저장하는 환경으로
트랜잭션이 종료된 이후 DB 에 적용할 수 있도록 DB 와 Application 중간 계층 역할을 수행한다.
위와 같이 동작하기 위해서 JPA 의 Persistence Context 에는 1차 Cache 라는 개념이 존재한다.
Entity 가 영속성 컨텍스트에서 엔티티를 관리하는 장소.
Entity 를 정의할 때 @Id 를 key 값으로 엔티티를 저장한다.
동일한 Transaction 에 동일한 1차 Cache를 갖는다.
public static void main(String[] args)
{
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
try
{
Member member = new Member();
member.setId(20L);
member.setName("Long20");
System.out.println(":: BEFORE :: ");
entityManager.persist(member);
System.out.println(":: AFTER :: ");
Member findLong20 = entityManager.find(Member.class, 20L);
System.out.println("findLong20.getId() : "+findLong20.getId());
System.out.println("findLong20.getName() : "+findLong20.getName());
tx.commit();
}
catch (Exception e)
{
tx.rollback();
}
finally
{
entityManager.close(); // entityManger 닫기
entityManagerFactory.close(); // Entity Manager Factory 닫기
}
}
:: BEFORE ::
:: AFTER ::
findLong20.getId() : 20
findLong20.getName() : Long20
Hibernate:
/* insert org.example.javamain.Member
*/ insert
into
Member
(name, id)
values
(?, ?)
동일한 트랜잭션내에 일어나고 commit 이전에 조회를 시도 하였다.
영속성 컨텍스트의 1차 캐시에 해당정보가 존재하여 select query 문이 발생하지 않았다.
위와 같은 경우 조회시 동일한 instance 를 찾기 때문에 동일한 객체에 대한 동일성을 보장 받을 수 있다.
public static void main(String[] args)
{
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
try
{
Member member31 = new Member(31L, "Long31");
entityManager.persist(member31);
Member member1 = entityManager.find(Member.class, 31L);
Member member2 = entityManager.find(Member.class, 31L);
if(member1.equals(member2))
{ System.out.println("SAME"); }
else
{ System.out.println("NOT SAME"); }
tx.commit();
}
catch (Exception e)
{ tx.rollback(); }
finally
{
entityManager.close(); // entityManger 닫기
entityManagerFactory.close(); // Entity Manager Factory 닫기
}
}
SAME
Hibernate:
/* insert org.example.javamain.Member
*/ insert
into
Member
(name, id)
values
(?, ?)
public static void main(String[] args)
{
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
try
{
Member findMemberNumber31 = entityManager.find(Member.class, 31L);
findMemberNumber31.setName("롱삼십일");
System.out.println("QUERY :::::::::::::::::::::::::::::: ");
tx.commit();
}
catch (Exception e)
{
tx.rollback();
}
finally
{
entityManager.close(); // entityManger 닫기
entityManagerFactory.close(); // Entity Manager Factory 닫기
}
}
Hibernate:
select
member0_.id as id1_0_0_,
member0_.name as name2_0_0_
from
Member member0_
where
member0_.id=?
QUERY ::::::::::::::::::::::::::::::
Hibernate:
/* update
org.example.javamain.Member */ update
Member
set
name=?
where
id=?
Member findMemberNumber31 = entityManager.find(Member.class, 31L);
findMemberNumber31.setName("롱삼십일");
영속성 컨텍스트의 1차 Cache 에 DB 에서 불러온 데이터를 저장하고
저장한 데이터를 변경하고 commit 을 진행하니 DB 에 데이터가 변경이 되었다.
update 를 선언하지 않았는데 UPDATE 쿼리가 생성되어 DB 에 적용이 되었다!
1차 cache 에 Entity 정보를 최초로 저장할 때 스냅샷을 생성한다.
이후 Commit 할 때 Entity 와 스냅샷의 데이터를 비교하여 변경된 경우 Update Query를 생성하여
DB 에 적용시킨다.
Entity Manager 가 1차 Cache 에 저장된 객체가 변경된 것을 자동으로 감지하여
DB 에 반영하는 것을 말한다
영속성 컨텍스트는 RDB 와 Application 간 중간 계층 역할을 수행하고 있다.
Transaction 이 커밋 되는 시점에 DB 에 데이터 조작이 발생하며
이를 이용하여 Buffer 처럼 사용할 수 있다.
public static void main(String[] args)
{
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("hello");
EntityManager entityManager = entityManagerFactory.createEntityManager();
EntityTransaction tx = entityManager.getTransaction();
tx.begin();
try
{
Member member31 = new Member(31L, "Long31");
Member member32 = new Member(32L, "Long32");
entityManager.persist(member31);
entityManager.persist(member32);
System.out.println("QUERY :::::::::::::::::::::::::::::: ");
// Commit 시점에 DB 로 쿼리가 발생
tx.commit();
}
catch (Exception e)
{
tx.rollback();
}
finally
{
entityManager.close(); // entityManger 닫기
entityManagerFactory.close(); // Entity Manager Factory 닫기
}
}
QUERY ::::::::::::::::::::::::::::::
Hibernate:
/* insert org.example.javamain.Member
*/ insert
into
Member
(name, id)
values
(?, ?)
Hibernate:
/* insert org.example.javamain.Member
*/ insert
into
Member
(name, id)
values
(?, ?)
persist 호출 이후에 Query 가 바로 실행 되지 않고
Commit 시점에 Query가 발생한 것을 볼 수 있다.
- entityManger 의 persist method 를 통해서 영속성 컨텍스트를 통해 객체가 관리된다.
- 객체가 관리 될 때 1차 Cache 에 저장이 되고
- 저장이 될 때 쓰기 지연을 위한 Query 가 생성이 되고 저장해둔다
- 저장된 Query 는 Transaction Commit 작동하며 DB 에 적용이 된다.
- persistence.xml 에서 hibernate.jdbc.batch_size속성을 추가하여 배치 처럼 일정 수의 쿼리가 채워지면 DB Commit 할 수 있도록 설정도 가능하다
- JPA 의 persistence context 는 Entity 를 영구적으로 저장하는 환경을 의미한다.
- Entity Manager 의 persist 메소드를 통해서 Entity 가 영속화 되고 1차 Cache 에 저장이 된다.
- 저장이 될때 해당 객체에 대한 Query 가 작성되어 따로 저장해둔다.
- Commit 시점에 DB 에 적용이 됨으로 쓰기 지연 기능을 이용하여 Buffer 처럼 사용이 가능하다.
- 조회 시 1차 Cache 먼저 조회를 하여 성능상의 약간의 이점과 객체지향 처럼 사용할 수 있다.- 1차 Cache 에 Entity 가 저장되며 최초 상태를 Snap Shot 으로 저장한다.
- Dirty Checking 사용 하여 영속화된 객체에 변경을 감지하고 자동으로 Update Query 를 생성한다.