개발 일기

[섹션1] JPA 소개 본문

Back-End/자바 ORM 표준 JPA 프로그래밍

[섹션1] JPA 소개

개발 일기장 주인 2024. 3. 24. 02:37

SQL 중심적인 개발의 문제점

데이터베이스는 대게 관계형 DB를 활용한다.

즉, 실무에서 객체를 관계형 DB에 보관하고 관리한다.

이 과정에서 CRUD(CREATE, READ, UPDATE, DELETE)를 하는 과정에서 수많은 SQL이 쓰일 것이다.

이것이 바로 "SQL 중심적 개발"이다.

만약, 요구사항 변경으로 Member 객체에 tel이라는 연락처 필드를 추가해야 하는 경우 기존 INSERT, SELECT 그리고 UPDATE 쿼리문 들에 TEL이라는 필드를 찾아가서 하나하나 추가해주어야 하기 때문에 상당히 효율적이지 않다.

 

 실무에서는 8~90% 관계형 데이트베이스를 채택하고 NoSQL을 보조적으로 쓸텐데 그렇다면 객체를 관계형 데이터베이스로 다루게 되는데 이때 둘 사이에 차이가 존재한다. 하나씩 살펴보자

 

1. 상속

상품을 팔려고 하는 경우 아이템이라는 것을 추상화하여 부모로 두고 그 밑에 앨범, 영화, 책을 두어 위와 같은 객체 상속 관계를 구성했다.

그렇다면 관계형 데이터베이스에는 객체간의 상속 관계를 어떻게 풀어내야 할까?
바로 데이터베이스 설계 기법 중 슈퍼타입 서브타입 관계를 사용하는 것이다.

 

  • SQL 중심적 개발
    : 앨범 상품 추가 시에 객체 분해 후 INSERT문을 ITEM 테이블과 ALBUM 테이블 각각에 던져 줘야 한다. 조회 시에는 ITEM과 ALBUM 테이블을 JOIN 해야 하고 MOVIE, BOOK에 대해서도 JOIN 쿼리를 추가적으로 해줘야 한다. 그 후로도 복잡한 과정을 거쳐야 한다.
  • 자바 컬렉션에 저장한다면?
    : list.add(album)으로 추가하고 조회할 때에도 Album album = list.get(albumId); 또는 부모 타입으로 조회 후 다형성을 활용하는 Item item = list.get(albumId);도 가능하다.

2. 연관관계


객체 연관관계의 경우 참조를 사용한다. member.getTeam();을 통해서 멤버의 팀을 조회가능하다.
그러나 위와 같은 연관관계를 테이블의 경우 외래 키를 통해 연결된 테이블을 JOIN을 통해 조회 가능하다.

  • SQL 중심적 개발
    : 맴버 객체를 팀 객체를 테이블에 저장하려면 Member에 team에 대한 참조가 아닌 teamId를 들고 있어야 한다. 그래야지 INSERT 할 때 TEAM_ID를 통해서 쉽게 쿼리 할 수 있다.
    그러나 이것은 객체다운 모델링이라고 볼 수 없다. 객체 연관관계는 참조를 통해 연관관계를 맺는다. 그래서 객체다운 모델링을 하려면  member.getTeam()을 통해 바로 꺼낼 수 있어야 한다. 그렇기 때문에 teamId 대신 team 객체를 넣어 참조로 연관관계를 맺는다. 그 대신 그렇게 되면 INSERT 하기 힘들어진다. DB에다가 team 객체를 넣을 수 없기 때문에 어쨌든 쿼리를 통해 TEAM_ID를 넣어야 하기 때문에 member.getTeam().getId();가 되어야 한다. 등록은 그나마 이렇게 간단하다.
    그러나 조회의 경우 멤버 테이블과 팀 테이블을 조인하여 한번에 들고온다. 그런 다음 맴버 객체와 팀 객체를 각각 만들어 쿼리한 데이터를 세팅해주고 member.setTeam(team);을 통해서 member 객체에 teamId가 아닌 실제 팀 객체를 넣어주는 과정을 거쳐야 한다. 참 번거로운 과정을 거쳐야 한다.
  • 자바 컬렉션에 저장한다면?
    : list.add(member);로 등록하고 list.get(memberId);로 맴버 객체를 꺼내고 member.getTeam();을 통해 Team도 바로 꺼낼 수 있다.

 

3. 엔티티 신뢰 문제

