JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공
해결
- JPA는 SQL을 추상화한 JPQL이라는 객체 지향 쿼리 언어 제공
💡 예시
List<Member> result = em.createQuery("select m from Member as m", Member.class)
.setFirstResult(5) // 5번 부터
.setMaxResults(10) // 10개 가지고 오기
.getResultList();
for(Member member : result) {
System.out.println("member.name = " + member.getName());
}
- JPA를 이해하는 데 가장 중요한 용어
- "엔티티(ENTITY)를 영구 저장하는 환경(공간)"이라는 뜻의 논리적인 개념
이러한 엔티티들을 JPA는 어떠한 곳에 저장하는데, 이 영역을 영속성 컨텍스트
영속성 컨텍스트는 엔티티 매니저를 통해서 접근
EntityManager.persist(entity);
cf.em.persist(member);
"entity"에 들어가는 객체를 DB에 저장하는 느낌
실제로는 DB에 저장하는 것 ❌ 영속성 컨텍스트에 저장
DB 저장은tx.commit();
에서
비영속 (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);
em.clear(member);
em.close(member);
.merge()
: 분리되었다가 다시 영속성 컨텍스트로 삭제 (removed)
// 객체를 삭제한 상태
em.remove(member);
.persist()
: 다시 영속성 컨텍스트로 flush()
: 영속성 컨텍스트에서 db에 들어가기 위해사용
- 애플리케이션과 DB사이에 왜 영속성 컨텍스트가 필요할까?
- 1차 캐시
- 동일성(identity)보장
- 트랜잭션을 지원하는 쓰기 지연(Transactional write-behind)
- 변경감지(Dirty Checking)
- 지연로딩(Lazy Loading)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
// 객체를 영속성 컨텍스트에 저장(영속)
em.persist(member);
- 영속성 컨텍스트와 식별자 값
- 엔티티를 식별자값(
@id
로 테이블의 기본 키와 매핑한 값)으로 구분- 영속 상태는 식별자값이 반드시 있어야 한다.
- 식별자 값이 없으면 예외 발생
- 영속성 컨텍스트와 데이터베이스 저장
- JPA는 보통 트랜잭션을 커밋하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 DB에 반영
- 플러쉬(flush)
이때, find()가 일어난다면?
Member findMember = em.find(Member.class, "Member1");
⭕ 1차 캐시에 데이터가 있을 때
❌ 1차 캐시에 데이터가 없을 때
같은 데이터를 2번 조회할 경우
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b) // true
EntityManager em = EntityManagerFactory.createEntityManager();
EntityTransaction tx = em.getTransaction(); // 트랜잭션
// 트랜잭션 시작
tx.begin();
// 비영속
Member memberA = new Member();
memberA.setId("member1");
memberA.setUsername("홍길동");
Member memberB = new Member();
memberB.setId("member1");
memberB.setUsername("홍길동");
// 영속 : 여기까지 Insert SQL을 데이터베이스에 보내지 않는다
em.persist(memberA);
em.persist(memberB);
// 엔티티 등록 : commit하는 순간 데이터베이스에 Insert SQL을 보냄
tx.commit();
persist()
가 일어나면 commit()
할 때, DB에 쿼리들을 보냄flush()
여러 개의 엔티티를 생성하고
persist
를 하더라도commit()
을 하기 전에는 데이터베이스에 저장❌ --> 쓰기 지연
📌 엔티티 수정 코드
EntityManager em = EntityManagerFactory.createEntityManager();
EntityTransaction tx = em.getTransaction(); // 트랜잭션
// 트랜잭션 시작
tx.begin();
// 조회
Member member A = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
// em.update(member); 이런 코드 없이도 update 가능
tx.commit();
엔티티 수정 -> update()
, persist()
필요없음
데이터만 set하고 트랜잭션을 커밋하면 자동으로 업데이트 쿼리
HOW? 변경 감지(Dirty Checking)를 통해
persist()
로 1차 캐시에 저장할 때 동시에 스냅샷 필드도 저장
commit()
또는 flush()
실행 시, 엔티티와 스냅샷을 비교해서 변경사항이있으면 UPDATE SQL을 알아서 만들어서 DB에 저장
영속성 컨텍스트의 변경내용을 데이터베이스에 반영하는 것
em.flush()
tx.commit()
(트랜잭션 커밋), JPQL 쿼리 실행
- 영속상태
- 1차 캐시에 올라간 상태
- 엔티티 매니저가 관리하는 상태
em.persist()
로 영속성 컨텍스트에 저장한 상태em.find()
로 조회를 할 때, 영속성 컨텍스트 1차 캐시에 없어서 DB에서 조회해와서 1차 캐시에 저장한 상태- 준영속상태
- 영속 상태의 엔티티가 영속성 컨텍스트에서 분리(detached)된 상태
- 영속성 컨텍스트가 제공하는 기능을 사용하지 못함. 쿼리 안나감.
em.detach(entity)
em.clear()
em.close()