JPA에 대해서 많은 포스팅은 한듯하다. 하지만 계속 계속 봐야겠다. 할때마다 까먹는다.
오늘은 Spring data jpa를 포스팅해보자.

Spring data JPA

Spring data jpa는 스프링 프레임워크에서 JPA를 편리하게 사용할 수 있도로고 제공해주는 프로젝트이다. 이 프로젝트는 데이터 접근 계층을 개발할 때 지루하게 반복되는 CRUD 문제를 조금더 세련된 방법으로 해결한다.

public class AccountRepository {
  public void save(Account account){
   ...
  }
  public Member findOne(Long id){
  ...
  }
  //update
  //delete
  //findAll
}

public class ItemRepository {
  public void save(Item item){
   ...
  }
  public Member findOne(Long id){
  ...
  }
  //update
  //delete
  //findAll
}

우리는 대게 이런작업을 일삼았다. 여기서 보면 두개는 비슷한 일을 한다. save, findOne, update, delete .. 기타 등등 그래서 이것을 해결하고자 우리는 제네릭 DAO를 만들어서 사용했다. 하지만 이방법은 공통 기능을 구현한 부모 ㅋ르래스에 너무 종속되고 구현 클래스 상속이 가지는 단점에 노출된다.

Spring data jpa는 CRUD를 처리하기 위한 공통 인터페이스가 존재 한다. 개발할때 인터페이스만 작성하면 실행 시점에 Spring data jpa가 구현 객체를 동적으로 생성해서 주입해준다. 그래서 우리는 구현 클래스 없이 인터페이스만 작성해도 개발할 수 있다.

public interface ItemRepository extends JpaRepository<Item, Long>{
}

위 처럼 작성하면 간단한 CRUD가 완성 된다.
일반적인 CRUD 메서드는 JpaRepository 인터페이스가 공통으로 제공하므로 문제가 될 건 없다. 예를들어 findByusername(String username) 처럼 메서드의 이름을 분석해서 JPQL를 실행한다.

spring data jpa Query creation
위에 링크에 가면 자세히 알 수 있다.

Spring data jpa는 스프링 데이터 프로젝트의 하위 프로젝트 중 하나이다. JPA 말고도 여러가지 다양한 데이터 저장소를 제공해준다.

쿼리 메서드

쿼리 메서드 기능은 스프링 데이터 JPA가 제공하는 마법 같은 기능이다. 대표적으로 메서드 이름만으로 쿼리를 생성하는 기능이 있다.
이외에도 NamedQuery, @Query 어노테이션 등을 활용해 좀더 세부적으로 개발 할 수 있다.

위에서 메서드명으로 쿼리를 생성하는 것은 잠깐 봤으니 생략 하겠다. 위의 문서에 더 자세히 나와있으니 참고하길 바란다.

JPA NamedQuery

JPA Named 쿼리는 이름 그대로 쿼리에 이름을 부여해서 사용하는 방법이다. 한번 살펴보자.

@Entity
@NamedQuery(
  name = "Account.findByusername",
  query = "select a from Account a where a.name = :name"
)
public class Account {

  @Id
  @GeneratedValue
  @Column(name = "ACCOUNT_ID")
  private Long id;

  private String name;

  private String password;

  private String email;
  ...getter setter ... etc
}

@NamedQuery 어노테이션으로 Named 쿼리를 지정하였다.

@Repository("accountRepositoryImplCustom")
public class AccountRepositoryImplCustom {

  @PersistenceContext
  private EntityManager entityManager;

  public List<Account> findByname(String name){
    return entityManager.createNamedQuery("Account.findByusername", Account.class)
      .setParameter("name", name)
      .getResultList();
  }
}

그리고 createNamedQuery에 위에 해당하는 Name을 지정해주면 된다.
위는 스프링 데이터 JPA를 쓰지 않았을 경우이고 Spring data jpa를 쓰면 더 간단해진다.

public interface AccountRepository extends JpaRepository<Account, Long> {

  List<Account> findByusername(@Param("name") String name);
}

스프링 데이터 JPA는 선언한 도메인 클래스 +.(점) + 메서드 이름으로 Named쿼리를 찾는다. 만약 실행한 Named 쿼리가 없다면 메서드 이름으로 쿼리 생성 전략을 사용한다.

@Query

레파지토리 메서드에 직접 쿼리를 정의하려면 Spring에 있는 @Query 어노테이션을 사용하면 된다. 메서드에 정적 쿼리를 직접 작성하므로 이름 없는 Named 쿼리라 할 수 있다. 또한 JPA Named 쿼리처럼 애플리케이션 실행 시점에 문법 오류를 발견 할 수 있는 장점이 있다.

@Query("select a from Account a where a.name = ?1")
List<Account> findByusernames(String name);

위에는 JPQL을 사용했을 경우이다 기본으로 JPQL을 사용한다. 네이티브 쿼리를 사용하고 싶다면 아래와 같이 하면 된다.

@Query(value = "select * from Account where name = ?1", nativeQuery = true)
List<Account> findByusernamesQueryNative(String name);

책에는 네이티브 쿼리시 위치 기반 파라미터가 0부터 시작한다고 했지만 필자는 1부터 시작했다. 버전이 올라가면서 바뀐듯하다?

파라미터 바인딩

Spring data jpa는 위치기반 파라미터와 이름 기반 파라미터 바인딩을 모두 지원한다.

select a from Account a where a.name = ?1 //위치 기반
select a from Account a where a.name = :name //이름 기반

기본값은 위치 기반인데 파라미터 순서로 바인딩한다. 이름 기반 파라미터 바인딩을 사용하려면 spring에 있는 @Param 어노테이션을 사용하면 된다.

@Query(value = "select a from Account a where a.name = :name")
List<Account> findByusernamesNamedQueryNative(@Param("name") String name);

코드 가독성과 유지보수를 위해 이름 기반 파라미터 바인딩을 사용하자!