Spring - ORM, JPA, Hibernate, JDBC 총정리

murphytklee·2023년 5월 2일
2
post-thumbnail

1. ORM(Object Relational Mapping)

ORM을 설명 전에 RDB를 먼저 알아봅시다!

관계형 데이터베이스(RDB)데이터가 행과 열이 있는 테이블로 구성되는 집합 이론 모델을 기반으로 합니다.

전반적으로 관계형 데이터베이스는 강력하고 효율적인 데이터 저장 및 검색 기능을 제공하지만 복잡한 객체 지향 개념을 모델링(상속, 다형성, 레퍼런스, 오브젝트)하는 데 적합하지 않습니다.

우리는 객체 지향 언어인 JAVA를 사용하고 있는데 RDB는 객체 지향을 모델링하는데 적합하지 않다??

이 말은 곧 객체에 접근할 때마다 DB상에 어떤 테이블과 연결시켜줘야되고 객체의 상태, 상속 정보가 바뀔 때마다 이를 수정해줘야 한다.

ORM은 테이블을 객체지향적으로 사용하기 위한 기술이다. 객체와 DB 테이블이 매핑을 이루는 것을 의미한다. 즉, 내가 코드 상에서 생성한 객체가 DB상에 어떤 테이블과 연결이 된다는 것을 의미한다. 이렇게 되면 내가 객체를 조작함으로써 DB를 조작할 수 있게 된다.

우리는 JPA에서 DB에 대한 접근을 시도할 때 직접 sql 쿼리문을 만들지 않는다. 다만 객체를 이용한 메소드를 통해 이를 관리할 뿐이다.

예시를 보자 SELECT * FROM user → user.findAll()

query를 직접 작성하지 않고 메서드 호출만으로 query가 수행되다 보니, ORM을 사용하면 생산성이 매우 높아집니다.

ORM 핵심 : ORM 을 사용해 Object와 RDB 사이에 객체지향적으로 다루기 위한 기술입니다.

ORM 프레임워크 종류

  • JAVA : JPA, Hibernate, EclipseLink, DataNucleus, Ebean 등

사진을 보면 Data Access Layer들이 곧 Service와 DB사이를 연동해주고 있다. JPA, Hibernate은 Java의 ORM 프레임워크인 셈이다. 그렇다면 JDBC 는 ORM인가?

2. JDBC(Java Database Connectivity)

JDBC는 ORM이 아닌, 데이터베이스에 연결 및 작업을 하기 위한 자바 표준 인터페이스이다.

바는 DBMS의 종류에 상관 없이 하나의 JDBC API를 이용해서 데이터베이스 작업을 처리한다. 이 JDBC는 DB에 접근해서 CRUD를 쉽고 효율적이게 할 수 있게 한다.

JDBC API가 생겨나기 전에는 데이터베이스의 종류마다 (mySql, Oracle...) 각각의 SQL을 사용하였다. 하지만 DB의 종류에 따라 SQL문의 작성 방법이 너무 차이가 나서 구현이 불편했다. 이런 불편함을 해결하고자 메소드, 전역변수 등을 하나의 문법으로 통일 시켰고 그것이 JDBC가 되었다.

JDBC 핵심: DBMS의 종류에 상관없이 하나의 JDBC API를 이용해서 하나의 문법으로 통일시켜 데이터베이스 작업을 처리한다!

JDBC 아키텍쳐

또 다시 그림을 보자, 이제 Service, Data Aceess, DB와의 연관 관계를 어느정도 알았다. 그러면 JPA와 Hibernate 는 무엇이고 어떻게 객체를 DB와 연동하는 것인가?

3. JPA(Java Persistence API)

JPA는 Hibernate 기반으로 새로운 자바 ORM 기술 표준이자, 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스이다.

