강의 순서는
순수 JPA 리포지토리 만든다음,
순수 JPA 리포지토리 -> 스프링 데이터 JPA
저 순수 JPA 리포지토리를 똑같은 기능을 가진 스프링 데이터 JPA로 어떻게 만드냐?
이거임
먼저 멤버 리포지토리
@Repository
@RequiredArgsConstructor
public class MemberJpaRepository {
private final EntityManager em;
public Member save(Member member){
em.persist(member);
return member;
}
public Member find(Long id){
return em.find(Member.class, id);
}
public Optional<Member> findById(Long id){
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
public void delete(Member member){
em.remove(member);
}
public long count(){
return em.createQuery("select count(m) from Member m", Long.class)
.getSingleResult();
}
public List<Member> findAll(){
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
}
뭐 보면 앎.
findById는 저렇게 옵셔널로.
저렇게 옵셔널로 한번 감싸는 게 NullException 등 방지할 수 있음.
delete는 그냥 em.remove 해주면 됨.
count는 저렇게.. JPQL에서 SQL에 있는 메소드들은 거의 기본적으로 제공을 해 줌.
@Repository
@RequiredArgsConstructor
public class TeamRepository {
private final 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();
}
}
팀인데.
보면 굉장히 비슷함.
그리고 우리가 저것들?
CrudRepository 인터페이스에서 본 적 있음..
다름이 아니고, JpaRepository, 즉 Spring Data JPA가 자동으로 구현해 주는 게 그냥 저런 모양일 거임.
@Test
public void basicCRUD(){
Member member1 = new Member("member1", 10);
Member member2 = new Member("member2", 11);
memberJpaRepository.save(member1);
memberJpaRepository.save(member2);
Member findMember1 = memberJpaRepository.findById(member1.getId()).get();
Member findMember2 = memberJpaRepository.findById(member2.getId()).get();
assertThat(findMember1).isSameAs(member1);
assertThat(findMember2).isSameAs(member2);
List<Member> members = memberJpaRepository.findAll();
assertThat(members).containsOnly(member1, member2);
long count = memberJpaRepository.count();
assertThat(count).isEqualTo(2);
memberJpaRepository.delete(member1);
Optional<Member> deletedMember = memberJpaRepository.findById(member1.getId());
assertThat(deletedMember.isEmpty()).isTrue();
}
테스트. 기존에 있던 MemberRepository 테스트(더 정확히는 MemberJpaRepository의 테스트)에 추가함.
보면 저장 한다음,
찾아서,
찾을 때 저렇게 옵셔널을 .get()해서 바로 찾는 건 안좋다고 함.
.orElse()라고 따로 있는데 이런 걸로 null인지 아닌지 확인 하면서 하는 거 인듯.
여튼 그렇게 save()하고, 찾아서 isSameAs해보면,
맞음. assertThat 해봐도 무사히 넘어감.
isSameAs는 말 그대로 같은건지, 주소가 같은건지 물어보는 거고,
isEqualTo는 같은 값인지 물어보는 거.
근데 같은 트랜잭션(지금 테스트 자체를 클래스 레벨에 @Transactional 해 놓음) 내에서는 영속성 컨텍스트가 같은 레퍼런스를 보장함.
그래서 isSameAs 해도 맞아서 저 assertThat도 무사히 넘어감.
그 다음 findAll() 해서 다 가져오고,
containsOnly로 member1과 member2를 포함하는지,
그러니가 저 contains 종류가 3개 있는데
contains 그냥 포함여부
containsOnly 포함여부인데, 원소의 개수와 값이 같아야 함.
containsExactly 이거는 원소의 개수, 값, 순서까지 다 같아야 함.
이번엔 member1, member2 순서는 그다지 상관 없어서 containsOnly로 함.
저것도 무사히 넘어감.
우리가 지금 ddl-auto 옵션을 create로 써놔서, 테이블 존재하면 지우고 다시 생성하면서 시작함.
사실 이렇게 하는 것 보다는,
엔티티에 핵심 비즈니스 로직을 포함시키며 스프링 환경과 상관없이 단위 테스트를 만들어 놓고,
스프링 환경용 통합테스트? 단위테스트 같은 것도 따로 만들어 놓는 것이 좋은 것 일듯.
그 test패키지에 application.yml따로 준 다음 설정 주거나 datasource 비워두면 메모리 DB에서 테스트 할 수 있는걸로 앎.
그래서 그거는 그냥 한번 실행하고 끝나면 휘발되는거라..
지금은 test패키지에 따로 application.yml 안 만들어 놔서 그냥 main패키지의 application.yml 따라가서 실제로 DB에 테이블 생성되고 그러는 중.
여튼 거기에 ddl-auto 옵션을 create로 해 놔서 test도 그거 따라가서 게다가 @SpringBootTest라서 지금 내가 @Rollback(false)도 해놔서 DB에 값이 남음.
여튼 그렇고, 그렇게 containsOnly 검사했고,
이제 count는 우리가 createQuery로 짜 놓은
public long count(){
return em.createQuery("select count(m) from Member m", Long.class)
.getSingleResult();
}
이거 실행되서 반환됨.
그럼 우리가 ddl-auto가 create라서 아예 테이블 있으면 삭제됐다가 다시 생성되면서 member1, member2 넣고 시작하면서
저렇게 조회해 보면(애초에 저렇게 JPQL로 짜놓은 쿼리가 나가는 순간은 영속성 컨텍스트를 flush해 놓음. JPQL -> SQL을 하긴 하지만 또 좀 개발자가 어떤 쿼리를 적어 놓을 지 미지수니까 여러가지 면에서 반영시키기 위해 그 전에 미리 flush를 해 놓는 듯.)
그래서, 그렇게 member 2개 넣어놨으니까 그냥 매직넘버로 2가 맞는 지 검사.
참임. 저것도 넘어감.
그다음 member1을 삭제 해 봄.
public void delete(Member member){
em.remove(member);
}
그냥 이게 삭제.
그 후 조회해보면 이게 remove는 영속성 컨텍스트에서 제거시켜버리는 거라.
영속성 컨텍스트에 없으므로 DB에 쿼리가 날라간다.
그 후 똑같은 id로 조회해본다.
그럼 일단 Optional을 반환타입으로 해놨으니까 그게 오고,
그 옵셔널에 Optional.isEmpty() 해보면 비어있는지 아닌 지 나온다.
그게 .isTrue()인지 확인 해 본다.
이것도 delete해서 지워서, 없는걸로, 즉 null이므로 참으로 넘어간다.
영속성 컨텍스트에 관한 건
https://qwefdg3.tistory.com/707
여기 참조.
'스프링데이터 + JPA > 스프링 데이터 JPA' 카테고리의 다른 글
7. 공통 인터페이스 적용 (0) | 2023.11.19 |
---|---|
6. 공통 인터페이스 설정 (0) | 2023.11.19 |
4. 예제 도메인 모델 구현 (0) | 2023.11.18 |
3. 스프링 데이터 JPA, DB 설정, 동작 확인 (0) | 2023.11.18 |
2. 프로젝트 생성 (0) | 2023.11.18 |