Spring Data JPA에 대한 OverView
Spring Data JPA
-> Spring Framework와 JPA이란 기반 위에서, [JPA]를 정말 편리하게 사용할 수 있게 해주는 Spring에서 제공하는 라이브
러리이다.
우리가 여태껏 복잡하게 순수 JPA만으로 개발해 왔던 Repository 구현체를 개발하지 않고, Spring에서 제공하는
[인터페이스]에서 [등록],[삭제],[수정] 등의 CRUD, 기본적으로 제공하므로 개발자는 그 API를 가져다 쓰기만 하면 된다.
(Repository 관련 개발 코드가 확연히 줄어 든다)
그러나, Spring에서 제공하는 Spring Data JPA는 JPA의 너무 많은 부분들을 자동화 및 추상화를 하기 때문에,
Spring Data JPA를 잘 사용하기 위해서는, Spring과 JPA에 대해서 100% 잘 알고 있어야만 활용 또한 잘 할 수가 있다.
여기서는 순수 JPA의 어떤 문제점 때문에 Spring Data JPA가 사용되는지 알아 보자!
@Repository
@RequiredArgsConstructor
public class MemberdJpaRepository {
@Autowired
private final EntityManager entityManager;
public Member save(Member member){
entityManager.persist(member);
return member;
}
public void delete(Member member){
entityManager.remove(member);
}
//전체 조회
public List<Member> findAll(){
return entityManager.createQuery("select m from Member m",Member.class)
.getResultList();
}
//단건 조회
public Member find(Long memberId){
return entityManager.find(Member.class,memberId);
}
//위 find() 메서드와 기능이 똑같지만, 반환형을 Optional 클래스로 해 줬을 뿐!
public Optional<Member> findById(Long id){
Member member = entityManager.find(Member.class, id);
return Optional.ofNullable(member); // member 객체를 Optional 객체로 한 번 Wrapping한다.
}
//조회된 객체의 개수
public Long count(){
return entityManager.createQuery("select count(m) from Member m",Long.class)
.getSingleResult();
}
}
@Repository
@RequiredArgsConstructor
public class TeamRepository {
private final EntityManager entityManager;
public Team save(Team team){
entityManager.persist(team);
return team;
}
public void delete(Team team){
entityManager.remove(team);
}
public List<Team> findAll(){
return entityManager.createQuery("select t FROM Team t",Team.class)
.getResultList();
}
// 단일 조회
public Team find(Long id){
Team team = entityManager.find(Team.class, id);
return team;
}
// 단일 조회 with Optional
public Optional<Team> findById(Long id){
Team team = entityManager.find(Team.class, id);
return Optional.ofNullable(team);
}
public Long count(){
return entityManager.createQuery("SELECT count(t) FROM Team t",Long.class)
.getSingleResult();
}
}
MemberRepository와 TeamRepository를 비교를 해 보자.
구현한 메서드가 기본적으로 같거나 거의 비슷하다.
Repository 계층에서는 기본적으로 기본적인 CRUD가 거의 다 같고, 디테일한 부분만 사실 다르다.
그럼에도 불구하고, MemberRepository,TeamRepository를 각각 중복 구현을 해야 한다는 번거로움이 있다.
만약 Repository가 2개가 아니라 10개라면???(실제 프로젝트에서 Repository가 달랑 2개인 경우는 거의 없다)
-> 이러한 중복 코딩 문제를 Spring Data JPA가 해결해준다.
(참고로, JpaRepository<>를 [공통 인터페이스]라고 부른다)
//@Repository : Spring Data JPA에서는 필요X
public interface MemberReposiotry extends JpaRepository<Member,Long> {
}
Spring(정확히는, Spring Data JPA)이 JpaRepository<>를 상속받은 [인터페이스]를 보고 [자동]으로 인터페이스에 대한
구현체(Proxy 구현체)를 생성해서 주입해 준다.
class MemberReposiotryTest {
@Autowired
MemberReposiotry repository; // Spring Data JPA를 설정한 인터페이스!(구현체는 Spring Data JPA가 자동 생성해서 주입)
@Test
@Transactional(readOnly = false)
@Rollback(value = false)
public void basicCRUD(){
Member member1 = new Member("member1");
Member member2 = new Member("member2");
repository.save(member1); // Spring Data JPA에서 제공하는 SAVE()이다.
repository.save(member2);
Optional<Member> findMember1 = repository.findById(member1.getId()); // Spring Data JPA에서 제공하는 findByID()이다.
Optional<Member> findMember2 = repository.findById(member2.getId());
//단일 조회 검증
assertThat(findMember1.get()).isEqualTo(member1);
assertThat(findMember2.get()).isEqualTo(member2);
//리스트 조회 검증
List<Member> all = repository.findAll(); // Spring Data JPA에서 제공하는 FindAll()이다.
assertThat(all.size()).isEqualTo(2);
//삭제 검증
repository.delete(member1); // Spring Data JPA에서 제공하는 DELETE이다.
repository.delete(member2);
long delete_count = repository.count();
assertThat(delete_count).isEqualTo(0);
}
Spring Data는 JPA뿐만 아니라, MONGO, REDIS 등 여러 DB에 공통적인 기능을 제공하는 계층이고
Spring Data [JPA]는 JPA에 특화된 기능을 제공하는 인터페이스이다.