강의를 들으며 생각 정리 + "자바 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 |