항해를 진행하면서 더 많은 공부를 해야겠다 다짐을 하고 김영한님의 자바 ORM 표준 JPA 프로그래밍과 MVC 기본편? 강의를 구입했다. gradle만 사용해봤는데 ORM 표준 강의는 MAVEN을 사용한다.
열공~!~!~!~!~!~!~!
JPA
는 자바 진영에서 DB 테이블과 자바 객체 사이의 매핑을 처리해주는 ORM(Object-relational mapping)이라는 기술의 표준이다.
즉 자바의 클래스와 DB의 테이블을 매핑하는 기술이라고 보면 될 것 같다.
보통 JDBC를 사용하면 테이블 수정을 할 때 관련되어있는 모든 쿼리를 수정해야 하는데 JPA는 매핑설정만 변경하면 되서 유지보수가 쉽다.
JPA
는 애플리케이션과 JDBC 사이에서 동작한다.
위 사진은 저장을 했을 때의 예 이다.
또한 JPA는 특정 데이터베이스에 종속되지 않는다.
JPA가 Dialect를 사용하는데 데이터베이스에 맞게 변경해준다.
META_INF 안 persistence.xml
저 파란체크 한 부분을 바꿔주면 된다.
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 = new Member();
member.setId(2L);
member.setName("HelloB");
em.persist(member);
tx.commit();
}catch(Exception e){
tx.rollback();
}finally {
em.close();
}
emf.close();
}
}
정석 Code
EntityManagerFactory
를 만드는 순간 데이터베이스 연결이 된다.
그 후 EntityManager
를 만든다.
또 알아야 할 것은 데이터를 변경하는 모든 작업은 트랜잭션 안에서 이루어져야 한다는 것이다.
EntityTransaction
tx 를 선언 후 begin() 과 commit().
문제가 생긴다면 rollback()을 한다.
엔티티를 영구 저장 하는 환경 이라는 뜻이다.
영속성 컨텍스트가 생기는 시점은 엔티티 매니저 생성 시점이다.
@@여기@@ 는 강의를 듣기 전, entity 4가지 상태가 궁금해서 간단히 정리를 해놓은 곳이다.(비영속, 삭제 등등)
추가로 하는 것이라서 전에 글에서 쓴 내용은 거의 안 쓸듯?
영속성 컨텍스트(엔티티를 영구 저장하는 환경)에 의해서 관리되어지는 상태이다.
영속 -> 준영속으로 된 것이다.
영속성 컨텍스트가 제공하는 기능을 사용하지 못한다.
Member member = em.find(Member.class, 155L);
member.setName("qweqwe");
em.detach(member);
tx.commit();
em.detach(member)
- 영속성 컨텍스트에서 끄집어온다.
즉 위 코드는 update문이 실행되지 않는다.
em.clear()
- 영속성 컨텍스트를 완전히 초기화한다.
em.close()
- 영속성 컨텍스트를 닫는다.
- 1차캐시
- 동일성 보장
- 트랜잭션을 지원하는 쓰기 지연
- 변경 감지
영속성 컨텍스트는 위 사진과 같이 1차 캐시를 가지고 있다.
위에 써둔 링크에도 설명이 되어있지만 .find()를 할 때 바로 DB에서 찾는 것이 아니라 1차 캐시를 본 후 없으면 DB를 간다.
1차캐시에 해당 ID값이 없을 때의 사진이다.
DB에서 꺼내올 때 DB에 담고 반환해준다.
Member a = em.find(Member.class, "member1");
Member b = em.find(Member.class, "member1");
System.out.println(a == b); //동일성 비교 true
1차 캐시가 있기 때문에 가능한 일이다.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
transaction.commit();
커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
즉, 쓰기지연 SQL 저장소에 있던 memberA와 memberB가 flush되면서 DB에 날라간다.
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
//em.persist(memberA);
transaction.commit();
em.persist(memberA)
이러한 저장코드가 없어도 저장이 된다.
이유
commit을 하면 내부적으로 flush()가 호출된다.
그렇게 되면 1차 캐시 안에 있는 Entity와 스냅샷을 비교한다.
만약 바뀌었으면 UPDATE Query문을 생성해서 쓰기 지연 SQL 저장소에 놓는다.
그 후 DB에 반영하고 commit한다.
스냅샷이란?
최초로 영속 컨텍스트에 들어왔을 때의 값
영속성 컨텍스트의 변경내용을 DB 반영하는 것이다.
언제 플러시가 발생할까?
- 변경 감지
- 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송
영속성 컨텍스트를 플러시 하는 방법
- em.flush() - 직접 호출
- 트랜잭션 커밋 - 플러시 자동 호출
- JPQL 쿼리 실행 - 플러시 자동 호출
즉
Member member = new Member(200L, "member200");
em.persist(member);
em.flush();
System.out.println("==============================");
tx.commit();
flush가 없는 상황에선 commit을 한 후 =====가 출력되었겠지만, DB에 바로 때려박는 flush형님이 있어서 쿼리문이 나온 후 ===== 가 나온다.
- flush를 해도 1차캐시는 남아있는다.
- 영속성 컨텍스트를 비우지 않는다.