데이터베이스/JPA

Spring Data JPA

칼퇴시켜주세요 2022. 2. 7. 16:16
728x90

안녕하세요! Spring Boot로 개발하다보면 DB를 필연적으로 마주치게 됩니다. Spring에서 기본 데이터 저장소의 특수 특성을 유지하면서 데이터 액세스를 위해 Spring Data를 제공하고 있으며
안에는 다양한 모듈이 있습니다. 오늘은 이중에서도 가장 자주 등장하는 Spring Data JPA에 대해 간단히 알아보도록 하겠습니다. (JPA 자체만으로도 양이
방대하기 때문에 극히 일부분만 다루고, 다양한 쿼리 방법에 대해 자세히 설명하도록 하겠습니다.)

JPA 란

JPA(Java Persistence API)란 DB 테이블과 자바 객체 사이의 매핑을 처리해주는 ORM이라는 기술의 표준입니다.

  • JPA가 제공하는 API를 사용하면 객체를 DB에 저장하고 관리할 때, 개발자가 직접 SQL을 작성하지 않아도 된다.
  • JPA가 개발자 대신 적절한 SQL을 생성해서 DB에 전달하고, 객체를 자동으로 Mapping 해준다.
  • JPA는 내부적으로 JDBC API를 활용하는데, 개발자가 직접 JDBC API를 활용하면 패러다임 불일치, SQL 의존성 등으로 인해 효율성이 떨어진다.
  • 이 때, JPA를 활용한다면 모든 SQL에 대해 개발자 대신 JPA가 자동으로 해결해 준다는 점에서 생산성을 크게 높인다.

JPA 특징

  1. 자바 객체와 DB 테이블 사이의 매핑 설정을 통해 SQL을 생성합니다. 보통 JDBC를 사용하여 개발을 하다보면 코드가 비슷한 형태로 반복되게 됩니다.

    DB커넥션을 구하고, 쿼리를 작성하고, 파라미터를 설정하고 실행한 결과를 자바 객체에 설정합니다.
    여기서 문제점은 테이블의 컬럼명이 추가, 삭제, 변경이 된다면 관련되어 있는 모든 쿼리를 수정해야 할 것인데 JPA는 매핑 설정만 변경하면 됩니다.
    또한 실행한 쿼리를 자바 객체에 설정해줘야하는데 JPA는 자바 객체로 매핑하여 검색할 수 있습니다. 즉 유지보수가 쉽습니다.

  2. 객체를 통해 쿼리를 작성할 수 있는 JPQL(Java Persistence Query Language)를 지원합니다.

  3. JPA는 성능 향상을 위해 지연 로딩이나 즉시 로딩과 같은 몇가지 기법을 제공하는데 이것을 잘 활용하면 SQL을 직접 사용하는 것과 유사한 성능을 얻을 수 있습니다. 하지만 잘못이해하고 사용하는
    JPA는 성능을 크게 감소시킬 수 있습니다.

Hibernate 란

하이버네이트란 JPA 프로바이더의 한 종류입니다. JPA 프로바이더는 JPA의 표준을 실제로 구현하고 있습니다. 쉽게 말하자면 JPA는 DB와 자바 객체를 매핑하기 위한 인터페이스(API)를 제공하고 JPA
프로바이더는(하이버네이트) 이 인터페이스를 구현한 것이라고 할수 있습니다.

※ 영속성(Persistence)과 EntityManager는 JPA Session에서 자세히 알아보도록 하겠습니다.

JPA Query

Query Method

JPA가 제공하는 마법같은 기능으로 인터페이스에 메소드만 선언하면 해당 메소드 이름으로 적절한 JPQL 쿼리를 생성해서 실행합니다.

Keyword Sample JPQL snippet
Distinct findDistinctByLastnameAndFirstname select distinct … where x.lastname = ?1 and x.firstname = ?2
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is, Equals findByFirstname,findByFirstnameIs,findByFirstnameEquals … where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age <= ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull, Null findByAge(Is)Null … where x.age is null
IsNotNull, NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection ages) … where x.age not in ?1
True findByActiveTrue() … where x.active = true
False findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)

