[JPA] 영속성 컨텍스트 생명주기2
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); // 데이터베이스당 하나 있어야됨
EntityManager em = emf.createEntityManager(); // 엔티티 메니저를 통해서 작업
EntityTransaction tx = em.getTransaction(); // 데이터베이스 모든 변경은 트랜잭션 안에서 일어나야함
tx.begin();
// code
try {
Member member = new Member();
member.setId(101L);
member.setName("HelloC");
System.out.println("Before");
em.persist(member); // 여기서부터 영속 상태가 된다. 이때 DB에 저장되는게 아니고 tx.commit 하는 시점에 날라감
System.out.println("after");
Member findMember = em.find(Member.class, 101L);
System.out.println("findMember.getId() = " + findMember.getId());
System.out.println("findMember.getName() = " + findMember.getName()); // 이걸 찍는데 데이터 베이스에 Select 쿼리가 안나갔다. 이것의 의미
tx.commit(); // 커밋안하면 반영이 안된다
} catch (Exception e) {
tx.rollback(); // 에러시 롤백
} finally {
em.close();
}
emf.close();
}
}
persist()는 DB에 저장하는 것이 아니라 영속 상태로 만드는 것이다. DB에 저장하는 시점은 commit()을 날리는 시점이다. 따라서 위의 코드를 실행했을 때, 쿼리문이 Before과 After 사이에 날려지는 것이 아니고 commit() 이후에 쿼리가 날라간다.
그리고 findMember의 id와 name을 찍어보니 select 쿼리문이 날라가지 않았다. 이 의미는 1차 캐시에서 꺼내썼다는 의미가 된다.
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); // 데이터베이스당 하나 있어야됨
EntityManager em = emf.createEntityManager(); // 엔티티 메니저를 통해서 작업
EntityTransaction tx = em.getTransaction(); // 데이터베이스 모든 변경은 트랜잭션 안에서 일어나야함
tx.begin();
// code
try {
Member findMember1 = em.find(Member.class, 101L); // 여기서는 쿼리가 나가야되고
Member findMember2 = em.find(Member.class, 101L); // 여기서는 쿼리가 나가면 안된다.
// 즉 쿼리 1번만 나간다.
System.out.println("result = " +(findMember1==findMember2)); // 동일성을 보장한다. 1차 캐시가 있기 때문에 가능한 것이다.
tx.commit(); // 커밋안하면 반영이 안된다
} catch (Exception e) {
tx.rollback(); // 에러시 롤백
} finally {
em.close();
}
emf.close();
}
}
즉 이 코드 또한 아이디가 같은 member를 두 번 조회했기 때문에 조회 쿼리가 한번만 나간다. 여기서 알 수 있는 것은 1차 캐시를 통해서 동일성을 보장한다는 점이다.
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); // 데이터베이스당 하나 있어야됨
EntityManager em = emf.createEntityManager(); // 엔티티 메니저를 통해서 작업
EntityTransaction tx = em.getTransaction(); // 데이터베이스 모든 변경은 트랜잭션 안에서 일어나야함
tx.begin();
// code
try {
Member member1 = new Member(150L, "A");
Member member2 = new Member(160L, "B"); // 영속성 컨텍스트에 차곡차곡 쌓아두고,commit 시점에 쿼리 날라감
// 이것의 장점은? buffer기능을 쓸 수 있다.
em.persist(member1);
em.persist(member2);
System.out.println("========================");
tx.commit(); // 커밋안하면 반영이 안된다
} catch (Exception e) {
tx.rollback(); // 에러시 롤백
} finally {
em.close();
}
emf.close();
}
}
persist()는 DB에 바로 쿼리를 날리는 것이 아니라고 앞서 말했다. persist()를 하게 되면 쓰기 지연 SQL 저장소에 쿼리를 넣어두었다가 flush, commit 시점에 한번에 날린다. 이를 통해 알 수 있는 것은 버퍼의 기능으로써 사용이 가능해진다는 것이다.
<property name="hibernate.jdbc.batch_size" value="10"/> 이런설정을 주면 10개 모았다가 한방에 쿼리를 날린다. 이런거 알면 성능이 더 잘 나올 수 있다.
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); // 데이터베이스당 하나 있어야됨
EntityManager em = emf.createEntityManager(); // 엔티티 메니저를 통해서 작업
EntityTransaction tx = em.getTransaction(); // 데이터베이스 모든 변경은 트랜잭션 안에서 일어나야함
tx.begin();
// code
try {
/* 변경감지 */
Member member = em.find(Member.class, 150L);
member.setName("ZZZㅋㅋ"); // flush가 일어나면 1차 캐시에 엔티티와 스냅샷을 비교해서 다르면 update 쿼리를 쓰기 지연 저장소에 두고 commit할때 쿼리날림
// em.persist(member); // 이걸 해야할 것 같지만 아니다. 이코드를 쓰면안됨
tx.commit(); // 커밋안하면 반영이 안된다
} catch (Exception e) {
tx.rollback(); // 에러시 롤백
} finally {
em.close();
}
emf.close();
}
}
수정시에는 persist 같은 것을 하면 안된다. JPA의 철학이 컬렉션 같이 사용하자라는 취지에 맞게.. 컬렉션 안의 값을 바꾼다고 우리가 다시 저장하지 않듯, JPA도 마찬가지이다. 영속상태에 존재하는 엔티티를 set이나 다른 update 메서드를 통해서 바꾸게 되면 JPA가 쓰기 지연 SQL 버퍼에 update SQL문을 저장해두었다가 commit 시점에 날린다.
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); // 데이터베이스당 하나 있어야됨
EntityManager em = emf.createEntityManager(); // 엔티티 메니저를 통해서 작업
EntityTransaction tx = em.getTransaction(); // 데이터베이스 모든 변경은 트랜잭션 안에서 일어나야함
tx.begin();
// code
try {
/* flush */
Member member = new Member(200L,"member200");
em.persist(member);
em.flush(); // 즉시 쿼리 날라감 1차캐시 안지워짐 그냥 쿼리만 날라가는거임
System.out.println("========================"); // 이 선 위로 쿼리 날라감
tx.commit(); // 커밋안하면 반영이 안된다
} catch (Exception e) {
tx.rollback(); // 에러시 롤백
} finally {
em.close();
}
emf.close();
}
}
주석 그대로 flush()를 하게 되면 즉시 쿼리가 날라간다. 이때 1차 캐시는 지워지지 않고 쿼리만 날라간다. 즉시 쿼리가 날라가기 때문에 콘솔에 찍은 선 위로 쿼리문이 찍히게 된다.
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); // 데이터베이스당 하나 있어야됨
EntityManager em = emf.createEntityManager(); // 엔티티 메니저를 통해서 작업
EntityTransaction tx = em.getTransaction(); // 데이터베이스 모든 변경은 트랜잭션 안에서 일어나야함
tx.begin();
// code
try {
Member member = em.find(Member.class, 150L);
member.setName("AAAAA");
// em.detach(member); // detach하면 JPA 에서 관리 안함
em.clear();// 통째로 날리기 or em.close();
Member member1 = em.find(Member.class, 150L);
tx.commit(); // 커밋안하면 반영이 안된다
} catch (Exception e) {
tx.rollback(); // 에러시 롤백
} finally {
em.close();
}
emf.close();
}
}
위의 코드는 set을 통해서 변경감지가 일어나서 name이 AAAAA로 바뀌어야 한다. 하지만 detach나 clear를 통해서 영속 상태를 해지하게 되면 JPA의 감시에서 벗어나기 때문에 commit해도 반영되지 않는다.