728x90
반응형
순수 JPA 기반 레포지토리 만들기
순수 JPA를 기반으로 레포지토리를 만들어 추후 스프링 데이터 JPA에서는 어떻게 간단해지는지 알아보자
Member 레포지토리
package study.datajpa.repository;
@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);
}
}
- JPQL은 객체를 대상으로 쿼리하는 것으로 SQL과 비슷한 모습과 문법을 갖고 있다.
- Optional<T> : NULL이 올 수 있는 값을 감싸는 Wrapper 클래스로 NullPointerException이 발생하지 않도록 도와주며 다양한 함수를 제공한다. (참고 : https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html)
- 기본 CRUD를 구성하며 수정은 변경감지 기능을 사용하기 때문에 별도 구성이 필요없다.
Team 레포지토리
package study.datajpa.repository;
@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();
}
}
동작확인
package study.datajpa.repository;
@SpringBootTest
@Transactional
@Rollback(value = false)
public class MemberJpaRepositoryTest {
@Autowired
MemberJpaRepository memberJpaRepository;
@PersistenceContext
EntityManager em;
@Test
public void testMember() {
Member member = new Member("memberA");
Member savedMember = memberJpaRepository.save(member);
Member findMember = memberJpaRepository.find(savedMember.getId());
Assertions.assertThat(findMember.getId()).isEqualTo(member.getId());
Assertions.assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
Assertions.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();
Assertions.assertThat(findMember1).isEqualTo(member1);
Assertions.assertThat(findMember2).isEqualTo(member2);
// 리스트 검증
List<Member> all = memberJpaRepository.findAll();
Assertions.assertThat(all.size()).isEqualTo(2);
// 카운트 검증
long count = memberJpaRepository.count();
Assertions.assertThat(count).isEqualTo(2);
// 삭제 검증
memberJpaRepository.delete(member1);
memberJpaRepository.delete(member2);
// 삭제 후 카운트 검증
long deletedCount = memberJpaRepository.count();
Assertions.assertThat(deletedCount).isEqualTo(0);
}
}
@Rollback(value = "false")
를 사용하여 테스트 후에도 롤백하지 않도록 지정
공통 인터페이스 설정
Java Config 설정을 통해 공통 인터페이스 설정이 필요하지만 스프링 부트 사용시 생략이 가능하다.
@Configuration
@EnableJpaRepositories(basePackages = "jpabook.jpashop.repository")
public class AppConfig {}
@SpringBootApplication
이 사용된 클래스 위치부터 하위 경로를 다 스프링 데이터 JPA가 인식한다.- 패키지가 다를 경우
@EnableJpaRepositories
어노테이션을 사용하여 경로를 잡아줘야 한다.
스프링 데이터 JPA 동작
- 구현 클래스에서 JpaRepository 또는 CrudRepository를 상속받으면 스프링 데이터 JPA가 자동으로 구현체를 생성하여 주입해준다.
- 스프링 데이터 JPA가 자바의 기본 프록시 기술을 사용하여 프록시 객체를 주입해준다.
@Repository
어노테이션을 생략 할 수 있다.
- 컴포넌트 스캔을 스프링 데이터 JPA가 자동으로 처리
- JPA 예외를 스프링 예외로 변환하는 과정도 자동으로 처리
공통 인터페이스 적용
구현할 레포지토리 인터페이스에 extends JpaRepository<Entity Type, Entity key Type>
으로 사용한다.
※ Entity key Type : 엔티티 내 @Id
어노테이션이 선언되어 있는 필드의 타입
Member 레포지토리
package study.datajpa.repository;
public interface MemberRepository extends JpaRepository<Member, Long> {}
Member 레포지토리 테스트
package study.datajpa.repository;
@SpringBootTest
@Transactional
@Rollback(value = false)
public class MemberRepositoryTest {
@Autowired
MemberRepository memberRepository;
@Autowired
TeamRepository teamRepository;
@PersistenceContext
EntityManager em;
@Test
public void testMember() {
Member member = new Member("memberA");
Member savedMember = memberRepository.save(member);
Member findMember = memberRepository.findById(savedMember.getId()).get();
Assertions.assertThat(findMember.getId()).isEqualTo(member.getId());
Assertions.assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
Assertions.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();
Assertions.assertThat(findMember1).isEqualTo(member1);
Assertions.assertThat(findMember2).isEqualTo(member2);
// 리스트 검증
List<Member> all = memberRepository.findAll();
Assertions.assertThat(all.size()).isEqualTo(2);
// 카운트 검증
long count = memberRepository.count();
Assertions.assertThat(count).isEqualTo(2);
// 삭제 검증
memberRepository.delete(member1);
memberRepository.delete(member2);
// 삭제 후 카운트 검증
long deletedCount = memberRepository.count();
Assertions.assertThat(deletedCount).isEqualTo(0);
}
}
- 순수 JPA 레포지토리 테스트를 스프링 데이터 JPA를 적용한 후에 테스트해도 동일하게 동작한다.
Team 레포지토리
package study.datajpa.repository;
public interface TeamRepository extends JpaRepository<Team, Long> {}
공통 인터페이스 분석
공통 인터페이스
- JpaRepository 인터페이스 : 공통 CRUD 기능 제공
public interface JpaRepository<T, ID extends Serializable>
extends PagingAndSortingRepository<T, ID>
{
...
}
공통 인터페이스 구성
T findOne(ID)
->Optional<T> findById(ID)
- T : 엔티티, ID : 엔티티의 식별자 타입, S : 엔티티와 그 자식 타입
공통 인터페이스 주요 함수
save(S)
: 새로운 엔티티는 저장하고 이미 있는 엔티티는 병합한다.delete(T)
: 엔티티 하나를 삭제한다. 내부에서EntityManager.remove()
호출findById(ID)
: 엔티티 하나를 조회한다. 내부에서EntityManager.find()
호출getOne(ID)
: 엔티티를 프록시로 조회한다. 내부에서EntityManager.getReference()
호출
※EntityManager.getReference()
: 실제 엔티티가 아닌 프록시를 이용하여 가짜 호출, 이후 프록시 초기화 시 실제 엔티티 호출findAll(...)
: 모든 엔티티를 조회한다. 정렬이나 페이징조건을 파라미터로 제공할 수 있다.
728x90
반응형
'Dev > Spring Data JPA' 카테고리의 다른 글
[Spring Data JPA] 확장 기능 (0) | 2021.12.13 |
---|---|
[Spring Data JPA] 쿼리 메소드 기능 (0) | 2021.12.09 |
[Spring Data JPA] 예제 도메인 모델 (0) | 2021.11.29 |
[Spring Data JPA] 프로젝트 환경설정 (0) | 2021.10.22 |
[Spring Data JPA] 강좌 소개 (0) | 2021.10.22 |
댓글