// 엔티티 매니저 팩토리 생성
// 비용이 아주 많이 든다.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpabook");
// 엔티티 매니저 생성, 비용이 거의 안든다.
EntityManager em = emf.createEntityManager();
여러 엔티티 매니저가 같은 영속성 컨텍스트에 접근할 수도 있다.
// 객체를 생성한 상태 (비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
em.find()
나 JPQL을 사용해 조회된 데이터는 영속 상태이다.// 객체를 저장한 상태 (영속)
em.persist(member);
em.datach()
, em.close()
, em.clear()
호출 시 준영속 상태가 된다.// 객체를 삭제한 상태(삭제)
em.remove(member);
@Id
로 테이블의 기본키와 매핑한 값)으로 구분한다.@Id
로 매핑한 식별자고 값은 엔티티 인스턴스다.// 엔티티를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원");
// 엔티티를 영속
em.persist(member);
EntityManager.persist()
를 통한 영속객체 등록은 우선 해당 객체가 1차 캐시에 저장되고 일반적으로 트랜잭션이 커밋되는 시점에 Insert 쿼리가 데이터베이스에 반영된다.EntityManager.find()
를 통한 데이터검색은 우선 1차 캐시를 통해 해당 객체를 검색하고 없을 경우 Select 쿼리를 통해 데이터베이스에석 검색을 수행한다.Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); // 동일성 비교
JPA를 이용하면, member1
이란 식별자(id)를 가진 객체를 반복해서 호출할 때 1차 캐시에 있는 같은 엔티티 인스턴스를 반환한다. 따라서 영속성 컨텍스트는 성능상의 이점과 엔티티의 동일성을 보장한다.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// 엔티티 매니저는 데이터 변경 시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
// 여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
// 커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
begin(); // 트랜잭션 시작
save(A);
save(B);
save(C);
commit(); // 트랜잭션 커밋
이 로직을 실행하는 2가지 방식이 있다.
save()
메서드를 호출할 때마다 즉시 DB에 등록 쿼리를 보내고 마지막에 트랜잭션을 커밋한다.트랜잭션 범위 안에서 실행되고, 커밋 시 함께 저장, 롤백 시 함께 취소 되므로 결과는 1번과 2번이 동일하다. 이 기능을 잘 활용하면 모아둔 등록 쿼리를 DB에 한 번에 전달해 기능을 최적화할 수 있다.
UPDATE MEMBER
SET
NAME=?,
AGE=?
WHERE
id=?
수정 쿼리는 수정 요소가 늘어날 때마다 SQL을 일일이 확인해야 하는 등, 비즈니스 로직을 분석하기 위해 SQL을 의존해야 하는 문제점이 있다. JPA는 이러한 수정 쿼리의 불편함을 변경 감지를 통해 해결한다.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
// em.update(member) 이런 코드가 있어야 하지 않을까?
trnasaction.commit(); // [트랜잭션] 커밋
JPA로 엔티티를 수정할 때는 단순히 엔티티를 조회해 데이터만 변경하면 된다. 이는 엔티티의 변경사항을 데이터베이스에 자동으로 반영하는 변경 감지(dirty checking) 덕분에 가능해진다.
flush()
)가 호출된다.UPDATE MEMBER
SET
NAME=?,
AGE=?,
GRADE=?,
...
WHERE
id=?
필드가 많거나 저장되는 내용이 너무 큰 경우, 수정된 데이터만 사용해 동적으로 UPDATE SQL을 생성하는 전략을 선택 가능하다. 이 경우 하이버네이트 확장 기능을 사용한다.
@Entity
@org.hibernate.annotations.DynamicUpdate
@Table(name="Member")
public class Member {...}
Member memberA = em.find(Member.class, "memberA") // 삭제 대상 엔티티 조회
em.remove(memberA); // 엔티티 삭제
remove()
를 이용한다.em.remove(memberA)
호출 시점에 memberA는 영속성 컨텍스트에서 제거된다.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();
em.persist()
를 호출해 3개의 멤버를 영속성 컨텍스트에 올렸으나 DB에는 반영되지 않았다. 이 상황에서 JPQL이 실행된다면 어떻게 될까?참고) 식별자를 기준으로 조회하는 find() 메서드 호출 시 플러시를 실행하지 않는다.
em.setFlushMode(FlushModeType.COMMIT)
public void testDetached() {
...
// 회원 엔티티 생성, 비영속 상태
Member member = new Member();
member.setId("memberA");
member.setUsername("회원A");
// 회원 엔티티 영속 상태
em.persist(member);
// 회원 엔티티를 영속성 컨텍스트에서 분리, 준영속 상태
em.detach(member);
transaction.commit();
// 엔티티 조회, 영속 상태
Member member = em.find(Member.class, "memberA");
em.clear(); // 영속성 컨텍스트 초기화
// 준영속 상태
member.setUsername("changeName");
public void closeEntityManager() {
EntityMangerFactory emf = Persistence.creatEntityManagerFactory("jpabook");
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin() // [트랜잭션] - 시작
Member memberA = em.fiind(Member.class, "memberA");
Member memberB = em.fiind(Member.class, "memberB");
transaction.commit(); //[트랜잭션] - 커밋
em.close(); // 영속성 컨텍스트 닫기(종료)
준영속 상태인 회원 엔티티는 어떻게 되는걸까?
merge()
을 사용하면 된다.merge()
: 준영속 상태의 엔티티를 받아 그 정보로 새로운 영속 상태의 엔티티를 반환한다.