JPA (1) SQL 중심적 개발의 문제점과 JPA 특징

김영한님의 자바 ORM 표준 JPA 프로그래밍 강의 정리

SQL 중심적인 개발의 문제점

  • 개발 언어는 객체지향 & DB는 관계형
  • 객체를 관계형 DB에 관리
  • CRUD 쿼리, 자바 객체 -> SQL, SQL -> 자바 객체 반복
  • 새로운 필드가 추가 될 경우 기존 쿼리 다 변경해야 함
  • 객체 지향과 관계형의 패러다임 불일치
  • 객체와 RDB의 차이
    • 상속
      • RDB에서는 객체의 상속 관계 같은 관계가 없음
      • 비슷하게 슈퍼타입-서브타입 관계가 있지만 데이터를 추가할 때 쿼리를 여러 번 작성해야 하며 조회할 때마다 JOIN 필요
    • 연관관계
      • 객체에서는 참조를 통해 연관된 객체를 가져올 수 있음 (ex. member.getTeam())
      • RDB에서는 외래키를 이용해 JOIN을 통해 가져옴
      • 객체를 테이블에 맞추어 모델링하게 됨(외래키 필드)
      • 외래키 필드를 객체로 바꾸게 되면 객체를 저장할 떄 참조를 통해 데이터를 넣어야 함
    • 데이터 타입
    • 데이터 식별 방법
  • 객체 그래프 탐색
    • 처음 실행하는 쿼리에 따라 탐색 범위가 결정되므로 객체 그래프 탐색이 자유롭지 않음
    • 엔티티 신뢰 문제 발생(값이 null인 경우 발생할 수 있음)
  • 객체답게 모델링하게 되면 매핑 작업이 많아짐

JPA

  • Java Persistence API
  • Java 진영의 ORM 기술 표준
  • ORM
    • Object relational mapping(객체 관계 매핑)
    • 객체는 객체대로 설계
    • 관계형 데이터베이스는 관계형 데이터베이스대로 설계
    • ORM 프레임워크가 중간에서 매핑
  • 애플리케이션과 JDBC 사이에서 동작
    • JPA가 JDBC API를 호출해서 DB에 SQL을 보내고 DB에서 반환한 결과 전달
  • 동작 과정 예시 (저장)
    • MemberDAO에서 JPA에 객체를 넘김
    • JPA 작업
      • Entity 분석
      • INSERT SQL 생성
      • JDBC API 이용
      • 패러다임 불일치 문제 해결 JPA 동작
  • 높은 생산성
    • 저장: jpa.persist(member)
    • 조회: Member member = jpa.find(memberId)
    • 수정: member.setName(“new name”)
    • 삭제: jpa.remove(member)
  • 패러다임 불일치 해결
    • 상속: 상속 관계의 엔티티를 저장할 경우 JPA가 알아서 여러 쿼리를 작성
    • 조회: JOIN으로 가져와야 하는 관계를 처리해줌
    • 연관관계 & 객체 그래프 탐색
        // 연관 관계 저장
        member.setTeam(team);
        jpa.persist(member);
              
        // 객체 그래프 탐색
        Member member = jpa.find(Member.class, memberId);
        Team team = member.getTeam();
      
    • 신뢰할 수 있는 엔티티: JPA 통해 가져온 객체로 연관관계의 객체를 가져올 때 쿼리가 나가서 신뢰할 수 있음
  • 성능 최적화 기능
    • 1차 캐시와 동일성(identity) 보장
      • 같은 트랜잭션 안에서는 같은 엔티티 반환
      • DB 격리 수준이 Read Commit이어도 애플리케이션에서는 Repeatable Read 보장
    • 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
      • 트랜잭션을 커밋할 때까지 INSERT 쿼리를 모음
      • JDBC BATCH SQL 기능을 이용해 한 번에 쿼리 전송
        transaction.begin(); // 트랜잭션 시작
              
        em.persist(memberA);
        em.persist(memberB);
              
        transaction.commit() // 커밋할 때, DB에 INSERT 쿼리 전송
      
    • 지연 로딩(lazy loading)
      • 지연 로딩: 객체가 실제 사용될 때 로딩
      • 즉시 로딩: JOIN SQL로 한 번에 연관 객체까지 미리 조회
        // 지연 로딩
        Member member = memberDAO.find(memberId); // SELECT * FROM MEMBER
        Team team = member.getTeam();			  // SELECT * FROM TEAM
        String teamName = team.getName();
              
        // 즉시 로딩
        Member member = memberDAO.find(memberId); // SELECT M.*, T.* 
        Team team = member.getTeam();			  // FROM MEMBER M 
        String teamName = team.getName();   	  // JOIN TEAM T
      
  • ORM을 잘 쓰기 위해 객체지향과 관계형 DB 모두 잘 알아야 함