[Spring-입문] 스프링 데이터 JPA

DANI·2023년 11월 19일

Spring[김영한T]

목록 보기
11/31
post-thumbnail
  • 스프링 데이터 JPA를 사용하면, 리포지토리에 구현 클래스 없이 인터페이스만으로 개발을 완료할 수 있다.

  • 반복 개발해온 CRUD기능도 스프링 데이터 JPA가 모두 제공한다.



💾 SpringDataJpaMemberRepository 인터페이스 생성

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 파일에서 빈을 추가로 등록하지 않아도 테스트가 실행된다!



💡 다른 메서드는 Override 안하는 이유?

공통인터페이스기 때문에 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);

💡 공통 인터페이스 알아보기


🔍 자세히 보기


🔴 JpaRepository 인터페이스

@NoRepositoryBean
public interface JpaRepository<T, ID> extends ListCrudRepository<T, ID>, ListPagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
...
}

ListCrudRepository<T, ID>,
ListPagingAndSortingRepository<T, ID>,
QueryByExampleExecutor<T> 를 상속한다.


🔵 ListCrudRepository 인터페이스

@NoRepositoryBean
public interface ListCrudRepository<T, ID> extends CrudRepository<T, ID> {
...
}

CrudRepository 를 상속함

  • CrudRepository 인터페이스

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
...
}

Repository 인터페이스를 상속함


🔵 ListPagingAndSortingRepository 인터페이스

@NoRepositoryBean
public interface ListPagingAndSortingRepository<T, ID> extends PagingAndSortingRepository<T, ID> {
...
}

PagingAndSortingRepository를 상속함

  • PagingAndSortingRepository 인터페이스

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends Repository<T, ID> {
...
}

Repository 인터페이스를 상속함


🔵 QueryByExampleExecutor 인터페이스

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);
}





스프링 데이터 JPA를 사용하도록 config 파일 수정

SpringDataJpaMemberRepository는 빈으로 등록하지 않아도 스프링에서 자동으로 등록해줌

💾 SpringConfig 파일 수정

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);
   // }
}


💻 Test 파일

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("이미 존재하는 회원입니다.");
//        }
    }
}

🔵 실행결과

0개의 댓글