Programming/Database

JPA와 Spring Data JPA 구분하기

Jan92 2021. 11. 29. 23:15

 

"SQL이 아닌 객체 중심의 개발을 위한 JPA"

 

해당 포스팅은 JPA에 대해 세부적으로 다루기보다는 전체적인 개념과 함께 JPA와 Spring Data JPA를 구분하는데 초점을 두었습니다. 참고 부탁드리겠습니다.

 

 

- ORM(Object Relational Mapping)

JPA에 대해 알아보기 전 더 큰 개념인 ORM 부터 간단하게 살펴보겠습니다.

객체 관계 매핑(Object Relational Mapping)이란? 객체와 관계형 데이터베이스의 데이터를 ORM 프레임워크가 중간에서 자동으로 매핑해주는 것을 말합니다. (대부분의 대중적인 언어에는 ORM 기술이 존재)

객체를 객체대로 설계하고, 관계형 데이터베이스는 관계형 데이터베이스대로 설계했을 때 객체 모델과 관계형 모델 간의 불일치가 존재하는데, 이때 ORM을 통해 객체 간의 관계를 바탕으로 SQL을 자동으로 생성하여 불일치를 해결합니다.

 

 

 

- ORM의 장점과 단점

장점으로는 SQL문이 아닌 Method를 통해 DB를 조작할 수 있어서 개발자는 모델을 이용하여 비즈니스 로직을 구성하는데 더 집중할 수 있게 됩니다. (이때 ORM 내부적으로 쿼리를 생성하여 DB를 조작하게 됩니다.)

또한 객체지향적 접근만 고려하면 되기 때문에 객체지향적인 코드 작성으로 생산성이 높아집니다.

매핑하는 정보가 Class로 명시되어 있어 있기 때문에 ERD를 보는 의존도를 낮출 수 있고, 보수 및 리펙토링에 유리합니다.

사용하던 데이터베이스가 MySQL에서 PostgreSQL으로 변하게 되는 경우 ORM을 사용하면 쿼리를 새로 짤 필요가 없다는 장점이 있습니다.

 

단점으로는 규모가 크고 복잡한 프로젝트에서 설계가 잘못된 경우 속도 저하 및 일관성을 무너뜨리는 문제가 발생할 수 있습니다.

또 복잡하고 무거운 쿼리는 속도를 위해 별도의 튜닝이 필요하기 때문에 결국 SQL문을 써야할 수도 있습니다. 사용하기는 편리하지만 제대로 사용하기까지 많은 학습 비용이 들어가는 것도 단점입니다.

 

 

* ERD: Entity Relationship Diagram 개체 - 관계 다이어그램

 

 


 

JPA, Spring Data JPA

 

- JPA (Java Persistence API)

현재 자바 ORM 기술의 표준으로 인터페이스의 모음, 즉 기술을 명세한 기술 명세입니다. 특정 기능을 하는 라이브러리가 아니라는 점이 중요합니다. JPA 2.1 표준 명세를 구현한 3가지 구현체 Hibernate, EclipseLink, DataNucleus가 있는데 그중에 Hibernate가 가장 대표적입니다.

 

(JPA는 인터페이스, Hibernate는 JPA의 구현체, 이어서 나올 Spring Data JPA는 JPA를 추상화 한 모듈)

 

위 구조에서 볼 수 있는 것처럼 JPA는 어플리케이션과 JDBC 사이에서 동작합니다. 개발자가 JPA를 사용하면 JPA 내부에서 JDBC API를 사용하여 SQL을 호출하고 DB와 통신합니다.

예를 들어 User 객체를 데이터베이스에 저장하고 싶을 때 개발자는 JPA에 User 객체를 넘깁니다. JPA는 User Entity를 분석하여 INSERT SQL을 생성하고 JDBC API를 사용하여 DB에 생성된 SQL을 날립니다.

 

 

 

- JPA를 사용하는 이유

JPA를 사용하는 가장 큰 이유는 SQL 중심의 개발에서 객체 중심의 개발을 하기 위해서 입니다. JPA를 사용함으로써 반복되는 CRUD가 간단해지며, 특히 수정은 매핑된 객체(테이블 데이터)를 조회해서 값을 변경한 후 commit 하게 되면 JPA가 UPDATE SQL을 DB에 전송하여 수정된 내용이 저장됩니다.

 

또한 JAVA에는 부모클래스와 자식클래스 사이의 관계 즉, 상속관계가 존재하는데요. 반면에 데이터베이스에서는 이러한 객체의 상속관계를 지원하지 않습니다. 이러한 문제점을 JPA에서는 슈퍼클래스와 서브클래스의 구조를 통해 해결할 수도 있습니다.

