public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
// 위 코드가 뜨면 디비와 연결이 왼다.
// em은 데이터베이스 커넥션이라고 생각해도 된다.
EntityManager em = emf.createEntityManager(); //생성, 일관적인 단위 행동을 할떄마다 em을 만들어줘야 한다.
EntityTransaction tx = em.getTransaction(); // em은 트랜잭션내에서 실행해야 디비에 적용된다.
tx.begin();
try {
//code
// 회원 등록
Member member = new Member();
member.setId(2L);
member.setName("HelloB");
em.persist(member); // JPA에 저장
// 회원 수정
Member findMember = em.find(Member.class, 1L);
System.out.println("findMember.id = " + findMember.getId());
System.out.println("findMember.name = " + findMember.getName());
// 회원 삭제
Member findMember = em.find(Member.class, 1L);
em.remove(findMember);
// 회원 단건 조회 -> 따로 em.persist()를 안해줘도 된다.
Member findMember = em.find(Member.class, 1L);
findMember.setName("HelloJPA");
// 회원 전체 조회
List<Member> result = em.createQuery("select m from Member as m", Member.class)
.setFirstResult(5)
.setMaxResults(8)
.getResultList();
for (Member member : result) {
System.out.println("member.name = " + member.getName());
}
tx.commit(); // 커밋발생시 데이터베이스에 반영됨.
}catch (Exception e) {
tx.rollback();
}finally {
em.close(); // em이 내부적으로 데이터베이스 컬랙션을 물고 동작해서 꼭 닫아줘야 한다.
}
emf.close();
}
}
em.persist(findMember);
로 따로 저장해주지 않는다. 그 이유는 JPA를 통해서 엔티티를 가지고 오면 JPA가 그 엔티티를 관리한다. 그래서 트랜잭션이 커밋하는 시점에 변경된 점이 있는지 JPA가 체크한다. 그래서 바뀐 점이 있으면 트랜잭션 커밋전에 update쿼리를 만든다. 그리고 나서 트랜잭션이 커밋되면 update쿼리가 DB에 보내진다.EntityManager.persist(entity);
em.persist()
를 하면 영속상태가 된다.em.persist()
는 디비에 저장되는 것이 아니다.em.detached(entity)
를 하면 영속성 컨텍스트에서 지운다.em.remove(entity)
로, 디비에서 값을 지우는 것이다.영속성 컨텍스트는 어플리케이션과 디비 사이에 있는 어느 장소이다.
1. 빠른 속도의 조회
→ JPA는 em.find(entity);
로 조회할때, 먼저 영속성 컨텍스트의 1차캐시에 값이 있는지 찾는다. 1차 캐시에 값이 있으면 그대로 가져가고, 1차캐시에 값이 없으면 데이터베이스에서 값이 있는지 조회한다. 그리고 DB에 조회해온 값을 1차캐시에 저장한다. 그리고나서 찾은 값을 반환한다.
em.persist(entity);
로 인한 쿼리실행 단축em.persist(entity);
를 실행해서 이미 영속성 컨텍스트의 1차 캐시에 member가 저장되었다. 그래서 em.find(member);
를 하면 1차 캐시에서 값을 찾을 수 있다. 그래서 따로 DB에서 값을 찾지 않아도 된다. Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//1차 캐시에 저장됨
em.persist(member);
//1차 캐시에서 조회
Member findMember = em.find(Member.class, "member1");
tx.commit();
// 사진 12
em.find(member);
를 두번 실행 -> select 쿼리 한번 실행em.find(Member.class, 20L);
에서 DB에서 Member값을 가지고 오면서 영속성 컨텍스트에 올린다. 그리고 나서 두번째로 똑같은 Member값을 조회하면 이미 영속성 컨텍스트에 찾는 값이 있어서 따로 DB에 select 쿼리를 보내서 찾지 않고, 1차 캐시에서 바로 값을 가져온다. Member findMember1 = em.find(Member.class, 20L);
Member findMember2 = em.find(Member.class, 20L);
findMember1 == findMember2
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 true
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
위 코드를 보면, transaction.commit();
이 실행되기 전까지 em.persist(entity);
들을 모았다가 트랜잭션이 커밋되면 DB에 insert 쿼리가 나간다. 즉, transaction.commit();
이 실행되기 전에 em.persist(entity);
를 실행하면 영속성 컨텍스트의 1차캐시에 값이 저장된다. 그리고 영속성 컨텍스트의 쓰기 지연 SQL저장소에 insert 쿼리가 생성되어 저장된다.
transaction.commit();
를 실행하면 쓰기 지연 SQL저장소에 있던 쿼리들이 em.flush()
가 되면서 DB에 넘어간다. 그리고나서 실제 DB 트랜잭션이 커밋된다.
이렇게 쿼리들을 모았다가 한번에 DB에 보내는 기능을 "버퍼링"이라고 한다. 이를 잘 사용하면 성능을 좋게 할 수 있다.
-> 사용예시 ) <property name="hibernate.jdbc.batch_size" value="10"/>
10개씩 데이터베이스에 보내고 커밋을 하는 것
em.persist(entity);
를 해주지 않아도 값이 바뀌었던 것이 바로 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) 이런 코드가 있어야 하지 않을까? -> NONO
transaction.commit(); // [트랜잭션] 커밋
flush()
가 실행된다. 그러면 엔티티와 스냅샷을 비교한다. 1차 캐시에는 @Id값, Entity값, 스냅샷값 이 있다. 스냅샷 값은 값을 읽어온 최초 시점의 상태로 변경되기 이전의 값을 의미한다. 즉, 최초로 영속성 컨텍스트의 1차 캐시에 들어온 상태이다. 그래서 엔티티와 스냅샷의 값이 달라서, 엔티티의 값이 바뀐것을 JPA가 알게되면, update 쿼리를 만들어서 쓰기 지연 SQL저장소에 넣어둔다. 그리고 이 쿼리를 DB에 반영하고 커밋한다. 이 과정을 변경감지라고 한다. 즉, JPA는 값을 바꾸면 트랜잭션이 커밋되는 시점에 변경을 반영한다. 변경감지와 동작방식이 같다. 단지 트랜잭션 커밋시점에 delete쿼리가 나간다.
flush()
를 의미한다.transaction.commit();
이 실행되어야 DB 트랜잭션이 커밋된다. 즉, DB에 변경이 반영되려면 transaction.commit();
이 발생해야 한다.💗 flush, flush(), commit() 차이
1. 엔티티와 스냅샷 비교 후 변경된 것에 대한 SQL 생성
2. 생성된 SQL을 쓰기 지연 SQL 저장소에 등록
3. 쓰기 지연 SQL 저장소에 등록된 쿼리를 DB로 전송
em.flush()
- 직접 호출flush()
를 실행한다고 해서 1차 캐시가 다 지워지지는 않는다. 1차 캐시는 그대로 유지가 되고, 영속성 컨텍스트의 쓰기 지연 SQL저장소에 있는 쿼리들이 바뀐다. 그리고 이 바뀐 쿼리들이 DB에 적용이 된다.Member member = new Member(200L, "member200");
em.persist(member);
// 미리 데이터베이스에 적용하거나 미리 쿼리가 적용하고 싶으면 em.flush();
em.flush(); // tx.commit();전에 insert쿼리가 실행된다.
System.out.println("================");
tx.commit(); // 커밋발생시 데이터베이스에 반영됨.
트랜잭션 커밋 - 플러시 자동 호출
-> 트랜잭션이 커밋되면 플러시는 자동 호출된다.
JPQL 쿼리 실행 - 플러시 자동 호출
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(entity);
를 하면 영속상태가 된다. em.find(entity);
해서 가져오는 과정에서, 영속성 컨택스트에 없을때 디비에서 가져와서 1차캐시에 넣을때도 영속상태가 된다.준영속 상태란?
em.detach(entity)
em.clear()
em.close()