[스프링 데이터 JPA] 3. 공통 인터페이스 기능
2024. 1. 24. 01:12
728x90
SMALL
📌 순수 JPA 기반 리포지토리 만들기
✔ 기본 CRUD
- 저장
- 변경 -> 변경감지 사용
- 트랜잭션 안에서 엔티티를 조회한 다음에 데이터를 변경하면, 트랜잭션 종료 시점에 변경 감지 기능이 작동해서 변경된 엔티티를 감지하고 UPDATE SQL 실행
- 삭제
- 전체 조회
- 단건 조회
- 카운트
회원
package study.datajpa.repository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import study.datajpa.entity.Member;
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);
}
}
팀
package study.datajpa.repository;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import study.datajpa.entity.Team;
import java.util.List;
import java.util.Optional;
@Repository
public class TeamJpaRepository {
@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 기반 리포지토리 테스트
회원
package study.datajpa.repository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
import study.datajpa.entity.Member;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
@Transactional
class MemberJpaRepositoryTest {
@Autowired
MemberJpaRepository memberJpaRepository;
@Test
public void testMember() {
Member member = new Member("memberA");
Member savedMember = memberJpaRepository.save(member);
Member findMember = memberJpaRepository.find(savedMember.getId());
assertThat(findMember.getId()).isEqualTo(member.getId());
assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
assertThat(findMember).isEqualTo(member); //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);
}
}
반응형
📌 공통 인터페이스 설정
⚡ JavaConfig 설정 - 스프링 부트 사용시 생략 가능
@Configuration
@EnableJpaRepositories(basePackages = "jpabook.jpashop.repository")
public class AppConfig {}
- 스프링 부트 사용시
@SpringBootApplication
위치를 지정(해당 패키지와 하위 패키지 인식) - 만약 위치가 달라지면
@EnableJpaRepositories
필요
⚡ 스프링 데이터 JPA가 구현 클래스 대신 생성
org.springframework.data.repository.Repository
를 구현한 클래스는 스캔 대상- MemberRepository 인터페이스가 동작한 이유
- 실제 출력해보기(Proxy)
- memberRepository.getClass() class com.sun.proxy.$ProxyXXX
@Repository
애노테이션 생략 가능- 컴포넌트 스캔을 스프링 데이터 JPA가 자동으로 처리
- JPA 예외를 스프링 예외로 변환하는 과정도 자동으로 처리
📌 공통 인터페이스 적용
⚡ 스프링 데이터 JPA 기반 MemberRepository
public interface MemberRepository extends JpaRepository<Member, Long>{
}
MemberRepository 테스트
package study.datajpa.repository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
import study.datajpa.entity.Member;
import java.util.List;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@Transactional
@Rollback(value = false)
class MemberRepositoryTest {
@Autowired
MemberRepository memberRepository;
@Test
public void testMember() {
//given
Member member = new Member("memberA");
Member savedMember = memberRepository.save(member);
//when
Member findMember = memberRepository.findById(savedMember.getId()).get();
//then
assertThat(findMember.getId()).isEqualTo(member.getId());
assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
assertThat(findMember).isEqualTo(member); //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);
}
}
- 기존 순수 JPA 기반 테스트에서 사용했던 코드를 그대로 스프링 데이터 JPA 리포지토리 기반 테스트로 변경해도 동일 한 방식으로 동작
TeamRepository 생성
package study.datajpa.repository;
import org.springframework.data.jpa.repository.JpaRepository;
mport study.datajpa.entity.Team;
public interface TeamRepository extends JpaRepository<Team, Long> {
}
- TeamRepository는 테스트 생략
- Generic
- T: 엔티티 타입
- ID: 식별자 타입(PK)
📌 공통 인터페이스 분석
- JpaRepository 인터페이스: 공통 CRUD 제공
- 제네릭은 <엔티티 타입, 식별자 타입> 설정
⚡ JpaRepository 공통 기능 인터페이스
public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
...
}
⚡ JpaRepository를 사용하는 인터페이스
public interface MemberRepository extends JpaRepository<Member, Long> {
}
📌 공통 인터페이스 구성
❗ 주의
T findOne(ID)
->Optional<T> findById(ID)
변경boolean exists(ID)
->boolean existsById(ID)
변경
✔ 제네릭 타입
T
: 엔티티ID
: 엔티티의 식별자 타입S
: 엔티티와 그 자식 타입
✔ 주요 메서드
save(S)
: 새로운 엔티티는 저장하고 이미 있는 엔티티는 병합delete(T)
: 엔티티 하나를 삭제 -> 내부에서EntityManager.remove()
호출findById(ID)
: 엔티티 하나를 조회 -> 내부에서EntityManager.find()
호출getOne(ID)
: 엔티티를 프록시로 조회 -> 내부에서EntityManager.getReference()
호출findAll(...)
: 모든 엔티티를 조회 -> 정렬(Sort
)이나 페이징(Pageable
) 조건을 파라미터로 제공 가능
cf) JpaRepository 는 대부분의 공통 메서드를 제공한다.
김영한님의 <실전! 스프링 데이터 JPA>을 수강하고 작성한 글입니다
PREV
NEXT
728x90
LIST
'인프런 Spring 강의 정리 > 실전! 스프링 데이터 JPA' 카테고리의 다른 글
[스프링 데이터 JPA] 6. 스프링 데이터 JPA 분석 (0) | 2024.01.24 |
---|---|
[스프링 데이터 JPA] 5. 확장 기능 (0) | 2024.01.24 |
[스프링 데이터 JPA] 4. 쿼리 메소드 기능 (0) | 2024.01.24 |
[스프링 데이터 JPA] 2. 예제 도메인 모델 (0) | 2024.01.24 |
[스프링 데이터 JPA] 1. 프로젝트 환경설정 (0) | 2024.01.24 |