('각각의 테이블로 변환', '통합 테이블로 변환', '서브타입 테이블로 변환' 하는 3가지 방법이 있으며, 해당 포스팅에는 세부 내용이 없습니다.)

 

 


 

public static void main(String[] args) {
  EntityManagerFactory factory = Persistence.createEntityManagerFactory("Factory");
  
  // db connection
  EntityManager manager = factory.createEntityManager();
  EntityTransaction transaction = manager.getTransaction();
  // 트랜잭션 시작
  transaction.begin();
  
  try {
    Member member = new Member();
    member.setUsername("Jan");
    manager.persist(member); // db 저장
    Member findMember = manager.find(Member.class, member.getId()); // select 쿼리
    transaction.commit();
  } catch (Exception e) {
      transaction.rollback();
  } finally {
      manager.close();
      factory.close();
  }
}

 

- Spring Data JPA

JPA에 대한 공부를 시작하면 위 코드처럼 EntityManagerFactory를 생성하여 요청이 올 때마다 EntityManager를 생성, 해당하는 EntityManager는 DB Connection pool을 사용해서 DB에 접근하는 방식으로 먼저 공부하게 됩니다.

하지만 Spring으로 개발을 하면서 JPA를 사용하면 EntityManager를 직접 다루는 대신 대부분 Repository라는 인터페이스를 정의하여 사용하게 되는데요. 이때 사용하는 Repository가 바로 Spring Data JPA의 핵심이 됩니다.

 

Spring Data JPA는 한마디로 JPA를 쉽게 사용하기 위해 만들어놓은 모듈입니다.

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

 

 

SimpleJpaRepository

 

조금 더 자세하게 살펴보면 Spring Data JPA가 JPA를 추상화 했다는 말은 Spring Data JPA의 Repository를 구현하는 곳에서 JPA를 사용하고 있다는 뜻인데요. JpaRepository 인터페이스의 구현체인 SimpleJpaRepository를 보면 내부적으로 JPA의 EntityManager를 사용하는 것을 볼 수 있습니다.

(org.springframework.data.jpa.repository.support.SimpleJpaRepository)

 

 

 

JpaRepository 계층구조

 

JpaRepository의 계층구조를 살펴보면 다음과 같습니다.

가장 먼저 Repository 인터페이스가 존재하고, Repository를 상속받는 CrudRepository 인터페이스는 CRUD의 기본 기능을 가지고 있으며, CrudRepository를 상속받는 PagingAndSortingRepository는 페이징과 정렬 기능이 추가된 인터페이스입니다.

여기까지가 스프링 데이터 모듈이 사용하는 부분이며, 최종 인터페이스인 JpaRepository는 Spring Data JPA 모듈이 추가적으로 JPA에 특화된 기능을 제공하는 인터페이스입니다.

 

 

 

 

- JpaRepository 사용

public interface UserRepository extends JpaRepository<User, Long> {
  User findByIdx(Long idx);
  User findByIdAndState(String id, String state);
  boolean existsByIdAndName(String id, String name);
}

JpaRepository는 위 코드처럼 extend JpaRepositlry<엔티티 클래스 이름, ID 필드 타입> 지정을 통해 사용합니다.

위 코드의 findByIdx, existsByIdAndName 메서드처럼 JpaRepository의 공통 메서드가 아니더라도 규칙에 맞게 선언된 메서드의 이름으로 적절한 JPQL 쿼리를 생성하여 DB에 날릴 수 있습니다.

 

(공통 메서드)

save(S) : 새로운 엔티티는 저장하고, 이미 있는 엔티티는 수정 (식별자 값이 없으면 em.persist(), 있으면 em.merge() 호출)

delete(T) : 엔티티 하나를 삭제 (내부에서 em.remove() 호출)

findOne(ID) : 엔티티 하나를 조회 (내부에서 em.find() 호출)

getOne(ID) : 엔티티를 프록시로 조회 (내부에서 em.getReference() 호출)

findAll(..) : 모든 엔티티를 조회 (sort 또는 pageable 조건을 파라미터로 제공)

 

 

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.details

(JpaRepository메서드 선언 규칙 참고 문서)

 

 

 

결론적으로 JPA란, 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스이며 Spring Data JPA의 모듈, JpaRepository를 통해 실제로 JPA의 기능을 쉽고, 편리하게 사용할 수 있습니다.