객체는 자유롭게 객체 그래프를 탐색할 수 있어야 한다. ex) Member.getOrder().orderItem.Item() 
그러나 SQL 중심적이라면 실행한 쿼리문에 따라 탐색 범위가 결정된다. ex) 멤버 TB, 팀 TB 조인 시에 member.getOrder() 불가

그렇지만 모든 객체를 미리 로딩해 둘 수는 없다.

 

4. 비교하기

SQL을 통해 조회하게 되면 서로 다른 인스턴스가 되기 때문에 다르지만 자바 컬렉션에서 조회하면 같은 데이터가 담겨서 같다고 나온다.

// SQL을 통한 조회
String memberId = "100";
Member member1 = memberDAO.getMember(memberId);
Member member2 = memberDAO.getMember(memberId);

member1 == member2; //다르다. 

class MemberDAO {
    public Member getMember(String memberId) {
    String sql = "SELECT * FROM MEMBER WHERE MEMBER_ID = ?";
    ...
    //JDBC API, SQL 실행
    return new Member(...);
    }
}

// 자바 컬렉션을 통한 조회
String memberId = "100";
Member member1 = list.get(memberId);
Member member2 = list.get(memberId);

member1 == member2; //같다.

 

위와 같이 객체답게 모델링을 하기 위해서 맵핑 작업이 늘어나서 "객체를 자바 컬렉션에 저장하듯이 DB에 저장할 수는 없을까?"라는 고민을 하게 되었고 그래서 그 문제를 해결해 주는 것이 JPA(Java Persistence API)이다.

JPA - Java Persistence API

JPA 소개

ORM이란?
: Object-Relational Mapping(객체 관계 매핑)의 약어로 객체와 관계형 데이터베이스 사이를 연결해 주는 기술로 객체는 객체대로, 관계형 데이터베이스는 관계형 데이터베이스대로 설계하고 ORM 프레임워크가 중간에서 매핑해 주는 것이다.

JPA는 애플리케이션과 JDBC 사이에서 동작한다. 

JPA 동작 저장(좌), 조회(우)

내가 이전에 쓴 게시글에서와 같이 EJB 엔티티 빈을 쓰다가 불편하여 하이버네이트(오픈 소스)가 만들어졌고 사람들이 많이 쓰기 시작하여 하이버네이트를 기반으로 JPA를 새로 만들어 자바 표준으로 만들었다.

즉, JPA는 표준 명세이다. 인터페이스의 모음이고 이를 구현한 3가지 구현체인 Hibernate, EclipseLink, DataNucleus가 있는데 당연히 Hibernate가 대세이다. 즉, JPA 인터페이스에 Hibernate 구현체를 쓴다고 생각하면 된다.

 

JPA를 사용해야 하는 이유?

SQL 중심적인 개발에서 객체 중심으로 개발

생산성
- 저장: jpa.persist(member)
- 조회: Member member = jjpa.find(memberId)
- 수정: member.setName("변경할 이름")
- 삭제: jpa.remove(member)

 

유지보수
: 위에서 필드가 추가될 경우 쿼리문을 일일이 찾아가 수정해줘야 한다고 했었다. 그러나 JPA의 경우 필드만 추가해 주면 SQL은 JPA가 알아서 처리해 준다.

 

패러다임의 불일치 해결
: 위에서 언급한 상속, 연관관계, 객체 그래프 탐색, 비교하기를 모두 해결해 준다.

 

성능 최적화

  1. 1차 캐시와 동일성 보장
    : 같은 트랜잭션 안에서는 같은 엔티티를 반환한다. 처음에는 SQL 쿼리가 나가는데 이것을 캐시에 들고 있다가 다시 요청되면 캐시에서 들고 온다. 덕분에 동일성도 보장된다.
  2. 트랜잭션을 지원하는 쓰기 지연(transactional write-behind)
    : 트랜잭션을 커밋할 때까지 INSERT SQL을 모은다. 즉, JDBC BATCH SQL 기능을 통해 한번에 SQL을 전송하는 것이다.
    네트워크 통신 비용이 절약된다. 비즈니스 로직 수행 동안에 UPDATE와 DELETE로 인한 로우 락이 걸리지 않는다.
  3. 지연 로딩(Lazy Loading)
    : 객체가 실제 사용될 때 로딩한다. <-> 즉시 로딩의 경우 JOIN 쿼리를 통해 한번에 연관된 객체까지 미리 끌어온다.

데이터 접근 추상화와 벤더 독립성

표준