JPA (1) SQL 중심적 개발의 문제점과 JPA 특징
26 Feb 2022김영한님의 자바 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.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
- 1차 캐시와 동일성(identity) 보장
- ORM을 잘 쓰기 위해 객체지향과 관계형 DB 모두 잘 알아야 함