참고 링크 : https://wikidocs.net/book/7601
오라클DB와 intelliJ로 작업하였습니다
리포지터리
엔티티만으로는 데이터베이스에 데이터를 저장하거나 조회 할 수 없다.
데이터 처리를 위해서는 실제 데이터베이스와 연동하는 JPA 리포지터리가 필요하다.
리포지터리란?
리포지터리는 엔티티에 의해 생성된 데이터베이스 테이블에 접근하는 메서드들(예: findAll, save 등)을 사용하기 위한 인터페이스이다. 데이터 처리를 위해서는 테이블에 어떤 값을 넣거나 값을 조회하는 등의 CRUD(Create, Read, Update, Delete)가 필요하다. 이 때 이러한 CRUD를 어떻게 처리할지 정의하는 계층이 바로 리포지터리이다.
다음과 같이 QuestionRepository 인터페이스를 생성하자.
package com.gosari.repick_project.Repository;
import com.gosari.repick_project.Entity.Question;
import org.springframework.data.jpa.repository.JpaRepository;
public interface QuestionRepository extends JpaRepository<Question, Integer> {
}
QuestionRepository는 리포지터리로 만들기 위해 JpaRepository 인터페이스를 상속했다.
JpaRepository를 상속할 때는 제네릭스 타입으로 <Question, Integer> 처럼 리포지터리의 대상이 되는 엔티티의 타입(Question)과 해당 엔티티의 PK의 속성 타입(Integer)을 지정해야 한다. 이것은 JpaRepository를 생성하기 위한 규칙이다.
Question 엔티티의 PK(Primary Key) 속성인 id의 타입은 Integer 이다.
마찬가지로 AnswerRepository도 다음과 같이 생성하자.
package com.gosari.repick_project.Repository;
import com.gosari.repick_project.Entity.Answer;
import org.springframework.data.jpa.repository.JpaRepository;
public interface AnswerRepository extends JpaRepository <Answer, Integer>{
}
이제 QuestionRepository, AnswerRepository를 이용하여 question, answer 테이블에 데이터를 저장하거나 조회할 수 있다.
+ 리포지터리 추가설명
@Repository
-Entity에 의해 생성된 DB에 접근하는 메서드(ex) findAll()) 들을 사용하기 위한 인터페이스이다. 위에서 엔티티를 선언함으로써 데이터베이스 구조를 만들었다면, 여기에 어떤 값을 넣거나, 넣어진 값을 조회하는 등의 CRUD(Create, Read, Update, Delete)를 해야 쓸모가 있는데, 이것을 어떻게 할 것인지 정의해주는 계층이라고 생각하면 된다.
JpaRepository를 상속받도록 함으로써 기본적인 동작이 모두 가능해진다! JpaRepository는 어떤 엔티티를 메서드의 대상으로 할지를 다음 키워드로 지정한다. JpaRepository<대상으로 지정할 엔티티, 해당 엔티티의 PK의 타입>.
이렇게 extends를 통해서 상속받고나면
해당 레포지토리의 객체를 이용해서 기본적으로 제공되는 메서드(save(), findAll(), get()) 등을 사용할 수 있게 된다.
데이터 저장하기
작성한 리포지터리를 테스트하기 위해서 JUnit 기반의 스프링부트의 테스트 프레임워크를 사용해 보자.
package com.gosari.repick_project;
import com.gosari.repick_project.Entity.Question;
import com.gosari.repick_project.Repository.QuestionRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDateTime;
@SpringBootTest
public class testApplicationTests {
@Autowired //자동으로 QuestionRepository 객체를 스프링이 자동으로 생성
private QuestionRepository questionRepository;
@Test
void testJpa(){
Question q1 = new Question();
q1.setSubject("스프링부트가 무엇인가요?");
q1.setContent("스프링부트에 대해서 알고싶습니다.");
q1.setCreateDate(newDate());
this.questionRepository.save(q1); //첫번째 질문 저장
Question q2 = new Question();
q2.setSubject("스프링부트 모델 질문입니다.");
q2.setContent("id는 자동으로 생성되나요?");
q2.setCreateDate(newDate());
this.questionRepository.save(q2); //두번째 질문 저장
}
}
.NOW() -> 오라클의 경우 newDate()
MySQL에서 현재 시간을 값이 필요한 경우가 있습니다.
이 때 사용하는 함수가 NOW 함수입니다.
NOW 함수는 현재 MySQL 서버의 시간 값을 가져오는 함수입니다.
출처: https://extbrain.tistory.com/57 [확장형 뇌 저장소:티스토리]
@SpringBootTest 애너테이션은 SbbApplicationTests 클래스가 스프링부트 테스트 클래스임을 의미한다. 그리고 @Autowired 애너테이션은 스프링의 DI 기능으로 questionRepository 객체를 스프링이 자동으로 생성해 준다.
DI(Dependency Injection) - 스프링이 객체를 대신 생성하여 주입한다.
@Autowired
객체를 주입하기 위해 사용하는 스프링의 애너테이션이다.
객체를 주입하는 방식에는 @Autowired 외에 Setter 또는 생성자를 사용하는 방식이 있다.
순환참조 문제와 같은 이유로 @Autowired 보다는 생성자를 통한 객체 주입방식이 권장된다.
하지만 테스트 코드의 경우에는 생성자를 통한 객체의 주입이 불가능하므로 테스트 코드 작성시에만
@Autowired를 사용하고 실제 코드 작성시에는 생성자를 통한 객체 주입방식을 사용하겠다.
@Test
testJpa 메서드 위의 @Test 애너테이션은 testJpa 메서드가 테스트 메서드임을 나타낸다.
위 클래스를 JUnit으로 실행하면 @Test 애너테이션이 붙은 메서드가 실행된다.
JUnit은 테스트코드를 작성하고 작성한 테스트코드를 실행하기 위해 사용하는 자바의 테스트 프레임워크이다.
testJpa 메서드의 내용을 잠시 살펴보자. testJpa 메서드는 q1, q2 라는 Question 엔티티 객체를 생성하고 QuestionRepository를 이용하여 그 값을 데이터베이스에 저장하는 코드이다.
이제 작성한 SbbApplicationTests 클래스를 실행해 보자.
DB관리툴에서 우리가 저장한 Question 객체의 값이 데이터베이스에 저장된 것을 확인한 수 있다.
id는 Question 엔티티의 기본 키(Primary Key)이다. id는 앞에서 엔티티를 생성할 때 설정했던대로 데이터를 생성할 때 속성값이 자동으로 1씩 증가하는 것을 확인할 수 있다. Question 엔티티의 id는 @GeneratedValue 설정을 했다.
데이터 조회하기
이번에는 데이터베이스에 저장된 데이터를 조회해 보자.
findAll
작성한 테스트 코드를 다음처럼 수정해 보자.
package com.gosari.repick_project;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.gosari.repick_project.Entity.Question;
import com.gosari.repick_project.Repository.QuestionRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDateTime;
import java.util.List;
@SpringBootTest
public class testApplicationTests {
@Autowired //자동으로 QuestionRepository 객체를 스프링이 자동으로 생성
private QuestionRepository questionRepository;
@Test
void testJpa(){
List<Question> all = this.questionRepository.findAll();
assertEquals(2, all.size());
Question q = all.get(0);
assertEquals("스프링부트가 무엇인가요?", q.getSubject());
}
}
question 테이블에 저장된 모든 데이터를 조회하기 위해서 리포지터리의 findAll 메서드를 사용했다.
findAll은 데이터를 조회할때 사용하는 메서드이다.
우리는 총 2건의 데이터를 저장했기 때문에 데이터의 사이즈는 2가 되어야 한다.
데이터 사이즈가 2인지 확인하기 위해 JUnit의 assertEquals 메서드를 사용했다.
assertEquals는 assertEquals(기대값, 실제값)와 같이 사용하고 기대값과 실제값이 동일한지를 조사한다.
만약 기대값과 실제값이 동일하지 않다면 테스트는 실패로 처리된다.
그리고 우리가 저장한 첫번째 데이터의 제목이 "sbb가 무엇인가요?"와 일치하는지도 테스트했다.
테스트를 위해서는 로컬 서버를 중지하고 다시한번 [Run -> Run As -> JUnit Test]을 실행하면 된다.
테스트는 잘 통과될 것이다.
findById
이번에는 Question 엔티티의 Id값으로 데이터를 조회해 보자. 테스트 코드를 다음과 같이 수정하자.
package com.gosari.repick_project;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.gosari.repick_project.Entity.Question;
import com.gosari.repick_project.Repository.QuestionRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@SpringBootTest
public class testApplicationTests {
@Autowired //자동으로 QuestionRepository 객체를 스프링이 자동으로 생성
private QuestionRepository questionRepository;
@Test
void testJpa(){
Optional<Question> oq = this.questionRepository.findById(1);
if(oq.isPresent()){
Question q = oq.get();
assertEquals("스프링부트가 무엇인가요?", q.getSubject());
}
}
}
id 값으로 데이터를 조회하기 위해서는 리포지터리의 findById 메서드를 사용해야 한다.
하지만 findById의 리턴 타입은 Question이 아닌 Optional임에 주의하자.
Optional은 null 처리를 유연하게 처리하기 위해 사용하는 클래스로
위와 같이 isPresent로 null이 아닌지를 확인한 후에 get으로 실제 Question 객체 값을 얻어야 한다.
.
.
.
추가내용 링크
'Follow Work > SpringbootBoard' 카테고리의 다른 글
[StringBoot] 서비스 (6) (0) | 2022.08.11 |
---|---|
[StringBoot] ROOT URL (5) (0) | 2022.08.11 |
[StringBoot] 질문 목록과 템플릿 (4) (0) | 2022.08.11 |
[StringBoot] 엔티티 (2) (0) | 2022.08.11 |
[StringBoot] 프로젝트 설정 (1) (0) | 2022.08.11 |