📚 공부한 책 : 코드로배우는 스프링 부트 웹프로젝트
Spring Data JPA 에는 여러 종류의 인터페이스의 기능을 통해 JPA관련 작업을 별도의 코드 없이 처리할 수 있도록 지원한다
ex) CRUD작업,페이징,정렬 등을 인터페이스의 메서드를 호출하는 형태로 처리한다 (기능에 따라 상속 구조로 추가적인 기능을 제공한다)
- JpaRepository 인터페이스 : 모든 JPA 관련 기능을 사용하고 싶을때 이용한다
아래와 같이 JPA 인터페이스를 생성한다import com.example.entity.Memo; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository // JPA를 사용할 때는 엔티티의 타입정보(Memo)와 @Id 타입(Long)을 지정한다 // JPA는 인터페이스 선언만으로도 자동으로 스프링의 빈(Bean)으로 등록된다 public interface MemoRepository extends JpaRepository<Memo,Long> { }
위에서 작성한 MemoRepository를 이용해 작성된 테이블에 SQL없이 CRUD테스트를 진행한다 (다음과 같은 메서드를 활용한다)
- insert : save(entity 객체)
- select : findById(키 타입),getOne(키 타입)
- update : save(entity 객체)
- delete : deleteById(키 타입),delete(entity 객체)
◆ insert 와 update는 동일한 메서드를 사용한다 -> save()
=> JPA의 구현체가 메모리상에서 객체를 비교하고 없으면 insert 존재한다면 update를 동작시키는 방식으로 동작하기 때문이다
테스트 클래스를 아래와 같이 생성한다
import com.example.entity.Memo; import com.example.repository.MemoRepository; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.Optional; import java.util.stream.IntStream; @SpringBootTest public class MemberRepositoryTest { @Autowired MemoRepository memoRepository; @Test public void testInsert(){ // getClass() 메소드는 MemoRepository 인터페이스 타입의 실제 객체가 어떤것인지 확인한다 System.out.println(memoRepository.getClass().getName()); } }
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.example.ex2.repository.MemberRepositoryTest': Unsatisfied dependency expressed through field 'memoRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.repository.MemoRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.repository.MemoRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1799) ~[spring-beans-5.3.15.jar:5.3.15]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1355) ~[spring-beans-5.3.15.jar:5.3.15]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1309) ~[spring-beans-5.3.15.jar:5.3.15]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:656) ~[spring-beans-5.3.15.jar:5.3.15]
76 common frames omitted
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication
@EntityScan("com.example")
@EnableJpaRepositories("com.example")
public class Ex2Application {
public static void main(String[] args) {
SpringApplication.run(Ex2Application.class, args);
}
}
테스트 코드 작성
@SpringBootTest public class MemberRepositoryTest { @Autowired MemoRepository memoRepository; // 등록 @Test public void testInsertDummies(){ // 100개의 새로운 Memo객체를 생성하고 IntStream.rangeClosed(1,100).forEach(i -> { Memo memo = Memo.builder().memoText("Memo..." + i).build(); // 저장한다 memoRepository.save(memo); }); } }
테스트 코드 작성
@SpringBootTest public class MemberRepositoryTest { @Autowired MemoRepository memoRepository; //조회 @Test public void testSelect(){ Long mno = 100L; Optional<Memo> result = memoRepository.findById(mno); System.out.println("================"); if(result.isPresent()){ Memo memo = result.get(); System.out.println(memo); } } }
Hibernate:
select
memo0_.mno as mno1_0_0_,
memo0_.memo_text as memo_tex2_0_0_
from
tbl_memo memo0_
where
memo0_.mno=?
================
Memo(mno=100, memoText=Memo...100)
테스트 코드 작성
import com.example.entity.Memo; import com.example.repository.MemoRepository; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.Optional; import java.util.stream.IntStream; @SpringBootTest public class MemberRepositoryTest { @Autowired MemoRepository memoRepository; @Test public void testUpdate(){ // 객체 생성 Memo memo = Memo.builder().mno(1L).memoText("Update Text......").build(); System.out.println("1-------------"); memoRepository.save(memo); System.out.println("2-------------"); } }
- JPA는 엔티티 객체들을 메모리상에 보관하려고 하기 때문에 특정한 엔티티 객체가 존재 하는지 확인하는 select가 먼저 실행되고 해당 @Id를 가진 엔티티 객체가 있다면 update, 그렇지 않다면 insert를 실행하게 된다
1-------------
Hibernate:
select
memo0_.mno as mno1_0_0_,
memo0_.memo_text as memo_tex2_0_0_
from
tbl_memo memo0_
where
memo0_.mno=?
Hibernate:
update
tbl_memo
set
memo_text=?
where
mno=?
2-------------
테스트 코드 작성
import com.example.entity.Memo; import com.example.repository.MemoRepository; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.Optional; import java.util.stream.IntStream; @SpringBootTest public class MemberRepositoryTest { @Autowired MemoRepository memoRepository; @Test public void testDelete(){ Long mno = 5L; memoRepository.deleteById(mno); } }
Hibernate:
select
memo0_.mno as mno1_0_0_,
memo0_.memo_text as memo_tex2_0_0_
from
tbl_memo memo0_
where
memo0_.mno=?
Hibernate:
delete
from
tbl_memo
where
mno=?