
스프링 데이터 JPA를 사용하면, 리포지토리에 구현 클래스 없이 인터페이스만으로 개발을 완료할 수 있다.
반복 개발해온 CRUD기능도 스프링 데이터 JPA가 모두 제공한다.
package com.hello.hellospring.repository;
import com.hello.hellospring.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface SpringDataJpaRepository extends JpaRepository<Member, Long>, MemberRepository {
// 자동으로 쿼리를 짜줌
// SELECT M FROM MEMBER M WHERE M.NAME = ?
@Override
Optional<Member> findByName(String name);
}
인터페이스를 만들어 놓고, JpaRepository를 상속 받으면 빈으로 등록하지 않아도
SpringDataJpaMemberRepository구현체를 자동으로 빈으로 등록해줌. 즉, config 파일에서 빈을 추가로 등록하지 않아도 테스트가 실행된다!

공통인터페이스기 때문에 SpringDataJpaMemberRepository에 내장되어있다!
공통인터페이스에 없는findByName은 Override해줘야 한다.
사용하는 프로그램마다 name 혹은 email등으로 다를 수 있기 때문에 이 부분은 공통화 할 수 없다.
findByName : 뒤에 붙는 이름에 따라 자동으로 쿼리를 짜준다.
Name로 붙였을 경우 쿼리 👉SELECT M FROM MEMBER M WHERE M.NAME = ?
- 예시
// select m from m where m.id = ? and m.name = ? Optional<Member> findByIdAndName(Long id, String name);


@NoRepositoryBean
public interface JpaRepository<T, ID> extends ListCrudRepository<T, ID>, ListPagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
...
}
ListCrudRepository<T, ID>,
ListPagingAndSortingRepository<T, ID>,
QueryByExampleExecutor<T>를 상속한다.
@NoRepositoryBean
public interface ListCrudRepository<T, ID> extends CrudRepository<T, ID> {
...
}
CrudRepository를 상속함
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
...
}
Repository인터페이스를 상속함
@NoRepositoryBean
public interface ListPagingAndSortingRepository<T, ID> extends PagingAndSortingRepository<T, ID> {
...
}
PagingAndSortingRepository를 상속함
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends Repository<T, ID> {
...
}
Repository인터페이스를 상속함
public interface QueryByExampleExecutor<T> {
<S extends T> Optional<S> findOne(Example<S> example);
<S extends T> Iterable<S> findAll(Example<S> example);
<S extends T> Iterable<S> findAll(Example<S> example, Sort sort);
<S extends T> Page<S> findAll(Example<S> example, Pageable pageable);
<S extends T> long count(Example<S> example);
<S extends T> boolean exists(Example<S> example);
<S extends T, R> R findBy(Example<S> example, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction);
}
SpringDataJpaMemberRepository는 빈으로 등록하지 않아도 스프링에서 자동으로 등록해줌
package com.hello.hellospring.service;
import com.hello.hellospring.repository.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
private final MemberRepository memberRepository;
@Autowired
public SpringConfig(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Bean
public MemberService memberService(){
return new MemberService(memberRepository);
}
// SpringDataJpaMemberRepository는 빈으로 등록하지 않아도
// 스프링에서 자동으로 등록해준다.
// @Bean
//public MemberRepository memberRepository(){
// return new JpaMemberRepository(em);
// }
}
package com.hello.hellospring.service;
import com.hello.hellospring.domain.Member;
import com.hello.hellospring.repository.MemberRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
@SpringBootTest
// 자동으로 롤백됨
@Transactional
class MemberServiceIntergrationTest {
@Autowired MemberService memberService;
@Autowired
MemberRepository memberRepository;
@Test
// @Commit 붙이면 db에 저장됨
void join() {
// given 주어진 것
Member member = new Member();
member.setName("홍길동");
// when
Long id = memberService.join(member);
// then
Member result = memberService.findOne(id).get();
assertThat(member.getName()).isEqualTo(result.getName());
}
@Test
void 중복_회원_예외() throws Exception{
// given
Member member1 = new Member();
member1.setName("홍길동");
Member member2 = new Member();
member2.setName("홍길동");
// when
memberService.join(member1);
IllegalStateException e = assertThrows(IllegalStateException.class, () -> memberService.join(member2));
// memberService.join(member1);
// try{
// memberService.join(member2);
// fail();
// } catch(IllegalStateException e){
// assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
// }
}
}
