공통 인터페이스 기능

slee2·2022년 3월 26일
0

순수 JPA 기반 리포지토리 만들기

MemberJpaRepository

package study.datajpa.repository;

import org.springframework.stereotype.Repository;
import study.datajpa.entity.Member;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;
import java.util.Optional;

@Repository
public class MemberJpaRepository {

    @PersistenceContext
    private EntityManager em;

    public Member save(Member member) {
        em.persist(member);
        return member;
    }

    public void delete(Member member) {
        em.remove(member);
    }

    public List<Member> findAll() {
        return em.createQuery("select m from Member m", Member.class)
                .getResultList();
    }

    public Optional<Member> findById(Long id) {
        Member member = em.find(Member.class, id);
        return Optional.ofNullable(member);
    }

    public long count() {
        return em.createQuery("select count(m) from Member m", Long.class)
                .getSingleResult();
    }

    public Member find(Long id) {
        return em.find(Member.class, id);
    }
}

TeamRepository

package study.datajpa.repository;

import org.springframework.stereotype.Repository;
import study.datajpa.entity.Member;
import study.datajpa.entity.Team;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;
import java.util.Optional;

@Repository
public class TeamRepository {

    @PersistenceContext
    EntityManager em;

    public Team save(Team team) {
        em.persist(team);
        return team;
    }

    public void delete(Team team) {
        em.remove(team);
    }

    public List<Team> findAll() {
        return em.createQuery("select t from Team t",Team.class)
                .getResultList();
    }

    public Optional<Team> findById(Long id) {
        Team team = em.find(Team.class, id);
        return Optional.ofNullable(team);
    }

    public long count() {
        return em.createQuery("select count(t) from Team t", Long.class)
                .getSingleResult();
    }
}

Test

공통 인터페이스 설정

어떻게 이렇게만 설정해도 save(), delete() 등등 메서드가 알아서 동작하는 걸까?
그건 바로 이 JpaRepository가 프록시로 들어가서 그렇다.

프록시로 동작하기 때문에 구현된 클래스가 들어가게 되어, 동작하게 된다는 것.

  • @Repository 어노테이션 생략 가능
  • 컴포넌트 스캔을 스프링 데이터 JPA가 자동으로 처리
  • JPA 예외를 스프링 예외로 변환하는 과정도 자동으로 처리

공통 인터페이스 적용

이전에 사용했던 리포지토리 테스트 그대로 가져와서 하면 통과된다.
기능이 잘 동작한다는 것이 확인됨.

공통 인터페이스 분석

T findOne(ID) -> Optional<T> findById(ID) 변경

제네릭 타입

  • T: 엔티티
  • ID: 엔티티의 식별자 타입
  • S: 엔티티와 그 자식 타입

주요 메서드

  • save(S): 새로운 엔티티는 저장하고 이미 있는 엔티티는 병합한다.
  • delete(T): 엔티티 하나를 삭제한다. 내부에서 EntityManager.remove() 호출
  • findById(ID): 엔티티 하나를 조회한다. 내부에서 EntityManager.find() 호출
  • getOne(ID): 엔티티를 프록시로 조회한다. 내부에서 EntityManager.getReference() 호출
  • findAll(...): 모든 엔티티를 조회한다. 정렬(Sort)이나 페이징(Pageable)조건을 파라미터로 제공할 수 있다.

이러한 공통 메서드를 제공한다.

그런데 특정한 메서드를 더 디테일하게 만들고 싶을때 이를 구현하기가 쉽지 않다. 예를 들어 유저이름(username)으로 엔티티(Member)를 조회하고 싶을때,

이 리포지토리를 상속받아서 구현하기에는 너무나도 많은 양의 메서드가 들어있어서 구현이 불가능하다.

이를 가능하게 하는 방법이 있고, 최종본으로는 커스텀 기능이라는 것이 있다.

이 커스텀 기능은 맨 나중에 배우게 되고,
이제 곧 배울건 그냥 메서드 구현을 할 수 있는 방법에 대해서이다.

0개의 댓글