nauni 2021. 6. 27. 17:01

JPA 왜 사용하는가?

  • 패러다임 불일치(객체지향 VS 관계형 데이터베이스)
    • 객체는 방향성이 있는데, 테이블은 방향성이 없음
    • 객체답게 모델링할수록 RDB에 저장하기 위해 매핑작업만 늘어남
  • 엔티티 신뢰문제
    • 원하는 내부 필드를 다 가지고 있는지 확신할 수 없다. 널포인터 익셉션이 일어날 수 있다!
  • 자바 컬렉션에 저장하듯이 DB에 저장할 수는 없을까?
    • JPA(Java Persistence Api)
    • ORM(Object-Relational Mapping, 객체 관계 매핑)JPA를 사용하면?
  • SQL 중심적인 개발에서 객체 중심적인 개발이 가능
  • 생산성 향상 (비지니스 로직에 더 집중가능)
  • 유지보수
  • 신뢰할 수 있는 엔티티
  • 패러다임 불일치 문제 해결

JPA의 핵심개념

  • 영속성 컨텍스트, 연관관계 매핑

JPA 사용에 필요한 기초지식

  • @Entity: JPA가 관리할 객체
  • @Id: DB의 PK와 매핑할 필드
  • persistence.xml: JPA 설정파일 (SpringBoot 없이 사용하려면 필수임)
    • META-INF 폴더 아래애 persistence.xml로 생성
    • 실행시 설정정보를 조회하고, EntityManagerFactory에서 EntityManager를 생성함
  • 트랜잭션 단위로 매번 EntityManger를 만듦
  • EntityManagerFactory는 하나만 생성하여 애플리케이션 전체에 공유
  • EntityManager는 스레드간에 공유하면 안됨 -> DB 커넥션이 물릴 수 있다.
  • JPA 모든 데이터 변경은 트랜잭션 안에서 실행
  • flush: 쓰기지연 sql 저장소의 쿼리를 데이터베이스에 전송(영속성 컨텍스트를 비우진 않음, 동기화가 목적)
    • 직접호출: em.flush()
    • 자동호출: 트랜잭션 커밋
    • 자동호출: JPQL 쿼리 실행
  • commit: DB에 영구반영 (flush 호출되고 commit 호출)

DDL

  • hibernate.hbm2ddl.auto로 옵션을 설정
  • create: 기존 테이블 삭제 후 다시 생성(create+drop) → 개발초기단계
  • create-drop: create와 같으나 종료시점에 테이블 DROP → 테스트코드
  • update: 변경분만 반영(운영 DB에서는 사용하면 안 됨) → 개발초기단계, 테스트서버
  • validate: 엔티티와 테이블이 정상 매핑되었는지만 확인 → 테스트서버, 스테이징, 운영
  • none: 사용하지 않음 → 스테이징, 운영

1. 영속성 컨텍스트

  • 엔티티를 영구 저장하는 환경
  • 논리적 개념
  • 엔티티 매니저를 통해 영속성 컨텍스트에 접근
  • 일단은 엔티티 매니저와 영속성 컨텍스트가 비슷한 개념임
  • 하나의 트랜잭션 단위에서 작동
    • 서로 다른 트랜잭션에서는 캐시등이 적용되지 않음
  • DB 커넥션도 들고 있음

상태

  • 비영속: 객체 생성만함, JPA 안넣음
  • 영속: 객체 생성 후, 넣음(관리대상!) → em.persist(member);
    • persist 메소드는 영속상태로 만든다는 의미
  • 준영속: 관리하고 있었는데 잠시 떼어놓은 상태(트랜잭션 범위를 벗어나는 경우도 해당)
    • 변경감지 등이 되지 않음
    • em.clear()
    • em.detach(entity)
    • em.close()
  • 삭제

장점

  • 1차캐시
    • 트랜잭션 단위의 엄청 짧은 주기의 캐시
    • 맵 같은 느낌으로 ID를 key 값으로 entity를 value 값으로 저장함
    • 스냅샷도 저장함
    • 동일성 보장
    • 1차 캐시로 repeatableRead 등급의 트랜잭션 격리수준을 데이터베이스가 아닌 애플리케이션 차원에서 제공
  • 트랜잭션을 지원하는 쓰기지원(buffer 기능)
    • flush: DB에 보내는 시점(sync를 맞추기 위함)
    • commit: flush and commit, DB에 영구반영
  • 엔티티 수정(변경감지, DirtyChecking)
    • 1차 캐시되는 시점에 스냅샷도 같이 저장함
    • flush, commit 시점에 스냅샷을 비교하여 변경사항은 DB에 update 보냄
    • 마치 컬렉션에서 참조 객체를 수정하는 것처럼 관리됨
  • 엔티티 삭제
    • 트랜잭션 커밋시점에! em.remove(entity);

