1장 JPA 소개
자바 ORM 표준 JPA 프로그래밍 1장을 요약한 내용 입니다.
SQL을 직접 다룰 때 발생하는 문제점
SQL
을 직접 다룰 때의 문제점을 알아보기 위해 자바
와 관계형 데이터베이스
를 사용해서 회원 관리 기능을 개발해보자.
먼저 회원을 조회하는 기능을 개발한다.
회원 조회용 SQL을 작성한다.
JDBC API를 사용해서 SQL을 실행한다.
조회 결과를 Member 객체로 실행한다.
이번엔 회원 등록 기능을 개발한다.
회원 등록용 SQL을 작성한다.
회원 객체의 값을 꺼내서 등록 SQL에 전달한다.
JDBC API를 사용해서 SQL을 실행한다.
회원 객체를 데이터베이스가 아닌 자바 컬렉션
에 보관한다면 어떨까? 컬렉션은 다음 한 줄로 객체를 저장할 수 있다.
따라서 개발자가 객체지향 애플리케이션
과 데이터베이스
중간에서 SQL
과 JDBC API
를 사용해서 변환
작업을 직접 해주어야 한다.
문제는 객체를 데이터베이스에 CRUD
하려면 너무 많은 SQL
과 JDBC API
를 코드로 작성해야 한다는 점이다. 데이터 접근 계층(DAO)
을 개발하는 일은 이렇듯 지루함과 반복의 연속이다.
SQL에 의존적인 개발
회원 객체를 관리하는 MemberDAO
를 완성하고 애플리케이션의 나머지 기능도 개발을 완료했다. 그런데 갑자기 회원의 연락처도 함께 저장해 달라는 요구사항
이 추가되었다.
등록 코드 변경
회원 클래스에 연락처 필드 추가
등록 SQL을 수정
회원 객체의 연락처 값을 꺼내서 등록 SQL에 전달한다.
조회 코드 변경
다음으로 회원 조회 화면에서 모든 연락처의 값이 null
로 출력되었다. 생각해보니 조회 SQL
에 연락처 컬럼을 추가하지 않았다.
조회용 SQL을 수정
연락처의 조회 결과를 Member 객체에 추가로 매핑한다.
연관된 객체
회원은 어떤 한 팀에 필수로 소속되어야 한다는 요구사항이 추가되었다. Member
객체에 team
필드가 추가되어 있었다. 그러나 코드를 실행해보니 member.getTeam()
의 값이 항상 null
이 나오고 있었다.
memberDAO
코드를 열어보니 회원을 출력할 때 사용하는 find()
메소드는 회원만 조회하는 SQL
을 그대로 유지하였다. 그래서 findWithTeam()
메서드를 추가하였다.
연관 팀 정보를 가져오는 SQL 추가
위의 사례에서 보듯이 Member
객체가 연관된 Team
객체를 사용할 수 있을지 없을지는 전적으로 사용하는 SQL
에 달려 있다. 이런 방식의 가장 큰 문제는 데이터 접근 계층
을 사용해서 SQL
을 숨겨도 어쩔 수 없이 DAO
를 열어서 어떤 SQL
이 실행 되는지 확인해야 한다는 점이다.
이렇듯 SQL
에 모든 것을 의존하는 상황에서는 개발자들이 엔티티
를 신뢰하고 사용할 수 없다. 대신에 DAO
를 열어서 어떤 SQL
이 실행되고 어떤 객체 들이 함께 조회 되는지 일일이 확인해야 한다.
앞의 문제점을 요약하면 다음과 같다.
진정한 의미의 계층 분할이 어렵다.
엔티티를 신뢰할 수 없다.
SQL에 의존적인 개발을 피하기 어렵다.
JPA와 문제 해결
JPA
를 사용하면 객체를 데이터베이스에 저장하고 관리할 때, 개발자가 직접 SQL
을 작성하는 것이 아니라 JPA
가 제공하는 API
를 사용하면 된다. 그러면 JPA
가 개발자 대신에 적절한 SQL
을 생성해서 데이터베이스에 전달한다.
이전에 구현하였던 애플리케이션의 기능을 JPA를 사용해서 구현 해보자.
조회 기능
수정 기능
연관된 객체 조회
패러다임의 불일치
비즈니스 요구사항을 정의한 도메인 모델도 객체로 모델링
하면 객체지향 언어가 가진 장점
들을 활용할 수 있다. 문제는 이렇게 정의한 도메인 모델
을 저장할 때 발생한다.
객체의 기능
은 클래스에 정의되어 있으므로 객체인스턴스
의상태
인 속성만 저장 했다가 필요할 때 불러 와서 복구하면 된다.관계형 데이터베이스
는 데이터 중심으로 구조화되어 있고,집합적인 사고
를 요구한다. 그리고 객체 지향에서 이야기하는추상화
,상속
,다형성
같은 개념이 없다.
객체와 관계형 데이터베이스는 지향하는 목적
이 서로 다르므로 둘의 기능
과 표현 방법
도 다르다. 이것을 객체와 관계형 데이터베이스의 패러다임 불일치 문제
라 한다. 따라서 객체 구조
를 테이블 구조
에 저장하는 데는 한계가 있다.
문제는 이런 객체와 관계형 데이터베이스 사이의 패러다임 불일치 문제를 해결하는 데 너무 많은 시간
과 코드
를 소비
하는 데 있다.
상속
객체는 상속 이라는 기능을 가지고 있지만 테이블은 상속 이라는 기능이 없다.
JDBC를 이용한 상속 구현
JDBC API를 사용해서 상속 관계의 객체를 저장 하려면 부모 객체
에서 부모 데이터
만 꺼내서 INSERT SQL을 작성하고 자식 객체
에서 자식 데이터
만 꺼내서 INSERT SQL을 별도로 작성해야 한다.
JPA를 이용한 상속 구현
JPA는 상속과 관련된 패러다임의 불일치 문제를 개발자 대신 해결해준다. 개발자는 마치 자바 컬렉션
에 객체를 저장 하듯이 JPA
에게 객체를 저장하면 된다.
연관관계
객체는 참조를 사용해서 다른 객체와 연관관계를 가지고 참조
에 접근
해서 연관된 객체를 조회한다. 반면에 테이블은 외래 키
를 사용해서 다른 테이블과 연관관계를 가지고 조인
을 사용해서 연관된 테이블을 조회한다.
테이블에 맞춘 객체 모델
그런데 여기서 TEAM_ID
외래 키의 값을 그대로 보관하는 teamId
필드에는 문제가 있다. 객체는 연관된 객체의 참조
를 보관해야 다음처럼 참조를 통해 연관된 객체를 찾을 수 있어야 하는데 하지 못하게 된다.
이런 방식을 따르면 좋은 객체 모델링은 기대하기 어렵고 결국 객체 지향의 특징을 잃어버리게 된다.
엔티티를 직접 참조하는 것과 간접 참조하는 것에 대한 장단점이 무엇이 있을까?
직접 참조
장점 : 연관된 데이터를
한번에
추출할 수 있다.단점 : 연관된 데이터에 대한 수정이 발생할 경우
영향의 범위
가 커질 수 있다.
간접 참조
장점 : 복잡도를 낮출 수 있고,
응집도
를 높이고결합도
를 낮출 수 있다.단점 : 연관된 데이터를 한번에 추출 하려면 구현해야 하는
로직
이 복잡하다.
참조를 사용하는 객체 모델
JDBC를 이용한 연관 관계 구현
만약 JDBC로 저장하는 로직을 만들기 위해서는 team 필드를 TEAM_ID 외래 키
값으로 변환해야 한다.
또는 조회하는 로직에도 객체를 생성하고 연관관계를 설정해서 반환하는 로직이 필요하다.
JPA와 연관관계 구현
개발자는 회원
과 팀
의 관계를 설정하고 회원 객체
를 저장하면 된다. JPA
는 team
의 참조를 외래 키로 변환해서 적절한 INSERT SQL을 데이터베이스에 전달한다.
객체를 조회할 때 외래 키를 참조로 변환하는 일도 JPA가 처리 해준다.
객체 그래프 탐색
SQL
을 직접 다루면 처음 실행하는 SQL
에 따라 객체 그래프를 어디까지 탐색할 수 있는지 정해진다. 이것은 객체지향 개발자에겐 너무 큰 제약이다. 왜냐하면 비즈니스 로직에 따라 사용하는 객체 그래프가 다른데 언제 끊어질지 모를 객체 그래프
를 함부로 탐색할 수는 없기 때문이다.
결국, 어디까지 객체 그래프 탐색이 가능한지 알아보려면 데이터 접근 계층인 DAO
를 열어서 SQL
을 직접 확인해야 한다.
JPA와 객체 그래프 탐색
JPA를 사용하면 연관된 객체를 신뢰하고 마음껏 조회할 수 있다. 이 기능은 실제 객체를 사용하는 기점까지 데이터베이스 조회를 미룬다고 해서 지연 로딩
이라 한다.
여기서 마지막 줄의 order.getOrderDate()
같이 실제 Order
객체를 사용하는 시점에 JPA는 데이터베이스에서 ORDER
테이블을 조회한다.
비교
데이터베이스는 기본 키의 값으로 각 로우(row)를 구분한다. 반면에 객체는 동일성(indentity)
비교와 동등성(equality)
비교라는 두 가지 비교 방법이 있다.
동일성 비교는 == 비교다. 객체 인스턴스의 주소 값을 비교한다.
동등성 비교는 equals() 메소드를 사용해서 객체 내부의 값을 비교한다.
JDBC로 구현한 비교
데이터베이스의 같은 로우를 조회했지만 객체의 동일성 비교에는 실패한다.
JPA로 구현한 비교
JPA는 같은 트랜잭션일 때 같은 객체가 조회되는 것을 보장한다. 그러므로 다음 코드에서 member1과 member2는 동일성 비교에 성공한다.
정리
객체 모델
과 관계형 데이터베이스 모델
은 지향하는 패러다임
이 서로 다르다. 더 어려운 문제는 객체 지향 애플리케이션 답게 정교한 객체 모델링을 할수록 패러다임의 불일치 문제가 더 커진다는 점이다. 이는 결국 객체 모델링은 힘을 잃고 점점 데이터 중심의 모델
로 변해간다. JPA는 패러다임의 불일치 문제를 해결 해주고 정교한 객체 모델링 유지하게 도와준다.
데이터 주도 설계와 도메인 주도 설계의 장단점은 무엇일까?
JPA란 무엇인가?
JPA(Java Persistence API)
는 자바 진영의 ORM 기술 표준이다.
그렇다면 ORM이란 무엇일까?
ORM(Object-Relational Mapping)
은 이름 그대로 객체와 관계형 데이터베이스를 매핑
한다는 뜻이다. ORM 프레임워크는 객체와 테이블을 매핑 해서 패러다임의 불일치 문제를 개발자 대신 해결해준다.
따라서 객체 측면에서는 정교한 객체 모델링을 할 수 있고 관계형 데이터베이스는 데이터베이스에 맞도록 모델링하면 된다. 그리고 둘을 어떻게 매핑 해야 하는지 매핑 방법만 ORM 프레임워크에게 알려주면 된다.
왜 JPA를 사용해야 하는가?
생산성
이전에
DAO
에서 작업하던 지루하고 반복적인 일은JPA
가 대신 처리 해준다. 이런 기능들을 사용하면 데이터베이스 설계 중심의 패러다임을객체 설계 중심
으로 역전시킬 수 있다.유지보수
이전엔
엔티티
필드 하나만 수정해도 관련된DAO
로직의SQL
문을 모두 변경해야 했다. 반면에JPA
는 대신 처리해주므로 필드를 추가하거나 삭제해도 수정해야 할 코드가 줄어든다.패러다임의 불일치 해결
성능
JPA는 애플리케이션과 데이터베이스 사이에 동작하여
최적화
관점에서 시도해 볼 수 있는 것들이 많다. 예를 들어 동일한 조건으로 조회 했을 경우엔 SELECT SQL을 한 번만 데이터베이스에 전달하고 두 번째 조회한 회원 객체는재사용
할 수 있다.데이터 접근 추상화와 벤더 독립성
관계형 데이터베이스는 같은 기능도 벤더 마다 사용법이 다른 경우가 많다. 단적인 예로
페이징 처리
는 데이터베이스마다 달라서 사용법을 각각 배워야 한다. 결국 애플리케이션은 데이터베이스에종속
되어 변경 하기는 매우 어렵다.**JPA
는 추상화된 데이터 접근 계층을 제공해서 애플리케이션이 특정 데이터베이스 기술에 종속되지 않도록 할 수 있다.**
ORM에 대한 궁금증과 오해
JPA를 사용하면 성능이 느리지 않나요?
JPA
는 다양한 성능 최적화
기능을 제공해서 잘 이해하고 사용하면 SQL
을 직접 사용할 때보다 더 좋은 성능을 낼 수도 있다. 하지만 JPA를 잘 이해하지 못하고 사용하면 N+1
같은 문제로 인해 심각한 성능 저하가 발생할 수 있다.
JPA N+1 문제란 무엇인가? 해결 방법은 또한 무엇인가?
통계 쿼리처럼 매우 복잡한 SQL은 어떻게 하나요?
JPA는 통계 쿼리 같이 복잡한 쿼리보다는 실시간 처리용 쿼리
에 더 최적화되어 있다. 따라서 통계용은 네이티브 SQL
을 사용하거나 마이바티스
나 스프링의 jdbcTemplate
같은 SQL 매퍼 형태의 프레임워크를 혼용하는 것도 좋은 방법이다.
Last updated