아래는 김영한 강사님의 스프링 JPA 를 듣고 정리한 내용이다.
JPA 표준
section1
JPA: 과거에는 객체를 데이터 베이스에 저장하려면 복잡한 JDBC,Mybatis,JDBC template 사용
이제는 JPA 라는 트랙터로 밀어버리면 된다(SQL없이)
개발자는 SQL 매퍼이다. 매핑이란 객체랑 RDB를 연결하는 것
데이터 베이스 테이블에는 상속관계가 없다
풀 수 있는 방법:
부모같은 테이블 만들고, 자식같은 테이블 만들어서 필요할 떄는 join 해서 가져오는 것
슈퍼타입, 서브타입 관계
객체는 참조로, 테이블은 외래키를 이용해서 JOIN 해서 다른 객체 및 테이블을 사용
JPA 필요성
상속, 연관관계, 데이터 타입, 데이터 식별 방법 이 객체와 관계형 db의 차이점이 있다
이 차이를 매꾸면서 객체를 자바 컬렉션 저장 하듯이 DB에 저장 하는 것이 JPA기술이다.
ORM
객체와 관계형 DB를 매핑하는 것.
1차 캐쉬의 이점
1) 모아서 보내는 것:Buffer Writing 기능
2) 캐시: 조회할때 DB에서 가지고 오지 않고 쉽게 가져올 수 있다.
3) 동일성 보장: 같은 데이터베이스 트랜잭션 안에서 동일성을 보장해 준다(==비교가 가능하다)
Section2,3
h2 데이터베이스
DB에 따라서 SEQUENCE,AUTO INCREMENT 가 다른데 H2는 둘다 지원
테스트 용도로 많이 쓴다.
스프링에 맞는 JPA 를 찾으려면...
spring.io->projects->spring boot->: 내가 사용하는 버젼과 Reference DOC에서 Dependency versions 에서 ORG.Hibernate 찾으면 어떤 버젼 사용하는지 나온다.
쿼리 콘솔창에 보이기
show_sql: 쿼리 콘솔에 보이기
format_sql: 이쁘게 포맷팅 시켜줌
use_sql_comments:쿼리가 왜 나오는지 적어줌
JPA 구동 방식
1)Persistence 가 persistence.xml 을 참고해서 EntityManagerFactor 를 생성한다
2)필요할 떄마다 EntityManager 를 생성한다.
EntityManagerFactory
EntityManagerFactory는 애플리케이션 로딩시점에 딱 하나만 만들어 놔야 함
Transaction
JPA 에서는 데이터를 변경하는 모든 작업은 트랜잭션 안에서 작업해야 한다.
Transaction은 한 개 이상의 데이터베이스 연산을 묶어서 하나의 작업으로 처리하는 것을 의미
영속성 관리
고객의 요청이 올떄마다 EntityManager 를 사용한다
엔티티매니저는 DB커넥션을 사용한다.
스프링 프레임워크 같은 컨테이너 환경에서는 엔티티 매니저와 영속성 컨텍스트가 N:1 이다
entityManager 안에 영속성 컨텍스트가 있다고 생각하자
em.persist()
em.persist는 DB에 저장을 의미하는게 아니라 엔티티를 영속화 한다는 뜻이다
em.persist(member)는 영속성 컨텍스트에서 멤버를 관리하게 하는 것이다. 아직 DB에 저장하지 않았고
transaction commit 시점에 쿼리를 보낸다 .
영속성 컨텍스트의 이점
1차 캐쉬: em.find 하면 먼저 1차 캐쉬를 조회한 후 없다면 DB로 select 쿼리를 날린다
동일성 보장: 단 같은 transaction 안에서만
트랜잭션을 지원하는 쓰기 지연: transaction.commit -> 축적한 후 한번에 보낸다. BATCHSIZE로 묶어서 보내기가 가능(Buffer 모아서 Writing 가능) 이렇게 하면 최적
화 할 수 있는 여지가 있다.
변경 감지:데이터 베이스에서 객체를 찾아온 다음에 데이터의 값을 수정하면 자동으로 update 쿼리가 나간다.
그 방식은 트랜잭션 커밋 시점에 엔티티와 엔티티의 이전 초기 상태였던 스냅샷을 비교하여 변한게 있다면 그 때 쓰기 지연 저장소에 update 쿼리를 넣고
flush를 수행하는 식이다.
엔티티를 삭제할 떄는?
em.remove()로 가능하다.
준영속상태?
em.detach(member) 등으로 회원 엔티티를 영속성 컨텍스트에서 분리한 상태를 준영속 상태라고 한다
em.detach()를 트랜잭션이 커밋되기 전에 수행한다면 그 전에 수정이나 persist 마구마구 수행해도 전혀 바뀌지 않는다
그렇다면 영속성 컨텍스트가 제공하는 기능(더티체킹) 을 사용하지 못한다.
준영속상태로 만드는 방법
1)em.detach(entity)
2)em.clear()->영속성 컨텍스트를 완전 초기화
3)em.close()
플러쉬
보통 데이터 베이스 트랜잭션이 커밋될 때 flush가 일어난다.
플러쉬가 발생한다면
1) 변경 감지 수행
2) 수정된 엔티티를 쓰기 지연 SQL 저장소에 등록
3) 쓰기 지연 SQL 저장소의 쿼리를 db에 전송한다.
영속성 컨텍스트를 flush하는 법
1)em.flush()
2)트랜잭션 커밋
3)JPQL 쿼리 실행
3번은 그럴 수 밖에 없다. em.persist()를 하고 JPQL 을 실행하면 맥락상 persist 한게 들어갔다고 보는게 편리할 것 같기 때문이다
em.flush()한다고 1차 캐쉬가 지워진다는 망상은 하지 말자!
repository.save의 안에는
실무에서 자주 사용하는 public interface MemberRepository extends JpaRepository<Member,Long>
를 만들때에는 memberRepository.save(member);
를 통해서 엔티티를 저장하는데 이 안에 내부적으로 살펴보면 내부적으로 SimpleJpaRepository가 빈등록이 되어서 아래 메서드를 호출 하여 저장하게 된다.
@Transactional
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}