❓ JPA는 영속성 컨텍스트인 EntityManager를 통해 Entity를 관리하고 이러한 Entity가 DB와 매핑되어 사용자가 Entity에 대한 CRUD를 실행을 했을 때 Entity와 관련된 테이블에 대한 적절한 SQL 쿼리문을 생성하고 이를 관리하였다가 필요시 JDBC API를 통해 DB에 날리게 된다.

이 뜻을 이해하기 위해 영속성 컨텍스트를 먼저 알아보면

Persistence Context

영속성 컨텍스트란 Entity를 저장하는 비휘발성 환경이라는 뜻이다.

왜 사용할까? → DB와의 통신 횟수나 방식을 효율적으로 관리해야 서버의 속도가 빨라지기 때문에! → 중복되는 쿼리를 줄이기 위해 !

이러한 이유는 memory나 cache를 두는 이유와 동일하다. 따라서 영속성 컨텍스트는 Entity에 대한 캐시라고 이해하면 쉬울 것이다. 이렇게 Entity를 캐시처럼 다루면서 얻는 이점은 다음과 같이 정리 될 수 있다.

1차 캐시

다음과 같이 우리가 특정 row를 불러오고 이에 대해 수정을 여러번 진행하는 상황을 가정해보자.

정보1 불러오기 -> 정보 1 수정하기 -> 정보 1 저장하기 -> ..... -> 정보 1 수정하기

우리가 정보 1을 캐싱을 해놓지 않고 수정을 한다고 한다면 DB와의 통신을 한번 더 해야할 것이다. 하지만 우리가 Entity를 캐싱해놓기 때문에 이러한 불필요한 DB와의 통신이 줄어들 것이다.

쓰기 지연

빈번한 쓰기 작업을 하게 되면 context switching이 여러번 발생할 것이다. 따라서 이러한 Context Switching에 대한 overhead를 줄여주는게 쓰기 지연이다. 즉, 쓰기를 원하는 entity를 저장해 두었다가 한번에 처리한다는 것을 의미한다.

EntityManager entityManager = emf.createEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin(); entityManager.persist(memberA);
entityManager.persist(memberB);
// 이때까지 INSERT SQL을 DB에 보내지 않는다.// 커밋하는 순간 DB에 INSERT SQL을 보낸다.
transaction.commit();// Transaction 커밋

Dirty Checking

영속성 컨텍스트에서 관리하는 Entity에 대한 db상의 실제 업데이트는 영속성 컨테스트에서 commit이나 flush할 때 Entity에 대한 값이 조회할때를 기준으로 변경되었는지 확인한 후 바뀐 부분에 대해서 한꺼번에 db에 대한 업데이트를 지원하기 때문에 여러 비지니스 로직상에서 db상에 값이 꼬이는 경우를 방지해준다.

EnitityManagerFactory
WAS가 시작되는 시점에 만들어지고 종료되는 시점에 없어지면서 EntityManager를 관리하는 Object이다.

Q) 그럼 언제 EntityManger를 생성할까?
Transaction 단위를 수행할 때마다 생성한다. 즉, 고객의 요청이 올 때마다 사용했다가 닫는다. 따라서 Thread당 생성이 되고 이에 따라 Thread당 공유가 되지 않는다.


💡 EntityManager
EntityManager는 Entity를 생명주기를 관리하는 context로 Persistence Context 역할을 수행해준다.

우리가 Entity를 관리한다는 것을 EntityManger가 Entity에 대한 생명주기를 관리한다는 것을 의미할것이다. 마치 메모리에서 데이터를 언제 올리고 어떻게 내릴지에 관해 관리하는 것처럼 말이다. 이러한 Entity에 대한 Life cycle을 정리하면 (비영속, 영속, 준영속, 삭제) 가 있다.

Q: 그러면 JPA는 모두 트랜잭션일 때 사용이 가능한가요 ?

A: 아니다, Spring에서 트랜잭션 없이 JPA를 사용할 수 있지만 일반적으로 데이터 일관성과 무결성을 보장하기위해 트랜잭션을 활성화 하는 것 이좋다.

