본문 바로가기
Back-end/java spring

[JPA] Java 표준 ORM인 JPA란?

by kkkdh 2023. 1. 19.
728x90

SQL 중심적인 개발의 문제점

  • 무한 반복, 지루한 코드..
  • 기획 단계에서 잘못되어 새로운 필드를 테이블에 추가하려 하는 경우, 관련된 모든 쿼리를 전부 변경해야 한다.
  • 하지만, 관계형 데이터베이스를 대부분의 케이스에서 사용하기 때문에, SQL에 의존한 개발을 할 수밖에 없다..!!

개발을 하면서 객체라는 개념을 다양한 저장소에 저장하게 되는데, 이 중 현실적인 대안은 관계형 데이터베이스이다.

개발자는 사실상 객체를 관계형 데이터베이스에 저장하기 위한 mapping 과정을 도맡아 한다고 볼 수 있음

개발자 = SQL 매퍼

객체와 관계형 데이터베이스의 차이

  1. 상속
  2. 연관관계
  3. 데이터 타입
  4. 데이터 식별 방법

상속

데이터베이스의 테이블에는 상속 관계가 없다.

부모 테이블을 만들고, 자식 테이블을 만들어 관리하는 방식으로 흉내 낼 수 있고, 이를 슈퍼타입 서브타입으로 만들어 관리한다고 부른다.

 

이렇게 관리하는 경우 Album 아이템을 하나 저장하려면, 다음 과정이 진행된다.

  1. 객체의 분해
  2. insert into item ~
  3. insert into album ~

그나마, 저장은 두 개의 쿼리로 끝나는데, 조회 기능은 훨씬 더 많은 과정이 필요하다.

  1. 조인 SQL이 필요..
  2. 각 객체의 생성
  3. 그 이상의 복잡한 과정...
  4. ...
  5. 그래서 DB에 저장할 객체에는 상속 관계를 쓰지 않는다고 한다.

연관관계 표현

컬랙션을 이용해서 객체들을 관리하는 것과 다르게 데이터베이스를 통한 관리는 객체 그래프 탐색 과정도 쉽지 않다.

 

하지만, 자바 컬렉션을 이용해 객체를 추가하고 조회 하는 기능들을 구현하는 것은 매우 간단하다. (CRUD 모두 관계형 데이터베이스와 연동하는 과정보다 훨씬 간단하다)

 

객체를 테이블에 맞춰 모델링 하면, 다음과 같이 모델링을 해야 하는데

쿼리문에 매핑하기 위한 모델링

하지만, 객체다운 모델링은 다음과 같다. (식별자가 아닌 참조를 통해 다른 객체와의 연관 관계를 표현한다.)

하지만, 이렇게 모델링 하기 위해서는 다음과 같은 복잡한 매핑 과정이 추가로 요구된다.

객체에 정보를 입력하고, 관계를 또 추가적으로 설정해줘야..

위 과정을 연관된 객체만큼 수행해야 하는 문제가 생긴다.. (너무 노가다이다)

 

객체 그래프 탐색의 문제

객체는 자유롭게 객체 그래프를 탐색할 수 있어야 한다. 그런데, 데이터베이스는 처음 실행하는 SQL에 따라서 탐색할 수 있는 범위가 결정된다.

 

계층형 구조에서는 다음 계층을 믿고 사용할 수 있어야 하는데, 이 구조에서 문제가 발생한다.

다른 사람이 개발한 DAO를 사용하는 상황을 가정해보자.

이런 경우 해당 객체를 통해 다른 객체에 접근하는 것 자체에 대한 엔티티 신뢰성 문제가 발생

 

memberDAO의 find method를 직접 봐야 확신할 수 있다..

 

그렇다고 모든 객체를 전부 데이터베이스에서 로딩하는 것은 괜찮을까..??

(객체 그래프를 따라갈 수 있도록 모든 데이터를 로딩하는 것도 성능에 문제가 생길 것이다.)

 

엄청난 join query와 query 결과가 튀어나올 것이다..

 

그래서 이런 식으로 코드를 짠다고 한다.

상황에 맞는 method를 DAO에서 갖다 쓸 수 있도록..

 

이렇게 되면, 계층형 아키텍처에서 진정한 의미의 계층 분할이 어렵다.

(DAO 객체에서 알아서 조회해줬으면 싶은데, 메서드를 골라서 써야 하는 문제 때문인 것 같다)


비교하기

같은 memberId 여도 다른 member 객체를 반환한다.

보통의 DAO는 데이터베이스에서 가져온 정보를 새로 생성한 객체에 담아서 전달해 주기 때문에, 같은 데이터의 멤버를 두 번 조회해도, 서로 비교했을 때 다르다는 결과를 얻게 될 것이다.

 

하지만, 자바 컬렉션에서 조회한다면??

같은 객체의 인스턴스를 반환해 줄 것이기 때문에, 같다는 결과를 얻을 것


이렇듯이 뭔가 객체 지향적으로 개발한다고 하고 있는데, 객체답게 모델링을 할수록 mapping 작업이 어마어마하게 증가하는 문제가 발생한다..

 

그래서 객체를 자바 컬렉션을 사용해 저장하듯이 DB에 저장할 수 없을까..??라는 연속된 고민을 해결해 준 것이 JPA라는 기술이라고 한다.


JPA (Java Persistence API)

자바 진영의 ORM 기술 표준이다.

 

