JPA 중요 2가지
- 객체 & 관계형 DB 매핑(ORM) - 정적
- 영속성 컨텍스트 - 내부 동작 관련
Entity Manager Factory & Entity Manager
영속성 컨텍스트
EntityManager.persist(entitiy)
.persist()
는 DB에 저장하는 것이 아닌 영속성 컨텍스트에 저장하는 것!엔티티 생명주기
비영속(new/transient) : 영속성 컨텍스트와 전혀 관계가 없는 새로운 상태
//객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
영속(managed) : 영속성 컨텍스트에 관리되는 상태
//객체를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername(“회원1”);
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
//객체를 저장한 상태(영속)
em.persist(member);
준영속(detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
//회원 엔티티를 영속성 컨텍스트에서 분리
em.detach(member);
삭제(removed) : 삭제된 상태
//객체를 삭제한 상태(db 삭제)
em.remove(memeber);
영속성 컨텍스트 장점
1) 1차 캐시
//엔티티를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//엔티티를 영속
em.persist(member);
조회 시, 먼저 DB에서 찾는게 아니라 1차 캐시에서 조회.
//1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");
그 다음 조회할 때? (있으면 1차 캐시, 없으면 DB)
Member findMember2 = em.find(Member.class, "member2");
2) 동일성(identity) 보장
영속 엔티티 동일성 보장
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 true
3) 트랜잭션을 지원하는 쓰기 지연(Transactional write-behind)
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA); //1)
em.persist(memberB); //2)
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//Just, 영속성 컨텍스트 저장
//트랜잭션 커밋 시점, 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋 3)
SQL(INSERT A) 쓰기 지연 저장
SQL(INSERT B) 쓰기 지연 저장
DB 반영
예제)
package hellojpa;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
try {
//비영속
Member member1 = new Member(150L, "A");
Member member2 = new Member(100L, "B");
//영속 - DB 저장 x
em.persist(member1);
em.persist(member2);
System.out.println("==========query position line=========");
//DB에 쿼리가 날라간다.
tx.commit();
} catch (Exception e){
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}
출력
print:
==========query position line=========
Hibernate:
/* insert hellojpa.Member
*/ insert
into
Member
(name, id)
values
(?, ?)
Hibernate:
/* insert hellojpa.Member
*/ insert
into
Member
(name, id)
values
(?, ?)
"===query position line==="
라인 아래로 쿼리 생성4) 변경 감지(Dirty Checking)
try {
Member member = em.find(Member.class, 150L); //name=A
member.setName("ZZZZ");
//em.persist(member);
tx.commit();
}
.persist()
가 필요 없다.
결과
설명
엔티티 삭제
//삭제 대상 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
em.remove(memberA); //엔티티 삭제
5) 지연 로딩(Lazy Loading) - 추후 설명
플러시 : 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영
try {
Member member = new Member(200L, "member200");
em.persist(member); //영속성 컨텍스트에 저장, DB 반영 X
em.flush(); //플러시하는 순간 DB에 반영해버린다.
System.out.println("===========================");
tx.commit();
}
//앞선 설명과 다르게 em.flush()를 통해서 "=================" 위로 쿼리가 생성
플러시 발생 시점
플러시하는 방법
em.flush()
: 직접 호출
트랜잭션 커밋 (tx.commit()
) : 자동 호출
JPQL 쿼리 실행 : 자동 호출
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//중간에 JPQL 실행
query = em.createQuery("select m from Member m", Member.class);
List<Member> members= query.getResultList();
memberA, B, C
는 커밋되지 않았기에 DB에 반영되지 않는다.flush()
를 호출한다.모드
em.setFlushMode(FlushModeType.COMMIT)
FlushModeType.AUTO
: 커밋이나 쿼리 실행시 플러시(기본값)FlushModeType.COMMIT
: 커밋시에만 플러시플러시 is
영속 → 준영속
영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)
영속성 컨텍스트가 제공하는 기능을 사용 못함
준영속 상태로 만드는 방법
em.detach(entity)
: 특정 엔티티만 준영속 상태로 전환
try {
Member member = em.find(Member.class, 150L);
member.setName("AAAA");
em.detach(member);
System.out.println("===========================");
tx.commit();
}
print:
Hibernate:
select
member0_.id as id1_0_0_,
member0_.name as name2_0_0_
from
Member member0_
where
member0_.id=?
===========================
.detach()
를 준영속 상태가 됐기 때문에 "======" 이후 커밋과 동시에 보여져야 하는 UPDATE 쿼리가 출력되지 않았다. (=DB에 반영도 되지 않는다.)em.clear()
: 영속성 컨텍스트 초기화
em.close()
: 영속성 컨텍스트 종료