Lazy로딩: fetch = FetchType.Lazy

  • 실제 필요할 때 실제 값으로 교체
  • 생성시에는 프록시객체로 가지고 있다가 필요할 때 실제 값으로 교체
  • lazy 로딩을 사용하려면 final class를 하지 말아야 한다.
  • 가급적 지연로딩을 적용하는 것이 좋다.
    • 즉시로딩(FetchType.EAGER)을 사용하면 예상치 못한 SQL이 발생하여 N+1 발생가능
  • @ManyToOne, @OneToOne: 기본이 즉시로딩
  • @OneToMany, @ManyToMany: 기본이 지연로딩
  • 지연로딩을 하려면 프록시객체가 있어야 하고, 즉 영속성 컨텍스트가 있어야 함
  • 준영속상태에서는 지연로딩이 되지 않음

2. 연관관계 매핑

  • 객체를 데이터 의존적이게 모델링하면 협력관계를 만들 수 없다.
  • 객체와 테이블간의 연관관계 설정의 괴리를 줄여주는 역할

단방향 매핑

  • Member에서 Team 필드에

  • @JoinColumn(name = "team")

  • @ManyToOne(fetch = FetchType.LAZY)양방향 매핑

  • Team 에서 List<\Member>를 갖는다면?

  • @OneToMany(mappedBy = "team")

  • 객체는 양방향이 아니라 단방향이 2개 있는 것!

    • 객체를 양방향으로 참조하려면(Team -> List, Member-> Team) 단방향을 2개 만듦
  • 객체참조와 외래키는 성격이 다르므로 mappedBy가 필요

  • mappedBy

    • 연관관계의 주인을 설정하기 위해 사용, 주인이 아닌 쪽에서 mappedBy 사용
    • 연관관계의 주인만이 외래키를 관리(등록, 수정 등)
    • 주인이 아닌 쪽은 읽기만 가능
    • 나는 주인이 아님 -> mappedBy
    • 주인임 -> mappedBy 속성 사용하지 않음
    • 외래키가 있는 곳을 주인으로 정함
  • 주의사항

    • 연관관계 주인에 값을 입력해야함 (1번말고 2번처럼 해야함)

      team.getMembers().add(member) // 1. 외래키가 null로 들어감
      member.setTeam(team) // 2. ok
    • 1,2 코드 둘다 사용하는 것이 권장(객체사용의 의미로 보면)설계

  1. 먼저 단방향으로만 설계한다
  2. 이후 필요하다면 양방향 매핑을 해준다(조회를 편하게 하기 위한 부가기능)

기타

  • 모든 영속성컨텍스트는 트랜잭션 내에서 동작
  • propagation에 따라 트랜잭션이 동작하는게 다름
  • Id를 IDENTITY로 auto-increment로 설정하면 DB의 도움이 필요하므로 save 하는 순간 insert query 동작
  • 테스트코드에서는 어차피 rollback 할 것이라 쿼리를 안 날림JPQL
  • SQL을 추상화한 쿼리언어
  • 객체지향 SQL
  • 엔티티 이름을 사용함
  • 별칭은 필수
  • 프로젝션
    • 일부 데이터만 가져오는 방법
  • fetchJoin
    • 자주 같이 사용되는 객체에 설정
    • N+1 문제 해결
  • namedQuery
    • 애플리케이션 로딩 시점에 SQL 오류를 잡을 수 있음

Spring Data JPA

  • 지루하게 반복되는 CRUD 문제를 세련된 방법으로 해결
  • 개발자는 인터페이스만 생성

Query DSL

  • SQL, JPQL 문제점: 해당 로직이 실행전까지 작동여부 확인 불가, Type-check 불가
  • SQL, JPQL을 코드로 작성하게 해줌
  • 컴파일 시점에 문법오류 발견가능
  • IDE를 통해 코드 자동완성 도움
  • 동적쿼리(분기문등) 작성용이
  • 자바코드이기 때문에 extractMethod 하여 가독성을 높일 수 있음

참고