이제까지 질문에 대해서 데이터를 다루었다면 다음으로는 답변에 대하여 다루어보도록 하겠다.

 


 

답변 데이터 생성 후 저장하기

 

SshApplicationTests.java

package com.myspringboot.ssh;

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.time.LocalDateTime;
import java.util.Optional;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest // @SpringBootTest 애너테이션은 SbbApplicationTests 클래스가 스프링부트 테스트 클래스임을 의미
class SshApplicationTests {

	@Autowired // @Autowired 애너테이션은 스프링의 DI 기능으로 questionRepository 객체를 스프링이 자동으로 생성
	private QuestionRepository questionRepository;
	
	@Autowired
    private AnswerRepository answerRepository;
	
	
	@Test
	void testJpa() {
		
		Optional<Question> oq = questionRepository.findById(2);
		assertTrue(oq.isPresent());
		Question q = oq.get();
		
		Answer a = new Answer();
		a.setContent("네 자동으로 생성됩니다.");
		a.setQuestion(q); // 어떤 질문에 대한 답변인지에 대하여 Question 객체가 필요
		a.setCreateDate(LocalDateTime.now());
		answerRepository.save(a);
		
	} 

}

답변 레포지터리 사용을 위해 AnswerRepository 객체를 @Autowired 로 주입했다.

답변 데이터를 입력하기 위해서 먼저 답변을 달기위한 질문의 번호를 알아야한다. 그렇기 때문에 질문에 대한 데이터를 먼저 가져온 다음 Answer 엔티티의 question 속성에 대입하였다.

 

위 코드를 실행한 후 정상적으로 동작이 되었다면 H2 콘솔로 ANSWER 테이블을 확인하면 다음과 같은 데이터가 출력 될 것이다.

 


답변 조회하기

 

SshApplicationTests.java

package com.myspringboot.ssh;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Optional;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest // @SpringBootTest 애너테이션은 SbbApplicationTests 클래스가 스프링부트 테스트 클래스임을 의미
class SshApplicationTests {

	@Autowired // @Autowired 애너테이션은 스프링의 DI 기능으로 questionRepository 객체를 스프링이 자동으로 생성
	private QuestionRepository questionRepository;
	
	@Autowired
    private AnswerRepository answerRepository;
	
	
	@Test
	void testJpa() {
		
		Optional<Answer> oa = answerRepository.findById(1); // id 값이 1인 답변을 조회
		assertTrue(oa.isPresent()); // 데이터가 있는지 확인
		Answer a = oa.get();
		assertEquals(2, a.getQuestion().getId()); // 답변의 질문 아이디가 2인지 확인
	} 

}

 

 


답변에 열결된 질문 찾기 & 질문에 달린 답변 찾기

 

Answer 엔티티의 question 속성을 이용하면 답변에 연결된 질문의 아이디를 알 수 있고

그것을 통해 질문을 조회할 수 있다.

a.getQuestion()

 

반대로 질문에서 답변을 찾을 수 있는 방법으로는 이전에 Question 엔티티에 정의한 answerList를 사용하면 쉽게 구할 수 있다.

 

SshApplicationTests.java

package com.myspringboot.ssh;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest // @SpringBootTest 애너테이션은 SbbApplicationTests 클래스가 스프링부트 테스트 클래스임을 의미
class SshApplicationTests {

	@Autowired // @Autowired 애너테이션은 스프링의 DI 기능으로 questionRepository 객체를 스프링이 자동으로 생성
	private QuestionRepository questionRepository;
	
	@Autowired
    private AnswerRepository answerRepository;
	
	
	@Test
	void testJpa() {
		
		Optional<Question> oq = questionRepository.findById(2);
		assertTrue(oq.isPresent());
		Question q = oq.get();
		
		List<Answer> answerList = q.getAnswerList();
		
		assertEquals(1, answerList.size());
		assertEquals("네 자동으로 생성됩니다.", answerList.get(0).getContent());
	} 

}

위 코드는 질문객체로 부터 답변 리스트를 구하는 테스트 코드이다. 하지만 위 코드를 실행하면 다음과 같은 에러가 발생한다.

 

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.myspringboot.ssh.Question.answerList, could not initialize proxy - no Session
...
(생략)

 

이런 에러가 발생하는 이유는 Question 리포지터리가 findById를 호출하여 Question 객체를 조회하고 나면 DB세션이 끊어지기 때문에 그 이후에 a.getAnswerList() 메소드는 세션이 종료되어 오류가 발생하게 된다.

답변 데이터 리스트는 q 객체를 조회할 때 가져오지 않고 q.getAnswerList(); 메소드를 호출하는 시점에 가져오기 때문이다.

 

하지만 이런 문제는 테스트 코드에서만 발생한다. 실제 서버에서 JPA 프로그램들을 실행할 때는 DB세션이 종료되지 않기 때문이다. 하지만 테스트를 진행해야 하므로 다음과 같은 간단한 방법으로 오류를 방지할 수 있다.

@Transactional 어노테이션을 사용하는 것이다. 그렇게 되면 메소드가 종료될 때 까지 DB세션이 유지된다.

 

package com.myspringboot.ssh;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;
import java.util.Optional;

import javax.transaction.Transactional;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest // @SpringBootTest 애너테이션은 SbbApplicationTests 클래스가 스프링부트 테스트 클래스임을 의미
class SshApplicationTests {

	@Autowired // @Autowired 애너테이션은 스프링의 DI 기능으로 questionRepository 객체를 스프링이 자동으로 생성
	private QuestionRepository questionRepository;
	
	@Autowired
    private AnswerRepository answerRepository;
	
	@Transactional
	@Test
	void testJpa() {
		
		Optional<Question> oq = questionRepository.findById(2);
		assertTrue(oq.isPresent());
		Question q = oq.get();
		
		List<Answer> answerList = q.getAnswerList();
		
		assertEquals(1, answerList.size());
		assertEquals("네 자동으로 생성됩니다.", answerList.get(0).getContent());
	} 

}

위와 같이 testJpa 메소드에 @Transactional 어노테이션을 추가해주고 나서 테스트를 실행하면 다음과 같이 성공한 것을 확인할 수 있다.

 

'Backend > Spring Boot' 카테고리의 다른 글

[Spring Boot] 질문 List  (0) 2022.08.11
[Spring Boot] 도메인 나누기  (0) 2022.08.10
[Spring Boot] JPA Repository (2/3)  (0) 2022.08.08
[Spring Boot] JPA Repository (1/3)  (0) 2022.08.05
[Spring Boot] Entity (엔티티)  (0) 2022.08.04

+ Recent posts