BOOK 3 - 자바 ORM 표준 JPA 프로그래밍(1)
JPA 소개
SQL을 직접 다룰 때 발생하는 문제점
- Java application은 JDBC API를 이용해서 SQL을 데이터베이스에 전달한다.
반복되는 DAO와 SQL 구현 작업
- 개발자는 DAO(Data Access Object)와 JDBC API를 사용해서 실행할 SQL을 각 객체의 CRUD 요구사항마다 다음 과정에 따라 직접 구현해야 한다.
- SQL을 작성한다.
- JDBC API를 사용해서 SQL을 실행한다.
- 조회 결과를 객체로 매핑한다.
- 위 과정이 모든 객체의 CRUD에서 반복되게 된다.
SQL에 의존적인 개발
-
기존 기능에서 추가적인 요구사항이 발생하여 기존 객체에 필드를 하나 더 추가하게 된 경우, 해당 객체가 연관된 모든 CRUD 작업 SQL과 DAO를 일일이 수정해야 한다.
-
CRUD 관련 이슈가 발생했을 때 DAO를 열어서 어떤 SQL이 실행되는지 직접 확인해야 한다.
- 물리적으로는 SQL과 JDBC API를 DAO에 숨겨서 계층 분할을 했지만, 논리적으로는 entity와 아주 강한 의존관계를 가지고 있어 객체에 필드를 하나 추가할 때도 DAO의 CRUD 코드와 SQL을 대부분 변경해야 하는 문제가 발생한다.
문제점 정리
- 진정한 의미의 계층 분할이 어렵다.
- 엔티티를 신뢰할 수 없다.
- SQL에 의존적인 개발을 피하기 어렵다.
JPA와 문제 해결
- JPA를 사용하면 객체를 DB에 저장하고 관리할 때, 개발자가 직접 SQL을 작성하는 것이 아니라 JPA가 제공하는 API를 사용하면 된다.
- JPA가 적절한 SQL을 생성해서 데이터베이스에 전달한다.
패러다임의 불일치
- 객체지향 프로그래밍은 추상화, 캡슐화, 상속, 다형성 등 시스템의 복잡성을 제어할 수 있는 다양한 장치들을 제공한다.
- 이러한 장치들을 활용하여 비즈니스 요구사항을 정의한 도메인 모델을 객체로 모델링 할 수 있다.
- 객체는 속성과 기능을 가지고, 기능은 클래스에 정의되어 있으므로 인스턴스의 상태인 속성만 저장하면 된다.
- 관계형 데이터베이스는 데이터 중심으로 구조화되어 있고, 객체지향에서의 추상화, 상속, 다형성 같은 개념이 없다.
- 즉, 객체와 관계형 데이터베이스는 지향하는 목적이 서로 다르므로 둘의 기능과 표현 방법도 다르다.
- 이것을 객체와 관계형 데이터베이스의 패러다임 불일치 문제라고 부르고, 이 문제로 객체 구조를 테이블 구조에 저장하는 데에 한계가 있다.
- JPA는 이러한 패러다임 불일치 문제를 다양한 방식으로 해결해준다.
상속
- 예를 들어
Item이라는 부모 객체를 상속받는Album,Movie,Book이라는 자식 객체들이 있다고 가정해보자. - 기존의 SQL을 직접 구현하는 과정에서는
Album객체를 저장하기 위해서 객체를 분해해서 두개의 SQL을 만들어 실행해야 한다.INSERT INTO ITEM ...INSERT INTO ALBUM ...
JPA와 상속
- JPA를 사용하면 개발자는 마치 자바 Collection에 객체를 저장하듯이 JPA에게 객체를 저장하면 된다.
jpa.persist(album)와 같이 JPA에서 제공하는 메소드를 사용하면 JPA는 내부적으로 위의 두 SQL을 실행해서 객체를ITEM,ALBUM테이블에 나누어 저장한다.- 조회 역시
jpa.find(Album.class, albumId)와 같이find()메서드를 사용하면 JPA가 내부적으로ITEM과ALBUM테이블을 조인하여 필요한 데이터를 조회하고 그 결과를 객체에 매핑해준다.
연관관계
- 객체는 reference를 사용해서 다른 객체와 연관관계를 가지고, reference에 접근해서 연관된 객체를 조회한다.
- 테이블은 foreign key를 사용해서 다른 테이블과 연관관계를 가지고, join을 사용해서 연관된 테이블을 조회한다.
- 참조를 사용하는 객체와 외래 키를 사용하는 관계형 데이터베이스 사이의 패러다임 불일치가 발생하게 된다.
- 예를 들어, 회원이 팀에 속하는 비즈니스 모델을 가정해보자.
Member객체에 속한Team객체 참조를 통해서member.getTeam()같은 방식으로 연관된 팀을 조회할 수 있다.MEMBER테이블은MEMBER.TEAM_ID외래 키 컬럼을 사용해서TEAM테이블과 조인하여 연관된 팀을 조회할 수 있다.
- 객체는 참조가 있는 방향으로만 조회할 수 있지만, 테이블은 외래 키 하나로 양방향 조회가 가능하다.
- 예를 들어, 회원이 팀에 속하는 비즈니스 모델을 가정해보자.
- 기존에는
Member객체를 저장하려면Member객체 내의team필드를TEAM_ID외래 키 값으로 변환하는 과정이 필요했다.- 조회 시에는
TEAM_ID외래 키 값을 통해TEAM테이블과 조인하여team필드로 변환해야 한다. - 패러다임 불일치를 해결하기 위해 소모하는 비용이 너무 많이 들어간다.
- 조회 시에는
JPA와 연관관계
-
JPA를 사용하면 개발자는 회원과 팀의 관계를 설정하고, 회원 객체를 저장하기만 하면된다.
member.setTeam(team); jpa.persist(member);- JPA는 내부적으로
team필드의 참조를 외래 키로 변환해서 적절한 INSERT SQL을 DB에 전달한다.
- JPA는 내부적으로
객체 그래프 탐색
-
객체에서 회원이 소속된 팀을 조회할 때는 reference를 사용해서 연관된 팀을 찾으면 되는데, 이것을 객체 그래프 탐색이라 한다.
-
다음과 같은 객체 그래프에서 객체는 마음껏 객체 그래프를 탐색할 수 있어야 한다.

