일단 순수 JPA 기반으로 리포지토리를 만들고 스프링 데이터 JPA 를 적용해보자!
📌 기본
CRUD
- 저장
- 변경 → 변경감지 사용
- 삭제
- 전체 조회
- 단건 조회
- 카운트
JPA
기반 리포지토리 - 회원Create
(저장)public Member save(Member member) {
em.persist(member);
return member;
}
Read
(조회)//전체 조회
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);
}
Update
(변경) → 변경감지 사용JPA는 기본적으로 엔티티 변경감지로 인해 데이터를 바꾼다!
트랜잭션 안에서 엔티티를 조회한 다음에 데이터를 변경하면,
트랜잭션 종료 시점에 변경감지 기능이 작동해서 변경된 엔티티를 감지하고 UPDATE SQL
을 실행한다.
즉, 따로 update
를 구현할 필요가 없다!
Delete
(삭제)public void delete(Member member) {
em.remove(member);
}
Count
(카운트)public long count() {
return em.createQuery("select count(m) from Member m", Long.class)
.getSingleResult();
}
JPA
기반 리포지토리 - 팀Create
(저장)public Team save(Team team) {
em.persist(team);
return team;
}
Read
(조회)//전체 조회
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);
}
Update
(변경) → 변경감지 사용위에서 언급했듯이, 이것 역시 구현하지 않아도 됨.
Delete
(삭제)public void delete(Team team) {
em.remove(team);
}
Count
(카운트)public long count() {
return em.createQuery("select count(t) from Team t", Long.class)
.getSingleResult();
}
Member
와Team
의Repository
코드가 매우 유사하다는 것을 알 수 있다! 이러한 중복을 스프링 데이터 JPA가 해결할 수 있다.
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);
}
개발자가 interface
만 선언해주면 스프링 데이터 JPA
가 자동으로 구현 클래스를 만들어준다.
😲 프로젝트 환경 설정에서
MemberRepository
인터페이스가 동작할 수 있었던 이유가 바로 이것이다 ❗
@Repository //생략 가능
public interface TeamRepository extends JpaRepository<Team, Long> {
}
@Repository
어노테이션을 생략해도 된다.
스프링 데이터 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);
}
앞에서 사용했던 테스트 코드를 복사해서
memberJpaRepository
→ memberRepository
로 수정하기만 하면 잘 동작한다.
→ 거의 유사하기 때문이다 ❗❗
JpaRepository
인터페이스: 공통 CRUD 제공📌 T findOne(ID)
→ Optional<T> findById(ID)
로 변경되었다.
T
: 엔티티ID
: 엔티티의 식별자 타입S
: 엔티티와 그 자식 타입save(S)
: 새로운 엔티티는 저장하고 이미 있는 엔티티는 병합한다.delete(T)
: 엔티티 하나를 삭제한다. 내부에서 EntityManager.remove()
를 호출한다.findById(ID)
: 엔티티 하나를 조회한다. 내부에서 EntityManager.find()
를 호출한다.getOne(ID)
: 엔티티를 프록시로 조회한다. 내부에서 EntityManager.getReference()
를 호출한다.findAll(...)
: 모든 엔티티를 조회한다. 정렬(Sort
)이나 페이징(Pageable
) 조건을 파라미터로 제공 가능하다.