danuri
오늘의 기록
danuri
전체 방문자
오늘
어제
  • 오늘의 기록 (307)
    • java (150)
      • java (33)
      • spring (63)
      • jpa (36)
      • querydsl (7)
      • intelliJ (9)
    • kotlin (8)
    • python (24)
      • python (10)
      • data analysis (13)
      • crawling (1)
    • ddd (2)
    • chatgpt (2)
    • algorithm (33)
      • theory (9)
      • problems (23)
    • http (8)
    • git (8)
    • database (5)
    • aws (12)
    • devops (10)
      • docker (6)
      • cicd (4)
    • book (44)
      • clean code (9)
      • 도메인 주도 개발 시작하기 (10)
      • 자바 최적화 (11)
      • 마이크로서비스 패턴 (0)
      • 스프링으로 시작하는 리액티브 프로그래밍 (14)
    • tistory (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

인기 글

태그

  • RDS
  • 자바 최적화
  • Jackson
  • Thymeleaf
  • S3
  • nuribank
  • SWAGGER
  • reactive
  • Security
  • Spring
  • Bitmask
  • 등가속도 운동
  • mockito
  • CICD
  • Database
  • POSTGIS
  • AWS
  • DDD
  • gitlab
  • JPA
  • 마이크로서비스패턴
  • docker
  • Java
  • connection
  • 도메인 주도 설계
  • ChatGPT
  • Kotlin
  • 트랜잭션
  • PostgreSQL
  • Saving Plans

최근 댓글

최근 글

hELLO · Designed By 정상우.
danuri

오늘의 기록

java/jpa

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

2021. 5. 23. 19:19

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%8D%B0%EC%9D%B4%ED%84%B0-JPA-%EC%8B%A4%EC%A0%84/

 

실전! 스프링 데이터 JPA - 인프런 | 강의

스프링 데이터 JPA는 기존의 한계를 넘어 마치 마법처럼, 리포지토리에 구현 클래스 없이 인터페이스 만으로 개발을 완료할 수 있습니다. 그리고 반복 개발해온 기본 CRUD 기능도 모두 제공합니다

www.inflearn.com

강의를 들으며 생각 정리 + "자바 ORM 표준 JPA 프로그래밍" 책 참고

 

 

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


먼저 스프링 데이터 JPA를 사용하지 않고 순수 JPA만을 이용해 리포지토리를 만들어보자. 그리고 스프링 데이터 JPA를 이용해 리포지토리를 만들었을 때와 비교해보자.

 

기본적인 CURD 리포지토리를 만든다.

</repository/MemberJpaRepository>

@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);
    }
}

 

</repository/TeamRepository>

@Repository
public class TeamRepository {

    @PersistenceContext
    private 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();
    }
}

+) 참고 : JPA에서 수정은 따로 리포지토리 메서드로 만들 필요 없이 변경 감지 기능을 사용하면 된다.

트랜잭션 안에서 엔티티의 데이터 변경 시, 트랜잭션 종료 시점에 변경 감지 기능이 작동해서 update sql을 실행한다.

 

팀 리포지토리를 보면 타입만 다르지 기본적인 CRUD 메서드가 똑같은 형태다.

 

CRUD 테스트를 진행한다.

@Test
public void basicCURD() {

    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 인터페이스를 설정해보자.

</repository/MemberRepository>

public interface MemberRepository extends JpaRepository<Member, Long> {
}

 

</repository/TeamRepository>

public interface TeamRepository extends JpaRepository<Team, Long> {
}

 

리포지토리가 인터페이스로 되어 있어서 따로 구현체를 만들어야 하나 싶지만 그렇지 않다.

@Autowired MemberRepoistory memberRepository; 처럼 그대로 주입 받아 사용해도 스프링 데이터 JPA가 자동으로 구현체를 만들어서 주입해준다.

실제로 getClass()로 찍어보면 프록시 객체로 주입된 것을 확인할 수 있다.

 

또한 @Repository 애노테이션을 생략할 수 있다.

컴포넌트 스캔을 스프링 데이터 JPA가 자동으로 처리해주기 때문이다.

 

 

공통 인터페이스 적용


</repository/MemberRepository>

public interface MemberRepository extends JpaRepository<Member, Long> {
}

 스프링 데이터 JPA는 JpaRepository 인터페이스를 extends 받으면 된다.

이 때, <엔티티 타입, 엔티티의 식별자 타입(PK)>으로 타입 매칭을 한다.

 

이제 CRUD 테스트를 진행해보자. 메서드를 하나도 구현하지 않았지만, 직접 메서드를 하나하나 구현한 리포지토리와 동일하게 정상 작동한다.

@Test
public void basicCURD() {

    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);

    findMember1.setUsername("member!!!!!!!");

    //리스트 조회 검증
    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);
}

 

 

공통 인터페이스 분석


JpaRepository 인터페이스 코드를 분석해보자.

package org.springframework.data.jpa.repository;

public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, ...

JpaRepository는 springframework.data.jpa.repository 패키지 아래 위치하고 있다. 스프링 리포지토리들 중에서 JPA에 특화된 기능들을 모아놓아 놓은 인터페이스이다.

 

이번엔 상위 인터페이스인 PagingAndSortingRepository 코드를 보자.

package org.springframework.data.repository;

public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID>

PagingAndSortingRepository는 페이징, 정렬 관련된 메서드를 제공하는 인터페이스다.

패키지 경로를 보면 springframework.data.repository로 JPA 뿐만이 아닌 공통으로 사용할 수 있는 리포지토리들은 해당 패키지 하위에 위치한다. 즉, MongoDB 등에서도 사용할 수 있다는 것이다.

 

그 상위 인터페이슨 CrudRepository를 보자.

package org.springframework.data.repository;

public interface CrudRepository<T, ID> extends Repository<T, ID>

기본적인 CRUD 기능을 제공하는 인터페이스이다.

역시 경로는 springframework.data.repository이고 가장 최상위 계층인 Repository 인터페이스를 extends한다.

 

JpaRepository는 이처럼 계층형 인터페이스로 구성되어 있고 특히 JPA에 특화된 리포지토리임을 나타낸다.

주요 메서드

save(S) : 새로운 엔티티는 저장하고 이미 있는 엔티티는 병합한다.

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

findById(ID) : 엔티티 하나를 조회한다. 내부에서 EntityManager.find() 호출

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

findAll(…) : 모든 엔티티를 조회한다. 정렬( Sort )이나 페이징( Pageable ) 조건을 파라미터로 제공할 수 있다.

 

이 밖에도 JpaRepository는 개발하면서 생각할 수 있는 대부분의 공통 메서드를 제공한다.

'java > jpa' 카테고리의 다른 글

[Spring Data JPA] 확장 기능  (0) 2021.05.30
[Spring Data JPA] 쿼리 메소드 기능  (0) 2021.05.24
[Spring Data JPA] 예제 도메인 모델  (0) 2021.05.23
[Spring Data JPA] 프로젝트 환경설정  (0) 2021.05.23
[JPA] 쿼리 파라미터 로그 남기기  (0) 2021.05.23
    'java/jpa' 카테고리의 다른 글
    • [Spring Data JPA] 확장 기능
    • [Spring Data JPA] 쿼리 메소드 기능
    • [Spring Data JPA] 예제 도메인 모델
    • [Spring Data JPA] 프로젝트 환경설정
    danuri
    danuri
    IT 관련 정보(컴퓨터 지식, 개발)를 꾸준히 기록하는 블로그입니다.

    티스토리툴바