-
SQL을 직접 구현하는 경우에 처음 실행하는 SQL에 따라 객체 그래프를 어디까지 탐색할 수 있는지 정해진다.
-
즉, 다음과 같은 쿼리가 실행된 경우에는
Member객체에서Team객체까지밖에 탐색할 수 없다.SELECT M.*, T.* FROM MEMBER M JOIN TEAM T ON M.TEAM_ID = T.TEAM_IDmember.getOrder()를 실행했을 때null값이 나오게 되는 것이다.
-
비즈니스 로직에 따라 사용하는 객체 그래프가 달라지는 상황에서 언제 끊어질지 몰느느 객체 그래프를 함부로 탐색할 수 없게 되어 큰 제약이 생기게 된다.
-
-
JPA와 객체 그래프 탐색
- JPA를 사용하면 객체 그래프를 마음껏 탐색할 수 있다.
- JPA는 연관된 객체를 사용하는 시점에 적절한 SELECT SQL을 실행한다.
Lazy Loading: 실제 객체를 사용하는 시점까지 데이터베이스 조회를 미루는 기능- JPA는 lazy loading을 transparent하게 처리하여
Member엔티티 클래스의getOrder()메소드 구현 부분에 JPA와 관련된 어떤 코드도 직접 사용하지 않는다.
비교
- 데이터베이스는 primary key 값으로 각 row를 구분한다.
- 객체는 identity 비교와 equality 비교라는 두가지 방법으로 각 인스턴스를 구분한다.
- identity 비교(
==) : 객체 인스턴스의 주소 값 비교 - equality 비교 (
equals) : 객체 내부의 값 비교
- identity 비교(
memberDao.getMember()메서드는 호출 시마다 새로운Member인스턴스를 생성하여 반환하므로 데이터베이스의 같은 로우를 조회했지만 객체 동일성 비교에 실패하게 된다.
JPA와 비교
-
JPA는 같은 트랜잭션일 때 같은 객체가 조회되는 것을 보장한다.
-
다음 코드에서
member1과member2는 동일성 비교에 성공한다.String memberId = "100"; Member member1 = jpa.find(Member.class, memberId); Member member2 = jpa.find(Member.class, memberId); member1 == member2; // true
JPA란 무엇인가?
- JPA(Java Persistence API)는 자바 진영의 ORM 기술 표준이다.
ORM (Obejct-Relational Mapping)
- ORM 프레임워크는 객체와 테이블을 매핑해서 패러다임의 불일치 문제를 개발자 대신 해결해준다.
- 예를 들어 객체를 데이터베이스에 저장할 때 INSERT SQL을 직접 작성하는 것이 아니라 객체를 마치 Java Collection에 저장하듯이 구현하면 ORM 프레임워크가 다음 과정을 거쳐 데이터베이스에 객체를 저장 / 조회해준다.
- entity 분석
- SQL 생성
- JDBC API 사용
- ResultSet 매핑
- 패러다임 불일치 해결
- ORM 프레임워크는 SQL을 대신 생성해서 데이터베이스에 전달해주는 것뿐만 아니라 다양한 패러다임 불일치 문제들도 해결해준다.
- 따라서 개발자는 데이터 중심인 관계형 데이터베이스를 사용해도 객체지향 어플리케이션 개발에 집중할 수 있다.
JPA 소개
- JPA는 자바 ORM 기술에 대한 API 표준 명세 이다.
- 즉, Interface를 모아둔 것이므로 JPA를 사용하려면 JPA를 구현한 ORM 프레임워크를 선택해야 한다.
-
현재 JPA를 구현한 ORM 프레임워크는
Hibernate,EclipseLink,DataNucleus가 있고, 그 중 하이버네이트가 가장 대중적이다. - JPA라는 표준이 존재하여 특정 구현 기술에 대한 의존도를 줄일 수 있고, 다른 구현 기술로 손쉽게 교체 가능하다는 장점이 있다.
JPA를 사용해야 하는 이유
생산성
- JPA를 사용하면 SQL을 직접 작성하는 대신 자바 컬렉션에 객체를 저장하듯이 JPA에게 저장할 객체를 전달하면 된다.
- 개발자는 반복적인 CRUD용 SQL을 작성하는 작업을 하지 않아도 된다.
유지보수
- SQL을 직접 다루면 entity에 필드 하나만 추가해도 관련된 CRUD SQL과 결과를 매핑하기 위한 JDBC API 코드를 모두 변경해야 한다.
- JPA를 사용하면 이런 과정을 JPA가 대신 처리해주므로 비즈니스 요구사항이 변경되어도 유지보수해야 하는 코드가 줄어든다.
- 또한, JPA가 패러다임 불일치 문제를 해결해주므로 객체지향 언어가 가진 장점들을 활용해서 유연하고 유지보수하기 좋은 도메인 모델을 설계할 수 있다.
성능
- JPA는 어플리케이션과 데이터베이스 사이에서 동작하여 하나의 트랜잭션으로 데이터베이스와의 한번의 통신을 통해 여러 개의 쿼리를 처리하고 객체를 재사용할 수 있다.
데이터 접근 추상화와 벤더 독립성
-
관계형 데이터베이스는 같은 기능도 벤더마다 사용법이 다른 경우가 많다.
-
JPA는 어플리케이션과 데이터베이스 사이에 추상화된 데이터 접근 계층을 제공해서 어플리케이션이 특정 데이터베이스 기술에 종속되지 않도록 한다.

- 예를 들어 로컬 개발 환경은 H2를 사용하고, 운영 환경은 오라클이나 MySQL을 사용할 수 있다.