JPA가 제공하는 기능은 크게
// 공장 만들기, 비용이 아주 많이 든다.
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("jpabook");
Persistence.createEntityManagerFactory("jpabook");
를 호출 하면
.xml에 있는 정보를 바탕으로 EntityManagerFactory를 생성합니다.<persistence-unit name="jpabook">
<properties>
<property name="javax.persistence.jdbc.driver"
value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.user"
value="sa"/>
<property name="javax.persistence.jdbc.password"
value=""/>
<property name="javax.persistence.jdbc.url"
value="jdbc:h2:tcp://localhost/~/test"/>
...
</persistence-unit>
// 공장에서 엔티티 매니저 생성, 비용이 거의 안 든다.
EntityManager em = emf.createEntityManager();
.persist()
메소드는 엔티티 매니저를 사용해서 엔티티를 영속성 컨텍스트에 저장합니다.
Ex) em.persist(member);
⇒ 회원 엔티티를 저장엔티티의 4가지 상태
// 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
// 객체를 삭제한 상태(삭제)
em.remove(member);
영속 상태의 엔티티는 1차 캐시에 저장됩니다.
Map으로 생각하면 다음과 같습니다.
Key | Value |
---|---|
@Id로 매핑한 식별자 | 엔티티 인스턴스 |
// 엔티티를 생성한 상태 (비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
// 엔티티를 영속
em.persist(member);
위 코드를 실행하면 다음 그림과 같습니다.
Member member = em.find(Member.class, "member1");
find(엔티티 클래스 타입, 조회할 식별자 값)
메소드로 엔티티를 조회합니다.Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
// 1차 캐시에 저장됨
em.persist(member);
// 1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");
Memeber a = em.find(Memeber.class, "member1");
Memeber b = em.find(Memeber.class, "member1");
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// 엔티티 매니저는 데이터 변경 시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
// 여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
// 커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
⇒ 이를 **쓰기 지연(transactional write-behind)**이라 합니다.
즉,
persist()
⇒ 1차 캐시 저장 & 쓰기 지연 SQL 저장소에 저장(INSERT SQL 생성)commit()
⇒ 데이터베이스에 반영EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // 트랜잭션 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memeberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
// em.update(member) 이런 코드가 있어야 하지 않을까?
transaction.commit(); // 트랜잭션 커밋
장점:
단점:
flush()
가 호출됩니다.Member memberA = em.find(Member.class, "memberA"); // 삭제 대상 엔티티 조회
em.remove(memberA); // 엔티티 삭제
flush()
영속성 컨텍스트를 플러시하는 3가지 방법
em.flush()
를 직접 호출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, memberB, memberC는 영속성 컨텍스트에는 있지만 아직 데이터베이스에 반영되지 않았습니다.
JPQL은 SQL로 변환되어 데이터베이스에서 엔티티를 조회합니다.
⇒ 문제점: 데이터베이스에 없는데 데이터베이스에서 조회합니다.
(단순히 조회하는find()
메소드를 호출할 때는 플러시가 실행되지 않는다.)
영속성 컨텍스트에서 분리된(detached) 것을 준영속 상태라 합니다.
⇒ 영속성 컨텍스트가 제공하는 기능을 사용할 수 없습니다.
em.clear();
merge()
메소드는 준영속 상태의 엔티티를 받아서 그 정보로 새로운 영속 상태의 엔티티를 반환합니다.참고문헌
김영한, 자바 ORM 표준 JPA 프로그래밍(에이콘출판, 2015)