Q: 그러면 거의 모든 CRUD 서비스 단에서는 트랜잭션을 활성화 해야겠네?

A: 맞습니다 대부분 CRUD 작업을 수행할 때 데이터 일관성 무결성을 보장하기 위해 트랜잭션이 필요합니다. 하지만 Read 와 같이 읽기만 하는 경우 굳이 트랜잭션이 필요없지 않나? 해서 과제를 찾아보니 @Transactional(readOnly = true) 이렇게 읽기만하겠다 ! 불필요한 과정을 스킵할 수 있다.

❓ JPA는 영속성 컨텍스트인 EntityManager를 통해 Entity를 관리하고 이러한 Entity가 DB와 매핑되어 사용자가 Entity에 대한 CRUD를 실행을 했을 때 Entity와 관련된 테이블에 대한 적절한 SQL 쿼리문을 생성하고 이를 관리하였다가 필요시 JDBC API를 통해 DB에 날리게 된다.

다시 문장을 보고 정리를 하자면,

  • JPA는 영속성 컨테스트를 사용하여 db와의 통신을 효율적으로 관리한다.
  • 이러한 영속성 컨텍스트에 대한 JPA상에서 구현체를 EntityManger라고 한다.
  • 이러한 EntityManger는 Entity에 대한 생명주기를 관리하고 db와의 연결정보를 저장해둔다.
  • Entity에 대한 CRUD는 연결정보를 바탕으로 JPA가 자동으로 생성해준다.
  • 이러한 쿼리문은 EntityManger가 관리를 하다가 필요시 처리하게 된다.

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

JPA는 데이터베이스와 객체를 매핑하는 기술일 뿐, 내부적으로는 데이터베이스와의 통신을 위해 JDBC를 사용한다.

개발자가 JPA를 사용하면, JPA 내부에서 JDBC API를 사용하여 SQL을 호출하여 DB와 통신한다.

또한 JPA도 JDBC와 마찬가지로 인터페이스이기 때문에 구현체가 필요하고, 그 구현체 중 하나가 Hibernate이다. !!

JPA 핵심 : JPA는 영속성 컨텍스트인 EntityManager를 통해 Entity를 관리하고 이러한 Entity가 DB와 매핑되어 사용자가 Entity에 대한 CRUD를 실행을 했을 때 Entity와 관련된 테이블에 대한 적절한 SQL 쿼리문을 생성하고 이를 관리하였다가 필요시 JDBC API를 통해 DB에 날리게 된다.

지금까지의 내용을 그림을 통해 다시 정리를 하고 Hibernate로 넘어가 봅시다

잠깐 그러면 Spring Data JPA는 뭐지 ?

4. Spring Data JPA

→ 간단하게 설명하면 JPA를 더 쉽게 사용하기 위해 JPA 위에 구축된 상위 프레임워크

인터페이스 및 주석 세트를 제공하여 데이터 액세스 계층을 보다 쉽게 구축할 수 있도록 합니다.

Spring Data JPA는 Repository의 메소드를 통해 쿼리를 날릴 수 있다.

이는 JPA를 한 단계 추상화시킨 Repository라는 인터페이스를 제공함으로써 이루어진다. 사용자가 Repository 인터페이스에 정해진 규칙대로 메소드를 입력하면, Spring이 알아서 해당 메소드 이름에 적합한 쿼리를 날리는 구현체를 만들어서 Bean으로 등록해준다.

Method 이외에 SQL 문을 실행하고 싶을 때 → @Query → QueryDSL → 사용자가 직접 SQL문을 붙여 구현할 수 있다.

Spring Data JPA 핵심 : 메서드 이름 규칙을 기반으로 JPA 쿼리를 자동으로 생성하는 기능

5. Hibernate

