EntityManager는 내부적으로 데이터베이스 커넥션을 사용해서 db를 사용하게 됨.
persist 메서드는 db에 저장하는게 아니라, entity를 영속성 컨텍스트 안에다가 저장한다는 것.
엔티티매니저가 생기면 PersistenceContext라는 공간이 생성됨.
//비영속(JPA와 아무 관련이 없고, db에 들어가지도 않음)
Member member = new Member();
member.setId(100L);
member.setName("HelloJPA");
//영속
System.out.println("===BEFORE===");
em.persist(member);
System.out.println("===AFTER===");
em.persist 하게되면 영속 상태가 됨.
영속 이란? EntityManager 안에 있는 영속성 컨텍스트 라는 거에 통해서 Member가 관리되는것.
영속 상태가 된다고 해서 바로 db로 쿼리가 날라가는것이 아님.
트랜잭션을 커밋하는 시점에 영속성 컨텍스트 안에 있는 애가 DB로 쿼리가 날라가게됨.
영속 컨텍스트 내부에 1차 캐시를 들고 있다.
키가 PK가 되고, Entity 객체 자체가 값이 된다. 맵이 있다고 보면 됨.
find("member1")하면 DB를 뒤지는게 아니라 먼저 1차 캐시를 뒤짐
DB엔 있고 1차 캐시엔 없는 경우
JPA가 영속 컨텍스트 1차 캐시에 member2가 없으면 DB를 뒤진다.
DB를 뒤져서 1차 캐시에 저장한 후 member2를 반환한다.
entityManager라는 거는 데이터베이스 트랜잭션 단위로 만들고, 데이터베이스 트랜잭션이 끝나면 같이 종료시켜 버린다.
=>고객의 요청이 하나 들어온 후 비즈니스가 끝나 버리면 영속성 컨텍스트를 지운다. 1차 캐시도 다 날라감. 여러명의 고객이 사용하는 캐시가 아님.
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();
try {
//비영속(JPA와 아무 관련이 없고, db에 들어가지도 않음)
Member member = new Member();
member.setId(101L);
member.setName("HelloJPA");
//영속
em.persist(member);
Member findMember = em.find(Member.class, 101L);
System.out.println("findMember.id = " + findMember.getId());
System.out.println("findMember.name = " + findMember.getName());
tx.commit();
} catch (Exception e) {
tx.rollback();
}finally {
em.close();
}
emf.close();
}
}
Select 쿼리가 나가는게 아니라 인서트 쿼리가 나감
try {
Member findMember1 = em.find(Member.class, 101L);
Member findMember2 = em.find(Member.class, 101L);
tx.commit();
}
Select 쿼리가 1번만 나감.
JPA가 DB에서 가지고 와서 영속 컨텍스트에 올려놓는다.
JPA는 entity를 조회하면 무조건 영속성 컨텍스트에 올린다.
두번째 똑같은 걸로 조회하니까 조회하는 시점에 먼저 영속성 컨텍스트 안에 있는 1차 캐시부터 뒤진다.
영속 컨텍스트 내부에는 1차 캐시 외에도 쓰기 지연 SQL 저장소가 있다.
em.persist(memberA); 해서 memberA를 넣으면 1차 캐시에 들어가서
동시에 JPA가 이 entity를 분석해서 insert 쿼리를 생성하여 쓰기 지연 sql저장소에 쌓아둔다.
memberB도 persist하면 이때도 memberB를 1차캐시에 집어 넣는다.
이때 또 insert sql을 생성해서 쓰기지연 sql 저장소에 쌓는다.
언제 이 쿼리들이 db에 날라가나? -> 트랜잭션을 커밋하는 시점에!!
commit(); 하면 쓰기지연 sql 저장소에 있던 애들이 flush가 되면서 쿼리들이 날라간다. 그리고 실제 db에 트랜잭션이 커밋됨.
생성자 만들었을 때, 에러가 뜨는데 그 이유는 jpa는 기본적으로 리프랙션을 쓰므로 동적으로 객체를 생성해내야 하므로 기본 생성자가 하나 있어야 한다.
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(160L, "B");
em.persist(member1);
em.persist(member2); // 이떄까지 영속 컨텍스트에 차곡차곡 entity와 쿼리가 쌓임
System.out.println("========================");
tx.commit();//이때 진짜 db에 쿼리가 날라간다
}
구분선 다음으로 쿼리들이 나간것을 확인 가능.
모았다가 db에 보냄
옵션 사용
<property name="hibernate.jdbc.batch_size" value="10"/>
JPA는 자바 컬렉션을 다루듯이! 컬렉션에서 꺼내서 값을 바꾼 후 컬렉션에 다시 집어 넣나? 그렇지 않다. 따라서 변경 후 em.persist해주면 안된다!
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 member = em.find(Member.class, 150L);
member.setName("ZZZZ");
tx.commit();//이때 진짜 db에 쿼리가 날라간다
}
데이터베이스 트랜잭션 커밋하면 내부적으로 flush가 호출됨. 엔티티와 스냅샷을 비교한다. (1차 캐시 안에는 pk, entity, 스냅샷이 있다. 스냅샷이란 값을 읽어온 최초 시점의 값을 넣어둔다.) 비교해보고 바뀌었네? 하면 update쿼리를 쓰기 지연 sql저장소에 넣어둔다 그리고 update 쿼리를 데이터베이스에 반영하고, 커밋한다.
엔티티 찾아와서 remove하면 됨.
방금 매커니즘과 똑같으며, 트랜잭션 커밋 시점에 delete 쿼리가 나가게 된다.
영속성 컨텍스트의 쿼리들을 db에 날려줌
데이터베이스에 커밋하면 플러시가 발생한다 보면됨.
try {
Member member = new Member(201L, "member201");
em.persist(member); //영속성 컨텍스에 담기고, 쿼리가 저장소에 담겨있다.
em.flush(); // 데이터베이스에 insert 쿼리가 이 시점에 즉시 나간다.
System.out.println("=========================");
tx.commit();//이때 진짜 db에 쿼리가 날라간다
}
구분선 전에 쿼리가 나간것을 확인 가능.
플러시는 오직 영속성 컨텍스트에 있는 쓰기 지연 sql 저장소에 있는 쿼리들이 데이터베이스에 반영되는 과정이라 보면 됨.
em.persist()하면 영속 상태가 된다.
1차 캐시에 올라간 상태 : 영속 상태(JPA가 관리하는 상태)
persist로 저장했을 때 뿐만 아니라 em.find로 조회했을 때도 영속성 컨텍스트에 없으면 영속 상태가 된다
try {
//영속
Member member = em.find(Member.class, 150L);//JPA가 얘는 없네? 얘를 가져와서 영속성 컨텍스트에 올림
member.setName("AAAA");
em.detach(member); //얘는 이제 jpa에서 관리안함.
System.out.println("=========================");
tx.commit();
}
select쿼리만 나가고 update쿼리가 안나갔다. 왜냐면 detach 했기때문. 영속성 컨텍스트에서 관리하지마 하고 끄집어 낸것.
영속 상태였다가 영속성 컨텍스트에서 빠진 상태를 준영속 상태라 한다.
em.clear() : 영속성 컨텍스트 안에 있는 걸 통째로 다 지워버림.1차캐시를 통으로 다 지우는것.
try {
//영속
Member member = em.find(Member.class, 150L);//JPA가 얘는 없네? 얘를 가져와서 영속성 컨텍스트에 올림
member.setName("AAAA");
em.clear();
Member member2 = em.find(Member.class, 150L);
System.out.println("=========================");
tx.commit();
}
select 쿼리가 두번 나가는것을 확인가능.