반응형
공통 인터페이스 기능
- 순수 JPA 기반 리포지토리
- 스프링 데이터 JPA 공통 인터페이스 소개 및 활용
순수 JPA 기반 리포지토리
JPA에서 수정은 변경감지 기능을 사용하면 된다. 트랜잭션 안에서 엔티티를 조회한 다음에 데이터를 변경하면, 트랜잭션 종료 시점에 변경감지 기능이 작동해서 변경사항이 있으면 UPDATE SQL을 실행한다.
package study.datajpa.repository;
import org.springframework.stereotype.Repository;
import study.datajpa.entity.Member;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
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);
}
}
- 기본적인 CRUD를 구현하는 레포지토리를 구현해보자. 순수 JPA 기능은
find(), remove(), createQuery(), persist
등이 있으며 이 기능을 사용하여 CRUD를 구현한다.
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.*;
@SpringBootTest
@Transactional
class MemberJpaRepositoryTest {
private final MemberJpaRepository memberJpaRepository;
@Autowired
public MemberJpaRepositoryTest(MemberJpaRepository memberJpaRepository) {
this.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);
}
@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);
}
}
- 테스트 코드는 다음과 같이 작성할 수 있다.
@Transactional
은 트랜잭션 종료 시 롤백한다.
공통 인터페이스
@Configuration
@EnableJpaRepositories(basePackages = "jpabook.jpashop.repository")
public class AppConfig {}
- 스프링에선 다음과 같이 Config 설정이 필요하지만, 스프링 부트에선 생략 가능하다. 위치가 달라진다면,
@EnableJpaRepositories
설정이 필요하다.
- 인터페이스를 주입 받았음에도 구현체처럼 사용할 수 있는 원리는 다음과 같다.
MemberRepository
(Data JPA 인터페이스)가JpaRepository
를 상속 받으면 스프링에서class.com.sun.proxy.$ProxyXXX
와 같이 구현 프록시 객체를 생성해서 주입해준다.- 상속 받는 과정에서 컴포넌트 스캔의 대상으로도 등록해주기 때문에
@Repository
를 생략해도 된다.
T findOne(ID)
오타 (Optional<T> findById(ID)
로 변경)save(S)
: 새로운 엔티티는 저장하고, 있는 엔티티는 변경(병합)delete(T)
: 엔티티를 삭제한다. 내부에서EntityManager.remove()
호출findById(ID)
: 엔티티를 한 개 조회한다. 내부에서EntityManager.find()
호출getOne(ID)
: 엔티티를 프록시로 조회한다. 내부에서EntityManager.getReference()
호출findAll()
: 모든 엔티티를 조회한다. 정렬이나 페이징 조건을 파라미터로 제공한다.
JpaRepository는 대부분의 공통 메서드를 제공한다.
공통 기능이 아닌 별도의 커스텀 기능을 만들 때 쿼리 메서드 기능을 사용한다. 해당 내용은 다음 포스팅에서 정리해보자.
본 포스팅은 인프런 - 김영한님의 '실전! 스프링 데이터 JPA' 편을 정리한 내용입니다.
반응형