JPQL

JPA의 장점으로 앞서 언급한 Query Method를 이용하면 쿼리(JPQL)를 자동으로 생성해주지만, 상황에 따라 직접 쿼리를 작성할 필요가 생기게 됩니다.

JPA에서 직접 쿼리를 작성할 수 있는 방법은 다음과 같이 2가지가 있습니다.

  • JPQL로 작성
  • 일반 SQL로 작성

JPQL이라고 하는 것은 JPA의 일부분으로 정의된 플랫폼 독립적인 객체지향 쿼리 언어입니다. JPA에서 사용할 수 있는 쿼리 언어로 일반
SQL이 데이터베이스를 바라보고 작성한다면 JPQL은 엔티티 클래스를 바라보고 작성
해야 합니다.

JPQL과 SQL모두 직접 쿼리를 작성하는 방법은 동일하게 @Query어노테이션을 이용하면 됩니다. 그리고 @Query어노테이션에 들어있는 nativeQuery라는 속성을 이용하여 JPQL로 작성한 것인지 SQL로
작성한 것인지를 구분할 수 있습니다.

  • nativeQuery = true → SQL
  • nativeQuery = false (default) → JPQL

<JPQL 예시>

Querydsl

QueryDSL는 JPQL 빌더로 동적 쿼리를 메소드로 구조화하여 관리할 수 있도록 돕는 쿼리빌더 라이브러리입니다. 자체적인 빌드를 통해 생성한 Qclass 덕분에 IDE 자동완성 기능을 십분 활용 가능합니다.

한편 JPA와 QueryDSL을 혼용하여 사용하다보면 동일한 역할을 수행하는 repository를 각각 생성해야 합니다. JPA는 인터페이스만 만들어서 빌드를 하면 구현체를 자동으로 생성하기에 별도로 구현체를 만들지
않습니다. 필요에 따라 구현체를 추가로 만들어도 되지만, 그조차 관리 포인트입니다.

반면 QueryDSL은 본격적으로 활용하기 위해 구현체를 요구합니다. 물론 Java 8에서 추가된 디폴트 메소드를 통해 코드를 구현할 수 있어 인터페이스로도 만들 수 있지만, 이렇게 사용하라고 제공하는 기능이
아닙니다. 하나의 객체가 수많은 역할을 수행해서도 안 되지만, 동일한 역할을 수행하는 클래스가 여러 가지라도 문제입니다.

JPQL vs Querydsl

Mybatis, JDBC Template, JPQL 은 모두 쿼리문을 String 형식으로 직접 작성해서 구현하게 된다. 심지어 Spring Data JPA도 때때로 NativeQuery로 작성해야 합니다. 백엔드
개발 경험이 한 번이라도 있다면, 쿼리를 작성하다가 오타가 난 경험이 있을 것입니다. String으로 이뤄져 있기에 컴파일 단계에서 디버깅이 불가능하며, 그 말은 에러가 발생해도 띄어쓰기를 잘못 한 건지 대소문자를
잘못 쓴건지 알 수없는 단점이 있습니다.

반면, QueryDSL은 자바코드로 쿼리를 만들기 때문에 컴파일 시점에서 오류를 잡을 수 있고, 메서드 체이닝 형식으로 작성하기 때문에 IDE 코드 어시스턴트의 도움도 받을 수 있습니다. 특히 현업에서 여러 번의
Join과 동적쿼리를 작성할 때, 그 진가를 보여줍니다.

요악

오늘을 Spring Data JPA 에 대해 알아보았습니다. JPA Query 방법을 크게 세가지로 나눌수 있는데 '이게 정답이다' 는 없고 상황에 맞게 적절히 사용하면 될것 같습니다. Querydsl
사용에 대한 고찰은 여기를 참고바랍니다.

반응형