Hibernate는 자바 언어를 위한 ORM 프레임워크이다. JPA의 구현체로, JPA 인터페이스를 구현하며, 내부적으로 JDBC API를 사용한다.

JPA와 Hibernate는 마치 자바의 interface와 해당 interface를 구현한 class와 같은 관계이다.

https://suhwan.dev/images/jpa_hibernate_repository/jpa_hibernate_relationship.png

위 사진은 JPA와 Hibernate의 상속 및 구현 관계를 나타낸 것이다.

JPA의 핵심인EntityManagerFactoryEntityManagerEntityTransaction을 Hibernate에서는 각각 SessionFactorySessionTransaction으로 상속받고 각각 Impl로 구현하고 있음을 확인할 수 있다.

“Hibernate는 JPA의 구현체이다”로부터 도출되는 중요한 결론 중 하나는 JPA를 사용하기 위해서 반드시 Hibernate를 사용할 필요가 없다는 것이다.

Hibernate의 작동 방식이 마음에 들지 않는다면 언제든지 DataNucleus, EclipseLink 등 다른 JPA 구현체를 사용해도 되고, 심지어 본인이 직접 JPA를 구현해서 사용할 수도 있다.

다만 그렇게 하지 않는 이유는 단지 Hibernate가 굉장히 성숙한 라이브러리이기 때문일 뿐이다.

쉽게 얘기하면 Hibernate는 JPA에서 메서드로 요청된 정보들을 JDBC가 잘 알아들을 수 있게 한번 더 표준화 작업을 해주고 JDBC에게 넘겨준다.

Hibernate 핵심: JPA의 구현체, interface, class 로 되어있다. JPA에서 넘겨받은 정보들을 표준화 해서 JDBC에게 넘겨준다.

External Liberies에 hibernate-core 을 찾아보면 구경할 수 있다.

이러고 나서야 이 그림이 이해가 됐다. console에서 Hibernate가 찍히고 JPA 와 Raw Level의 EntityManger에서 동작하는 원리를 보면서 도대체 무슨관계인거고 어떻게 생겼는지에 대해서 여태까지 궁금증을 해결한 것 같다.


번외

2019.3.3 기준 글
현재 저는 회사에서 각기 다른 환경을 가지고 있는 세 시스템을 동시에 다루고있습니다.

(세 시스템의 환경은 아래와 같습니다.)

  • 유통시스템 : 마이플랫폼 + Spring framerwork(java) - 옛 DI구조 + mybatis + oracle
  • SCM시스템 : JSP,js,css + Spring framework(java) - 어노테이션구조 + mybatis + oracle
  • 자판기시스템 : 넥사크로 + Spring framework(java) + hibernate + postegreSql

2023.5.1 기준 지난 12개월 구글 트랜드

JPA가 나온지 얼마 안됐을 때 당시에는 JPA와 mybatis를 혼용해서 쓰는 경우가 많았지만, 현재는 JPA + QueryDSL을 많이 쓰는 추세이다. 아무래도 mybatis나 Hibernate 같은경우는 직접 XML로 쿼리를 작성을 해야하는데 오류나 실수로인한 에러를 통해 서비스가 장애를 일으킬 수 있기 때문에 SI 쪽이 아니라면 JPA와 mybatis, hibernate와 같은걸 같이 사용하지 않는 것 같다고 말씀해주셨다.

QueryDSL 공부하러 가자.





Reference

JPA, Hibernate, 그리고 Spring Data JPA의 차이점

[Spring JPA] ORM과 JPA

JPA Architecture | Class Level Architecture | JPA Exceptions Architecture

JPA: ORM, JDBC, Hibernate란?

[Spring JPA] ORM과 JPA 그리고 Hibernate

1.3 JPA란 무엇인가? · jpa

JPA(Java Persistence API)의 개념

JDBC, JPA, Hibernate, Spring Data JPA 차이

https://file.okky.kr/images/1482455332077.pdf

0개의 댓글