그렇다면 ORM은 뭘까?

  • Object - Relational Mapping (객체와 관계형 데이터베이스를 매핑해 준다.)
  • 객체는 객체대로 설계하고
  • 관계형 데이터베이스는 관계형 데이터베이스대로 설계한다.
  • 중간에서 ORM 프레임워크가 매핑해서 해결해 준다.
  • 대중적인 언어에는 대부분 ORM 기술이 존재한다.

 

JPA가 애플리케이션과 DB 사이에서 동작하는 구조 다이어그램

JDBC API와 애플리케이션 사이에서 동작한다.

JPA가 패러다임 불일치 문제를 해결해 준다.

기존의 복잡하던 조회, 저장 과정을 JPA에서 도맡아 해준다.

 

EJB라는 것이 기존의 자바 표준이었는데, 이것의 사용에 불편함을 느낀 개발자들이 모여 하이버네이트를 개발했다.

 

이후에 하이버네이트의 개발자를 자바에서 초빙해 지금은 JPA라는 자바 표준 ORM이 탄생했다.

 

JPA는 표준 명세이다.

  • JPA는 인터페이스의 모음
  • 따라서 JPA 2.1 표준 명세를 구현한 3가지 구현체가 있다.
  • 하이버네이트, EclipseLink, DataNucleus

거의 80% 이상이 구현체로 hibernate를 사용한다고 함

 

따라서 우리는 하이버네이트를 사용하자.


JPA를 왜 사용해야 할까?

  • SQL 중심적인 개발에서 객체 중심으로 개발
  • 생산성
  • 유지보수
  • 패러다임의 불일치 해결
  • 성능
  • 데이터 접근 추상화와 벤더 독립성
  • 표준임
참고: JPA가 관리하는 객체를 entity라고 한다.

생산성 향상

앞의 긴 코드들을 위와 같이 간단한 코드로 줄여준다.

 

유지보수

기존에는 필드를 변경하면, 코드에서 필드도 변경하고 필드가 들어간 SQL문도 직접 하나하나 전부 바꿔줘야 했다..

하지만, JPA를 사용하면 필드만 추가하면 된다. SQL은 JPA가 처리해 준다.

 

패러다임의 불일치 해결

어떻게 보면, 가장 중요한 포인트이다.

  1. JPA와 상속
  2. JPA와 연관관계
  3. JPA와 객체 그래프 탐색
  4. JPA와 비교하기

위의 문제들을 JPA가 모두 해결해 준다.

 

객체의 상속은 데이터베이스에서 table의 슈퍼타입 - 서브타입 관계로 표현되었다.

 

이를 매핑하기 위해서는 복잡한 과정이 필요했는데, JPA는 개발자가 할 일을 다음과 같이 줄여준다.

개발자가 persist method만 호출하면, 슈퍼타입과 서브타입에 객체를 알아서 추가해 준다.

조회 상황에서는 join까지 알아서 수행해 준다.

 

연관 관계의 설정도 굉장히 쉬워짐

이렇게만 해도 연관 관계가 설정이 된다.

JPA에서 find method를 이용해 entity를 반환 받아도 연관 관계가 설정되어있다. (내부적인 복잡한 과정들을 성능 최적화까지 고려해서 알아서 지원해준다고 함)

 

그래서 JPA를 사용하면, 이제는 entity 계층을 신뢰할 수 있게 된다.

 

JPA가 지원하는 성능 최적화 기능

<1차 캐시와 동일성 보장>

 

JPA를 이용하면, 같은 트랜잭션 내에서는 같은 엔티티를 반환하는 것을 보장해 준다.

 

즉, 같은 트랜잭션 안에서는 이전에 자바 컬랙션을 활용했던 것처럼 다음과 같이 같은 entity로 반환받을 수 있음을 의미한다고 볼 수 있다.

같은 트랜잭션 내에서 같은 엔티티의 반환을 보장해준다. (동일한 엔티티를 조회할 때)

이것은 한 번 엔티티 조회를 위해 SQL문을 이용해 데이터베이스에서 데이터를 조회하면, JPA가 이것을 가지고 있다가 트랜잭션 내에서 동일한 요청이 발생할 때, 메모리 상에서 바로 반환하는 방식을 취하기 때문이다.

 

그래서 '==' 연산자 수행 결과가 true로 나오는 것이다. (같은 트랜잭션 안에서만 가능하다)

 

<트랜잭션을 지원하는 쓰기 지연 - INSERT>

트랜잭션을 커밋할 때까지 INSERT SQL을 모은다.

JDBC BATCH SQL이라는 기능을 사용해서 한 번에 SQL을 전송한다.

 

이는 어차피 트랜잭션이 마무리될 때에만 반영하면 되는 점을 이용한 것

이를 통해 여러 번 네트워크 통신할 필요 없이 한 번의 통신으로 반영할 수 있게 된다.

JDBC BATCH SQL을 이용한 네트워크 통신 비용 절감

<지연 로딩과 즉시 로딩>

  • 지연 로딩: 객체가 실제 사용될 때의 로딩
  • 즉시 로딩: JOIN SQL로 한 번에 연관된 객체까지 미리 조회

이런 식으로 조회한다고..

자세한 개념들은 차차 공부해 보도록 하자.

 

가장 중요한 것은 객체 지향 설계라는 개념관계형 데이터베이스에 대해 잘 이해하고 있어야 한다는 점이라고 한다!!


이 글은 김영한 님의 "자바 ORM 표준 JPA 프로그래밍 - 기본편" 강의 내용을 공부하고 정리한 글입니다.

728x90

댓글