[Spring Data JPA] 공통 인터페이스 기능

kiteB·2021년 10월 30일
0

Spring Data JPA

목록 보기
3/8
post-thumbnail

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

일단 순수 JPA 기반으로 리포지토리를 만들고 스프링 데이터 JPA 를 적용해보자!

📌 기본 CRUD

  • 저장
  • 변경 → 변경감지 사용
  • 삭제
  • 전체 조회
  • 단건 조회
  • 카운트

🔗 전체 코드 확인하기


1. 순수 JPA 기반 리포지토리 - 회원

1) Create (저장)

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

2) Read (조회)

//전체 조회
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);
}

3) Update (변경) → 변경감지 사용

JPA는 기본적으로 엔티티 변경감지로 인해 데이터를 바꾼다!

트랜잭션 안에서 엔티티를 조회한 다음에 데이터를 변경하면,
트랜잭션 종료 시점에 변경감지 기능이 작동해서 변경된 엔티티를 감지하고 UPDATE SQL을 실행한다.
즉, 따로 update를 구현할 필요가 없다!

4) Delete (삭제)

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

5) Count (카운트)

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

2. 순수 JPA 기반 리포지토리 - 팀

1) Create (저장)

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

2) Read (조회)

//전체 조회
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);
}

3) Update (변경) → 변경감지 사용

위에서 언급했듯이, 이것 역시 구현하지 않아도 됨.

4) Delete (삭제)

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

5) Count (카운트)

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

MemberTeamRepository 코드가 매우 유사하다는 것을 알 수 있다! 이러한 중복을 스프링 데이터 JPA가 해결할 수 있다.


3. 순수 JPA 기반 리포지토리 테스트

@Test
public void basicCRUD() {
    Member member1 = new Member("member1");
    Member member2 = new Member("member2");
    memberJpaRepository.save(member1);
    memberJpaRepository.save(member2);

    //단건 조회 검증
    Member findMember1 = memberJpaRepository.findById(member1.getId()).get();
    Member findMember2 = memberJpaRepository.findById(member2.getId()).get();
    assertThat(findMember1).isEqualTo(member1);
    assertThat(findMember2).isEqualTo(member2);

    //리스트 조회 검증
    List<Member> all = memberJpaRepository.findAll();
    assertThat(all.size()).isEqualTo(2);

    //카운트 검증
    long count = memberJpaRepository.count();
    assertThat(count).isEqualTo(2);

    //삭제 검증
    memberJpaRepository.delete(member1);
    memberJpaRepository.delete(member2);
    
    long deletedCount = memberJpaRepository.count();
    assertThat(deletedCount).isEqualTo(0);
}

실행화면


[ 공통 인터페이스 설정 ]

스프링 데이터 JPA가 구현 클래스 대신 생성

개발자가 interface만 선언해주면 스프링 데이터 JPA가 자동으로 구현 클래스를 만들어준다.

😲 프로젝트 환경 설정에서 MemberRepository 인터페이스가 동작할 수 있었던 이유가 바로 이것이다 ❗

@Repository	//생략 가능
public interface TeamRepository extends JpaRepository<Team, Long> {
}

@Repository 어노테이션을 생략해도 된다.

  • 컴포넌트 스캔을 스프링 데이터 JPA가 자동으로 처리한다.
  • JPA 예외를 스프링 예외로 변환하는 과정도 자동으로 처리한다.

[ 공통 인터페이스 적용 ]

@Test
public void basicCRUD() {
    Member member1 = new Member("member1");
    Member member2 = new Member("member2");
    memberRepository.save(member1);
    memberRepository.save(member2);

    //단건 조회 검증
    Member findMember1 = memberRepository.findById(member1.getId()).get();
    Member findMember2 = memberRepository.findById(member2.getId()).get();
    assertThat(findMember1).isEqualTo(member1);
    assertThat(findMember2).isEqualTo(member2);

    //리스트 조회 검증
    List<Member> all = memberRepository.findAll();
    assertThat(all.size()).isEqualTo(2);

    //카운트 검증
    long count = memberRepository.count();
    assertThat(count).isEqualTo(2);

    //삭제 검증
    memberRepository.delete(member1);
    memberRepository.delete(member2);

    long deletedCount = memberRepository.count();
    assertThat(deletedCount).isEqualTo(0);
}

앞에서 사용했던 테스트 코드를 복사해서
memberJpaRepositorymemberRepository로 수정하기만 하면 잘 동작한다.
→ 거의 유사하기 때문이다 ❗❗

실행 결과


[ 공통 인터페이스 분석 ]

  • JpaRepository 인터페이스: 공통 CRUD 제공
  • 제네릭은 <엔티티 타입, 식별자 타입> 설정

공통 인터페이스 구성

📌 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) 조건을 파라미터로 제공 가능하다.
profile
🚧 https://coji.tistory.com/ 🏠

0개의 댓글