순수 JPA 기반 리포지토리 생성
JPA 에서는 변경감지를 통해서 엔티티의 값이 변경 되면, 자동으로 update 쿼리문을 생성해서 DB에 날려주기 때문에, update 쿼리를 생성 할 필요가 없다.
리포지토리 생성
MemberJpaRepositoy
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);
}
}
테스트 코드 추가
리포지토리 테스트를 위해 MemberJpaRepositoryTest에 테스트 메서드 추가
@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(findMember1);
assertThat(findMember2).isEqualTo(findMember2);
//리스트 조회 검증
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 = "study.datajpa.repository")
public class AppConfig {}
- 스프링 부트 사용시
@SpringBootApplication
위치를 지정해준다. - 만약 위치가 달라지면
@EnableJpaRepository
필요
스프링 데이터 JPA가 구현 클래스 대신 생성해준다.
- 스프링 데이터 JPA에서
org.springframework.data.jpa.repository
를 구현한 클래스는 스캔대상이 되어 클래스를 대신 생성 해줌 @Repository
생략 가능 → 컴포넌트 스캔을 스프링 데이터 JPA가 자동으로 처리
적용 해보기
package study.datajpa.repository;
import org.springframework.data.jpa.repository.JpaRepository; //JpaRepository를 구현함으로서 스캔대상이 된다.
import study.datajpa.entity.Team;
public interface TeamRepository extends JpaRepository<Team, Long> {
}
테스트 코드 작성
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
@Rollback(false)
class MemberRepositoryTest {
@Autowired
MemberRepository memberRepository;
@Test
public void testMember() {
Member member = new Member("memberA");
Member savedMember = memberRepository.save(member);
Member findMember = memberRepository.findById(savedMember.getId()).get();
assertThat(findMember.getId()).isEqualTo(member.getId());
assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
assertThat(findMember).isEqualTo(member);
}
@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(findMember1);
assertThat(findMember2).isEqualTo(findMember2);
//리스트 조회 검증
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 테스트 코드와 다를게 없지만, MemberRepository
의 의존을 데이터 JPA로 변경하여 잘 동작하는지만 확인하였다. → 동일한 방식으로 동작
공통 인터페이스 분석
변경점
일부 메서드들은 Jpa 버전이 올라감에 따라 다음과 같이 변경 되었다.
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)
: 엔티티를 프록시로 조회한다. 내부에서EnitityManager.getReference()
호출findAll(…)
: 모든 엔티티를 조회한다. 정렬(Sort)나 페이징(Pageable) 조건을 파라미터로 제공할 수 있다.
정리
이번 장에서는 스프링 데이터 JPA가 제공하는 가장 강력한 기능 중 하나인 JpaRepository
인터페이스에 대해서 다루어 봤다.
사용 방법 : 사용하고자 하는 인터페이스에 JpaRepository<엔티티클래스, PK클래스>를 상속받아서 사용 가능하다.
반응형