Skip to main content

02E. ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ

2023๋…„ 12์›” 27์ผAbout 5 minJavaSpringAWScrashcoursejavajdkjdk8streamspringspringframeworkspringbootawsaws-ec2

02E. ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ ๊ด€๋ จ


2-05. ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ

์ ํ”„ ํˆฌ ์Šคํ”„๋ง๋ถ€ํŠธ - WikiDocs

pahkey/sbb3 - 2-05

์ด๋ฒˆ ์žฅ์—์„œ๋Š” JPA๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•ด ๋ณด์ž.


๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ

์—”ํ‹ฐํ‹ฐ๋งŒ์œผ๋กœ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ฑฐ๋‚˜ ์กฐํšŒ ํ•  ์ˆ˜ ์—†๋‹ค. ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์—ฐ๋™ํ•˜๋Š” JPA ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๋ž€?

๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๋Š” ์—”ํ‹ฐํ‹ฐ์— ์˜ํ•ด ์ƒ์„ฑ๋œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์— ์ ‘๊ทผํ•˜๋Š” ๋ฉ”์„œ๋“œ๋“ค(์˜ˆ: findAll, save ๋“ฑ)์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค. ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ๋Š” ํ…Œ์ด๋ธ”์— ์–ด๋–ค ๊ฐ’์„ ๋„ฃ๊ฑฐ๋‚˜ ๊ฐ’์„ ์กฐํšŒํ•˜๋Š” ๋“ฑ์˜ CRUD(Create, Read, Update, Delete)๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ์ด ๋•Œ ์ด๋Ÿฌํ•œ CRUD๋ฅผ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ ์ •์˜ํ•˜๋Š” ๊ณ„์ธต์ด ๋ฐ”๋กœ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์ด๋‹ค.

๋‹ค์Œ๊ณผ ๊ฐ™์ด QuestionRepository ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์ž.

ํŒŒ์ผ๋ช…: /sbb/src/main/java/com/mysite/sbb/QuestionRepository.java

package com.mysite.sbb;

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๋„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ์„ฑํ•˜์ž.

ํŒŒ์ผ๋ช…: /sbb/src/main/java/com/mysite/sbb/AnswerRepository.java

package com.mysite.sbb;

import org.springframework.data.jpa.repository.JpaRepository;

public interface AnswerRepository extends JpaRepository<Answer, Integer> {

}

์ด์ œ QuestionRepository, AnswerRepository๋ฅผ ์ด์šฉํ•˜์—ฌ question, answer ํ…Œ์ด๋ธ”์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ฑฐ๋‚˜ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.


๋ฐ์ดํ„ฐ ์ €์žฅํ•˜๊ธฐ

์ž‘์„ฑํ•œ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด์„œ JUnit ๊ธฐ๋ฐ˜์˜ ์Šคํ”„๋ง๋ถ€ํŠธ์˜ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•ด ๋ณด์ž.

์œ„ ํŒŒ์ผ์„ ์—ด๊ณ  ๋‹ค์Œ์ฒ˜๋Ÿผ ์ˆ˜์ •ํ•˜์ž.
์œ„ ํŒŒ์ผ์„ ์—ด๊ณ  ๋‹ค์Œ์ฒ˜๋Ÿผ ์ˆ˜์ •ํ•˜์ž.

ํŒŒ์ผ๋ช…: /sbb/src/test/java/com/mysite/sbb/SbbApplicationTests.java

package com.mysite.sbb;

import java.time.LocalDateTime;

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


@SpringBootTest
class SbbApplicationTests {

    @Autowired
    private QuestionRepository questionRepository;

    @Test
    void testJpa() {        
        Question q1 = new Question();
        q1.setSubject("sbb๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”?");
        q1.setContent("sbb์— ๋Œ€ํ•ด์„œ ์•Œ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.");
        q1.setCreateDate(LocalDateTime.now());
        this.questionRepository.save(q1);  // ์ฒซ๋ฒˆ์งธ ์งˆ๋ฌธ ์ €์žฅ

        Question q2 = new Question();
        q2.setSubject("์Šคํ”„๋ง๋ถ€ํŠธ ๋ชจ๋ธ ์งˆ๋ฌธ์ž…๋‹ˆ๋‹ค.");
        q2.setContent("id๋Š” ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜๋‚˜์š”?");
        q2.setCreateDate(LocalDateTime.now());
        this.questionRepository.save(q2);  // ๋‘๋ฒˆ์งธ ์งˆ๋ฌธ ์ €์žฅ
    }
}

@SpringBootTest ์• ๋„ˆํ…Œ์ด์…˜์€ SbbApplicationTests ํด๋ž˜์Šค๊ฐ€ ์Šคํ”„๋ง๋ถ€ํŠธ ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค์ž„์„ ์˜๋ฏธํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  @Autowired ์• ๋„ˆํ…Œ์ด์…˜์€ ์Šคํ”„๋ง์˜ DI ๊ธฐ๋Šฅ์œผ๋กœ questionRepository ๊ฐ์ฒด๋ฅผ ์Šคํ”„๋ง์ด ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ด ์ค€๋‹ค.

DI(Dependency Injection) - ์Šคํ”„๋ง์ด ๊ฐ์ฒด๋ฅผ ๋Œ€์‹  ์ƒ์„ฑํ•˜์—ฌ ์ฃผ์ž…ํ•œ๋‹ค.

@Autowired

๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ์Šคํ”„๋ง์˜ ์• ๋„ˆํ…Œ์ด์…˜์ด๋‹ค. ๊ฐ์ฒด๋ฅผ ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ์‹์—๋Š” @Autowired ์™ธ์— Setter ๋˜๋Š” ์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด ์žˆ๋‹ค. ์ˆœํ™˜์ฐธ์กฐ ๋ฌธ์ œ์™€ ๊ฐ™์€ ์ด์œ ๋กœ @Autowired ๋ณด๋‹ค๋Š” ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•œ ๊ฐ์ฒด ์ฃผ์ž…๋ฐฉ์‹์ด ๊ถŒ์žฅ๋œ๋‹ค. ํ•˜์ง€๋งŒ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์˜ ๊ฒฝ์šฐ์—๋Š” ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•œ ๊ฐ์ฒด์˜ ์ฃผ์ž…์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์‹œ์—๋งŒ @Autowired๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ค์ œ ์ฝ”๋“œ ์ž‘์„ฑ์‹œ์—๋Š” ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•œ ๊ฐ์ฒด ์ฃผ์ž…๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค.

testJpa ๋ฉ”์„œ๋“œ ์œ„์˜ @Test ์• ๋„ˆํ…Œ์ด์…˜์€ testJpa ๋ฉ”์„œ๋“œ๊ฐ€ ํ…Œ์ŠคํŠธ ๋ฉ”์„œ๋“œ์ž„์„ ๋‚˜ํƒ€๋‚ธ๋‹ค. ์œ„ ํด๋ž˜์Šค๋ฅผ JUnit์œผ๋กœ ์‹คํ–‰ํ•˜๋ฉด @Test ์• ๋„ˆํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

JUnit์€ ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์ž‘์„ฑํ•œ ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ์ž๋ฐ”์˜ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค.

testJpa ๋ฉ”์„œ๋“œ์˜ ๋‚ด์šฉ์„ ์ž ์‹œ ์‚ดํŽด๋ณด์ž. testJpa ๋ฉ”์„œ๋“œ๋Š” q1, q2 ๋ผ๋Š” Question ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ  QuestionRepository๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ทธ ๊ฐ’์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๋Š” ์ฝ”๋“œ์ด๋‹ค.

์ด์ œ ์ž‘์„ฑํ•œ  ํด๋ž˜์Šค๋ฅผ ์‹คํ–‰ํ•ด ๋ณด์ž. ๋‹ค์Œ์ฒ˜๋Ÿผ <FontIcon icon="iconfont icon-select"/>๋ฅผ ์„ ํƒํ•˜๋ฉด  ํด๋ž˜์Šค๋ฅผ ์‹คํ–‰ํ• ์ˆ˜ ์žˆ๋‹ค.
์ด์ œ ์ž‘์„ฑํ•œ SbbApplicationTests ํด๋ž˜์Šค๋ฅผ ์‹คํ–‰ํ•ด ๋ณด์ž. ๋‹ค์Œ์ฒ˜๋Ÿผ [Run -> Run As -> JUnit Test]๋ฅผ ์„ ํƒํ•˜๋ฉด SbbApplicationTests ํด๋ž˜์Šค๋ฅผ ์‹คํ–‰ํ• ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋กœ์ปฌ์„œ๋ฒ„๊ฐ€ ์ด๋ฏธ ๊ตฌ๋™์ค‘์ด๋ผ๋ฉด The file is locked: nio:/Users/pahkey/local.mv.db ์™€ ๋น„์Šทํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒƒ์ด๋‹ค. H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ํŒŒ์ผ ๊ธฐ๋ฐ˜์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฏธ ๋กœ์ปฌ์„œ๋ฒ„๊ฐ€ ์ ์œ ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฌํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋กœ์ปฌ ์„œ๋ฒ„๋ฅผ ์ค‘์ง€ํ•ด์•ผ ํ•œ๋‹ค.

๋กœ์ปฌ์„œ๋ฒ„๋ฅผ ์ค‘์ง€ํ•˜๊ณ  ๋‹ค์‹œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ด ๋ณด์ž. ๊ทธ๋Ÿฌ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ JUnit ํ™”๋ฉด์ด ๋‚˜ํƒ€๋‚˜๊ณ  ์˜ค๋ฅ˜์—†์ด ์ž˜ ์‹คํ–‰๋  ๊ฒƒ์ด๋‹ค.

๋‹ค์Œ ํ™”๋ฉด์ฒ˜๋Ÿผ ์ดˆ๋ก์ƒ‰์ด ํ‘œ์‹œ๋˜๋ฉด ์„ฑ๊ณต์ด๊ณ  ๋นจ๊ฐ„์ƒ‰์ด ํ‘œ์‹œ๋˜๋ฉด ์‹คํŒจ์ด๋‹ค.
๋‹ค์Œ ํ™”๋ฉด์ฒ˜๋Ÿผ ์ดˆ๋ก์ƒ‰์ด ํ‘œ์‹œ๋˜๋ฉด ์„ฑ๊ณต์ด๊ณ  ๋นจ๊ฐ„์ƒ‰์ด ํ‘œ์‹œ๋˜๋ฉด ์‹คํŒจ์ด๋‹ค.

์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๊ฐ’์ด ์ž˜ ๋“ค์–ด๊ฐ”๋Š”์ง€ ํ™•์ธํ•ด ๋ณด๊ธฐ ์œ„ํ•ด ๋‹ค์‹œ ๋กœ์ปฌ์„œ๋ฒ„๋ฅผ ์‹œ์ž‘ํ•˜๊ณ  H2 ์ฝ˜์†”์— ์ ‘์†ํ•˜์—ฌ ๋‹ค์Œ ์ฟผ๋ฆฌ๋ฌธ์„ ์‹คํ–‰ํ•ด ๋ณด์ž.

SELECT * FROM QUESTION 
๊ทธ๋Ÿฌ๋ฉด ๋‹ค์Œ์ฒ˜๋Ÿผ ์šฐ๋ฆฌ๊ฐ€ ์ €์žฅํ•œ  ๊ฐ์ฒด์˜ ๊ฐ’์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ ๊ฒƒ์„ ํ™•์ธํ•œ ์ˆ˜ ์žˆ๋‹ค.
๊ทธ๋Ÿฌ๋ฉด ๋‹ค์Œ์ฒ˜๋Ÿผ ์šฐ๋ฆฌ๊ฐ€ ์ €์žฅํ•œ Question ๊ฐ์ฒด์˜ ๊ฐ’์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ ๊ฒƒ์„ ํ™•์ธํ•œ ์ˆ˜ ์žˆ๋‹ค.

id๋Š” Question ์—”ํ‹ฐํ‹ฐ์˜ ๊ธฐ๋ณธ ํ‚ค(Primary Key)์ด๋‹ค. id๋Š” ์•ž์—์„œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์„ค์ •ํ–ˆ๋˜๋Œ€๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์†์„ฑ๊ฐ’์ด ์ž๋™์œผ๋กœ 1์”ฉ ์ฆ๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

Question ์—”ํ‹ฐํ‹ฐ์˜ id๋Š” @GeneratedValue ์„ค์ •์„ ํ–ˆ๋‹ค.


๋ฐ์ดํ„ฐ ์กฐํšŒํ•˜๊ธฐ

์ด๋ฒˆ์—๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด ๋ณด์ž.

findAll

์ž‘์„ฑํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋‹ค์Œ์ฒ˜๋Ÿผ ์ˆ˜์ •ํ•ด ๋ณด์ž.

ํŒŒ์ผ๋ช…: /sbb/src/test/java/com/mysite/sbb/SbbApplicationTests.java

package com.mysite.sbb;

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

import java.util.List;

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


@SpringBootTest
class SbbApplicationTests {

    @Autowired
    private QuestionRepository questionRepository;

    @Test
    void testJpa() {
        List<Question> all = this.questionRepository.findAll();
        assertEquals(2, all.size());

        Question q = all.get(0);
        assertEquals("sbb๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”?", q.getSubject());
    }
}

question ํ…Œ์ด๋ธ”์— ์ €์žฅ๋œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์˜ findAll ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.

findAll์€ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ• ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค.

์šฐ๋ฆฌ๋Š” ์ด 2๊ฑด์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ์˜ ์‚ฌ์ด์ฆˆ๋Š” 2๊ฐ€ ๋˜์–ด์•ผ ํ•œ๋‹ค. ๋ฐ์ดํ„ฐ ์‚ฌ์ด์ฆˆ๊ฐ€ 2์ธ์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด JUnit์˜ assertEquals ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค. assertEquals๋Š” assertEquals(๊ธฐ๋Œ€๊ฐ’, ์‹ค์ œ๊ฐ’)์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•˜๊ณ  ๊ธฐ๋Œ€๊ฐ’๊ณผ ์‹ค์ œ๊ฐ’์ด ๋™์ผํ•œ์ง€๋ฅผ ์กฐ์‚ฌํ•œ๋‹ค. ๋งŒ์•ฝ ๊ธฐ๋Œ€๊ฐ’๊ณผ ์‹ค์ œ๊ฐ’์ด ๋™์ผํ•˜์ง€ ์•Š๋‹ค๋ฉด ํ…Œ์ŠคํŠธ๋Š” ์‹คํŒจ๋กœ ์ฒ˜๋ฆฌ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ๊ฐ€ ์ €์žฅํ•œ ์ฒซ๋ฒˆ์งธ ๋ฐ์ดํ„ฐ์˜ ์ œ๋ชฉ์ด "sbb๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”?"์™€ ์ผ์น˜ํ•˜๋Š”์ง€๋„ ํ…Œ์ŠคํŠธํ–ˆ๋‹ค.

ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด์„œ๋Š” ๋กœ์ปฌ ์„œ๋ฒ„๋ฅผ ์ค‘์ง€ํ•˜๊ณ  ๋‹ค์‹œํ•œ๋ฒˆ [Run -> Run As -> JUnit Test]์„ ์‹คํ–‰ํ•˜๋ฉด ๋œ๋‹ค. ํ…Œ์ŠคํŠธ๋Š” ์ž˜ ํ†ต๊ณผ๋  ๊ฒƒ์ด๋‹ค.

์šฐ๋ฆฌ๋Š” ํŽธ์˜์ƒ testJpa ๋ฉ”์„œ๋“œ ํ•˜๋‚˜๋งŒ์„ ๊ฐ€์ง€๊ณ  JPA์˜ ์—ฌ๋Ÿฌ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•  ๊ฒƒ์ด๋‹ค.

findById

์ด๋ฒˆ์—๋Š” Question ์—”ํ‹ฐํ‹ฐ์˜ Id๊ฐ’์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด ๋ณด์ž. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜์ž.

ํŒŒ์ผ๋ช…: /sbb/src/test/java/com/mysite/sbb/SbbApplicationTests.java

package com.mysite.sbb;

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

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
class SbbApplicationTests {

    @Autowired
    private QuestionRepository questionRepository;

    @Test
    void testJpa() {
        Optional<Question> oq = this.questionRepository.findById(1);
        if(oq.isPresent()) {
            Question q = oq.get();
            assertEquals("sbb๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”?", q.getSubject());
        }
    }
}

id ๊ฐ’์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์˜ findById ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ findById์˜ ๋ฆฌํ„ด ํƒ€์ž…์€ Question์ด ์•„๋‹Œ Optional์ž„์— ์ฃผ์˜ํ•˜์ž. Optional์€ null ์ฒ˜๋ฆฌ๋ฅผ ์œ ์—ฐํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ํด๋ž˜์Šค๋กœ ์œ„์™€ ๊ฐ™์ด isPresent๋กœ null์ด ์•„๋‹Œ์ง€๋ฅผ ํ™•์ธํ•œ ํ›„์— get์œผ๋กœ ์‹ค์ œ Question ๊ฐ์ฒด ๊ฐ’์„ ์–ป์–ด์•ผ ํ•œ๋‹ค.

findBySubject

์ด๋ฒˆ์—๋Š” Question ์—”ํ‹ฐํ‹ฐ์˜ subject ๊ฐ’์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด ๋ณด์ž.

ํ•˜์ง€๋งŒ ์•„์‰ฝ๊ฒŒ๋„ Question ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๋Š” findBySubject์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค. findBySubject ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋‹ค์Œ์ฒ˜๋Ÿผ QuestionRepository ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค.

ํŒŒ์ผ๋ช…: /sbb/src/main/java/com/mysite/sbb/QuestionRepository.java

package com.mysite.sbb;

import org.springframework.data.jpa.repository.JpaRepository;

public interface QuestionRepository extends JpaRepository<Question, Integer> {
    Question findBySubject(String subject);
}

๊ทธ๋Ÿฌ๋ฉด ๋‹ค์Œ์ฒ˜๋Ÿผ ์ œ๋ชฉ์œผ๋กœ ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.

ํŒŒ์ผ๋ช…: /sbb/src/test/java/com/mysite/sbb/SbbApplicationTests.java

package com.mysite.sbb;

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

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


@SpringBootTest
class SbbApplicationTests {

    @Autowired
    private QuestionRepository questionRepository;

    @Test
    void testJpa() {
        Question q = this.questionRepository.findBySubject("sbb๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”?");
        assertEquals(1, q.getId());
    }
}

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด ๋ณด๋ฉด ํ…Œ์ŠคํŠธ๋Š” ์•„์ฃผ ์ž˜ ํ†ต๊ณผ๋œ๋‹ค. ์•„๋งˆ ์—ฌ๋Ÿฌ๋ถ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์งˆ๋ฌธ๊ณผ ํ•จ๊ป˜ ํฐ ํ˜ผ๋ž€์ด ์ฐพ์•„ ์˜ฌ์ง€๋„ ๋ชจ๋ฅธ๋‹ค. "์ธํ„ฐํŽ˜์ด์Šค์— findBySubject ๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์„ ์–ธ๋งŒ ํ•˜๊ณ  ๊ตฌํ˜„์€ ํ•˜์ง€ ์•Š์•˜๋Š”๋ฐ ๋„๋Œ€์ฒด ์–ด๋–ป๊ฒŒ ์‹คํ–‰์ด ๋˜๋Š” ๊ฑฐ์ง€?"

์ด๋Ÿฌํ•œ ๋งˆ๋ฒ•์€ JpaRepository๋ฅผ ์ƒ์†ํ•œ QuestionRepository ๊ฐ์ฒด๊ฐ€ ์ƒ์„ฑ๋ ๋•Œ ๋ฒŒ์–ด์ง„๋‹ค. (DI์— ์˜ํ•ด ์Šคํ”„๋ง์ด ์ž๋™์œผ๋กœ QuestionRepository ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ์ด ๋•Œ ํ”„๋ก์‹œ ํŒจํ„ด์ด ์‚ฌ์šฉ๋œ๋‹ค๊ณ  ํ•œ๋‹ค.) ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ ๊ฐ์ฒด์˜ ๋ฉ”์„œ๋“œ๊ฐ€ ์‹คํ–‰๋ ๋•Œ JPA๊ฐ€ ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ช…์„ ๋ถ„์„ํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค๊ณ  ์‹คํ–‰ํ•œ๋‹ค.

์ฆ‰, ์—ฌ๋Ÿฌ๋ถ„์€ findBy + ์—”ํ‹ฐํ‹ฐ์˜ ์†์„ฑ๋ช…(์˜ˆ:findBySubject)๊ณผ ๊ฐ™์€ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ํ•ด๋‹น ์†์„ฑ์˜ ๊ฐ’์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ• ์ˆ˜ ์žˆ๋‹ค.

findBySubject ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ• ๋•Œ ์‹ค์ œ ์–ด๋–ค ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜๋Š”์ง€ ์‚ดํŽด๋ณด์ž. ์‹คํ–‰๋˜๋Š” ์ฟผ๋ฆฌ๋ฅผ ๋กœ๊ทธ์—์„œ ๋ณด๋ ค๋ฉด application.properties ํŒŒ์ผ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•ด์•ผ ํ•œ๋‹ค.

ํŒŒ์ผ๋ช…: /sbb/src/main/resources/application.properties

# DATABASE
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.url=jdbc:h2:~/local
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# JPA
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.show_sql=true

๊ทธ๋ฆฌ๊ณ  ๋‹ค์‹œํ•œ๋ฒˆ ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด ๋ณด์ž. ๊ทธ๋Ÿฌ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ˜์†”๋กœ๊ทธ์—์„œ ์‹คํ–‰๋œ ์ฟผ๋ฆฌ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์‹คํ–‰๋œ ์ฟผ๋ฆฌ์˜  ์กฐ๊ฑด์— ๊ฐ€ ํฌํ•จ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
์‹คํ–‰๋œ ์ฟผ๋ฆฌ์˜ where ์กฐ๊ฑด์— subject๊ฐ€ ํฌํ•จ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

findBySubjectAndContent

์ด๋ฒˆ์—๋Š” ์ œ๋ชฉ๊ณผ ๋‚ด์šฉ์„ ํ•จ๊ป˜ ์กฐํšŒํ•ด ๋ณด์ž. ๋‘ ๊ฐœ์˜ ์†์„ฑ์„ And ์กฐ๊ฑด์œผ๋กœ ์กฐํšŒํ• ๋•Œ๋Š” ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

ํŒŒ์ผ๋ช…: /sbb/src/main/java/com/mysite/sbb/QuestionRepository.java

package com.mysite.sbb;

import org.springframework.data.jpa.repository.JpaRepository;

public interface QuestionRepository extends JpaRepository<Question, Integer> {
    Question findBySubject(String subject);
    Question findBySubjectAndContent(String subject, String content);
}

๊ทธ๋ฆฌ๊ณ  ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•˜์ž.

ํŒŒ์ผ๋ช…: /sbb/src/test/java/com/mysite/sbb/SbbApplicationTests.java

package com.mysite.sbb;

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

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


@SpringBootTest
class SbbApplicationTests {

    @Autowired
    private QuestionRepository questionRepository;

    @Test
    void testJpa() {
        Question q = this.questionRepository.findBySubjectAndContent(
                "sbb๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”?", "sbb์— ๋Œ€ํ•ด์„œ ์•Œ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.");
        assertEquals(1, q.getId());
    }
}

ํ…Œ์ŠคํŠธ๋Š” ์ž˜ ํ†ต๊ณผ๋  ๊ฒƒ์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์‹ค์ œ ์ฟผ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‹คํ–‰๋˜๋Š” ๊ฒƒ์„ ์ฝ˜์†” ๋กœ๊ทธ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

SELECT
    question0_.id as id1_1_,
    question0_.content as content2_1_,
    question0_.create_date as create_d3_1_,
    question0_.subject as subject4_1_ 
from
    question question0_ 
WHERE
    question0_.subject=? 
    AND question0_.content=?

subject, content ์ปฌ๋Ÿผ์ด AND ์กฐ๊ฑด์œผ๋กœ WHERE๋ฌธ์— ์‚ฌ์šฉ๋˜์—ˆ๋‹ค.

์ด๋ ‡๋“ฏ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์˜ ๋ฉ”์„œ๋“œ๋ช…์€ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ์ฟผ๋ฆฌ๋ฌธ์˜ WHERE ์กฐ๊ฑด์„ ๊ฒฐ์ •ํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” findBySubject, findBySubjectAndContent ๋‘ ๊ฐœ๋งŒ ์•Œ์•„๋ดค์ง€๋งŒ ์ƒ๋‹นํžˆ ๋งŽ์€ ์กฐํ•ฉ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๊ฒƒ๋“ค์— ๋Œ€ํ•ด์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ‘œ๋กœ ์ •๋ฆฌํ•ด ๋ณด์•˜๋‹ค.

ํ•ญ๋ชฉ์˜ˆ์ œ์„ค๋ช…
AndfindBySubjectAndContent(String subject, String content)์—ฌ๋Ÿฌ ์ปฌ๋Ÿผ์„ and ๋กœ ๊ฒ€์ƒ‰
OrfindBySubjectOrContent(String subject, String content)์—ฌ๋Ÿฌ ์ปฌ๋Ÿผ์„ or ๋กœ ๊ฒ€์ƒ‰
BetweenfindByCreateDateBetween(LocalDateTime fromDate, LocalDateTime toDate)์ปฌ๋Ÿผ์„ between์œผ๋กœ ๊ฒ€์ƒ‰
LessThanfindByIdLessThan(Integer id)์ž‘์€ ํ•ญ๋ชฉ ๊ฒ€์ƒ‰
GreaterThanEqualfindByIdGraterThanEqual(Integer id)ํฌ๊ฑฐ๋‚˜ ๊ฐ™์€ ํ•ญ๋ชฉ ๊ฒ€์ƒ‰
LikefindBySubjectLike(String subject)LIKE ๊ฒ€์ƒ‰
InfindBySubjectIn(String[] subjects)์—ฌ๋Ÿฌ ๊ฐ’์ค‘์— ํ•˜๋‚˜์ธ ํ•ญ๋ชฉ ๊ฒ€์ƒ‰
OrderByfindBySubjectOrderByCreateDateAsc(String subject)๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ ์ •๋ ฌํ•˜์—ฌ ์ „๋‹ฌ

๋‹จ, ์‘๋‹ต ๊ฒฐ๊ณผ๊ฐ€ ์—ฌ๋Ÿฌ๊ฑด์ธ ๊ฒฝ์šฐ์—๋Š” ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ์˜ ๋ฆฌํ„ด ํƒ€์ž…์„ Question์ด ์•„๋‹Œ List<Question> ์œผ๋กœ ํ•ด์•ผ ํ•œ๋‹ค.

๋ณด๋‹ค ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ฟผ๋ฆฌ ์ƒ์„ฑ ๊ทœ์น™์— ๋Œ€ํ•œ ๋‹ค์Œ์˜ ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์ž.

findBySubjectLike

์ด๋ฒˆ์—๋Š” ์ œ๋ชฉ์— ํŠน์ • ๋ฌธ์ž์—ด์ด ํฌํ•จ๋˜์–ด ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด ๋ณด์ž. Question ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜์ž.

ํŒŒ์ผ๋ช…: /sbb/src/main/java/com/mysite/sbb/QuestionRepository.java

package com.mysite.sbb;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

public interface QuestionRepository extends JpaRepository<Question, Integer> {
    Question findBySubject(String subject);
    Question findBySubjectAndContent(String subject, String content);
    List<Question> findBySubjectLike(String subject);
}

ํ…Œ์ŠคํŠธ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜์ž.

ํŒŒ์ผ๋ช…: /sbb/src/test/java/com/mysite/sbb/SbbApplicationTests.java

package com.mysite.sbb;

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

import java.util.List;

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


@SpringBootTest
class SbbApplicationTests {

    @Autowired
    private QuestionRepository questionRepository;

    @Test
    void testJpa() {
        List<Question> qList = this.questionRepository.findBySubjectLike("sbb%");
        Question q = qList.get(0);
        assertEquals("sbb๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”?", q.getSubject());
    }
}

ํ…Œ์ŠคํŠธ๋Š” ์ž˜ ํ†ต๊ณผ๋  ๊ฒƒ์ด๋‹ค. Like ๊ฒ€์ƒ‰์„ ์œ„ํ•ด์„œ๋Š” findBySubjectLike ๋ฉ”์„œ๋“œ์˜ ์ž…๋ ฅ ๋ฌธ์ž์—ด๋กœ "sbb%"์™€ ๊ฐ™์ด "%"๋ฅผ ์ ์–ด์ฃผ์–ด์•ผ ํ•œ๋‹ค. % ํ‘œ๊ธฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜๋ฏธ๋ฅผ ๊ฐ–๋Š”๋‹ค.


๋ฐ์ดํ„ฐ ์ˆ˜์ •ํ•˜๊ธฐ

์ด๋ฒˆ์—๋Š” ์งˆ๋ฌธ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋ณด์ž.

ํŒŒ์ผ๋ช…: /sbb/src/test/java/com/mysite/sbb/SbbApplicationTests.java

package com.mysite.sbb;

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
class SbbApplicationTests {

    @Autowired
    private QuestionRepository questionRepository;

    @Test
    void testJpa() {
        Optional<Question> oq = this.questionRepository.findById(1);
        assertTrue(oq.isPresent());
        Question q = oq.get();
        q.setSubject("์ˆ˜์ •๋œ ์ œ๋ชฉ");
        this.questionRepository.save(q);
    }
}

assertTrue(๊ฐ’)์€ ๊ฐ’์ด true์ธ์ง€๋ฅผ ํ…Œ์ŠคํŠธํ•œ๋‹ค.

์งˆ๋ฌธ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•œ ๋‹ค์Œ subject๋ฅผ "์ˆ˜์ •๋œ ์ œ๋ชฉ" ์ด๋ผ๋Š” ๊ฐ’์œผ๋กœ ์ˆ˜์ •ํ–ˆ๋‹ค. ๋ณ€๊ฒฝ๋œ Question ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” this.questionRepository.save(q) ์ฒ˜๋Ÿผ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์˜ save ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•ด ๋ณด๋ฉด ์ฝ˜์†” ๋กœ๊ทธ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ UPDATE ๋ฌธ์ด ์‹คํ–‰๋˜์—ˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

UPDATE
    question 
SET
    content=?,
    create_date=?,
    subject=? 
WHERE
    id=?

๋ฐ์ดํ„ฐ ์‚ญ์ œํ•˜๊ธฐ

์ด์–ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ•˜๋Š” ๊ฒƒ๋„ ์‹ค์Šตํ•ด ๋ณด์ž. ์—ฌ๊ธฐ์„œ๋Š” ์ฒซ ๋ฒˆ์งธ ์งˆ๋ฌธ์„ ์‚ญ์ œํ•ด ๋ณด์ž.

ํŒŒ์ผ๋ช…: /sbb/src/test/java/com/mysite/sbb/SbbApplicationTests.java

package com.mysite.sbb;

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
class SbbApplicationTests {

    @Autowired
    private QuestionRepository questionRepository;

    @Test
    void testJpa() {
        assertEquals(2, this.questionRepository.count());
        Optional<Question> oq = this.questionRepository.findById(1);
        assertTrue(oq.isPresent());
        Question q = oq.get();
        this.questionRepository.delete(q);
        assertEquals(1, this.questionRepository.count());
    }
}

๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์˜ count() ๋ฉ”์„œ๋“œ๋Š” ํ•ด๋‹น ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์˜ ์ด ๋ฐ์ดํ„ฐ๊ฑด์ˆ˜๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.

Question ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ์˜ delete ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œํ–ˆ๋‹ค. ์‚ญ์ œํ•˜๊ธฐ ์ „์—๋Š” ๋ฐ์ดํ„ฐ ๊ฑด์ˆ˜๊ฐ€ 2, ์‚ญ์ œํ•œ ํ›„์—๋Š” ๋ฐ์ดํ„ฐ ๊ฑด์ˆ˜๊ฐ€ 1์ธ์ง€๋ฅผ ํ…Œ์ŠคํŠธํ–ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋Š” ์ž˜ ํ†ต๊ณผ๋  ๊ฒƒ์ด๋‹ค.


๋‹ต๋ณ€ ๋ฐ์ดํ„ฐ ์ƒ์„ฑ ํ›„ ์ €์žฅํ•˜๊ธฐ

์ด๋ฒˆ์—๋Š” ๋‹ต๋ณ€(Answer) ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ €์žฅํ•ด ๋ณด์ž.

ํŒŒ์ผ๋ช…: /sbb/src/test/java/com/mysite/sbb/SbbApplicationTests.java

package com.mysite.sbb;

import static org.junit.jupiter.api.Assertions.assertEquals;
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
class SbbApplicationTests {

    @Autowired
    private QuestionRepository questionRepository;

    @Autowired
    private AnswerRepository answerRepository;

    @Test
    void testJpa() {
        Optional<Question> oq = this.questionRepository.findById(2);
        assertTrue(oq.isPresent());
        Question q = oq.get();

        Answer a = new Answer();
        a.setContent("๋„ค ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.");
        a.setQuestion(q);  // ์–ด๋–ค ์งˆ๋ฌธ์˜ ๋‹ต๋ณ€์ธ์ง€ ์•Œ๊ธฐ์œ„ํ•ด์„œ Question ๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
        a.setCreateDate(LocalDateTime.now());
        this.answerRepository.save(a);
    }
}

๋‹ต๋ณ€ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ๋Š” ๋‹ต๋ณ€ ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ AnswerRepository ๊ฐ์ฒด๋ฅผ @Autowired๋กœ ์ฃผ์ž…ํ–ˆ๋‹ค. ๋‹ต๋ณ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜๋ ค๋ฉด ์งˆ๋ฌธ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ ์šฐ์„  ์งˆ๋ฌธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ตฌํ•ด์•ผ ํ•œ๋‹ค. id๊ฐ€ 2์ธ ์งˆ๋ฌธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ ๋‹ค์Œ Answer ์—”ํ‹ฐํ‹ฐ์˜ question ์†์„ฑ์— ๋ฐฉ๊ธˆ ๊ฐ€์ ธ์˜จ ์งˆ๋ฌธ ๋ฐ์ดํ„ฐ๋ฅผ ๋Œ€์ž…ํ•ด(a.setQuestion(q)) ๋‹ต๋ณ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ–ˆ๋‹ค. Answer ์—”ํ‹ฐํ‹ฐ์—๋Š” ์–ด๋–ค ์งˆ๋ฌธ์— ํ•ด๋‹นํ•˜๋Š” ๋‹ต๋ณ€์ธ์ง€ ์—ฐ๊ฒฐํ•  ๋ชฉ์ ์œผ๋กœ question ์†์„ฑ์ด ํ•„์š”ํ•˜๋‹ค.

ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•ด ๋ณด์ž. ๋‹ต๋ณ€ ๋ฐ์ดํ„ฐ๊ฐ€ ์ž˜ ์ƒ์„ฑ๋  ๊ฒƒ์ด๋‹ค.


๋‹ต๋ณ€ ์กฐํšŒํ•˜๊ธฐ

Answer๋„ Question ์—”ํ‹ฐํ‹ฐ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ id ์†์„ฑ์ด ๊ธฐ๋ณธ ํ‚ค์ด๋ฏ€๋กœ ๊ฐ’์ด ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค. ๋‹ค์Œ์ฒ˜๋Ÿผ id ๊ฐ’์„ ์ด์šฉํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด ๋ณด์ž.

ํŒŒ์ผ๋ช…: /sbb/src/test/java/com/mysite/sbb/SbbApplicationTests.java

package com.mysite.sbb;

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
class SbbApplicationTests {

    @Autowired
    private QuestionRepository questionRepository;

    @Autowired
    private AnswerRepository answerRepository;

    @Test
    void testJpa() {
        Optional<Answer> oa = this.answerRepository.findById(1);
        assertTrue(oa.isPresent());
        Answer a = oa.get();
        assertEquals(2, a.getQuestion().getId());
    }
}

id ๊ฐ’์ด 1์ธ ๋‹ต๋ณ€์„ ์กฐํšŒํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋‹ต๋ณ€์˜ ์งˆ๋ฌธ id๊ฐ€ 2์ธ์ง€๋„ ํ…Œ์ŠคํŠธํ•ด ๋ณด์•˜๋‹ค.


๋‹ต๋ณ€์— ์—ฐ๊ฒฐ๋œ ์งˆ๋ฌธ ์ฐพ๊ธฐ vs ์งˆ๋ฌธ์— ๋‹ฌ๋ฆฐ ๋‹ต๋ณ€ ์ฐพ๊ธฐ

์•ž์—์„œ ๊ตฌ์„ฑํ•œ Answer ์—”ํ‹ฐํ‹ฐ์˜ question ์†์„ฑ์„ ์ด์šฉํ•˜๋ฉด "๋‹ต๋ณ€์— ์—ฐ๊ฒฐ๋œ ์งˆ๋ฌธ"์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.

a.getQuestion()

๋‹ต๋ณ€์— ์—ฐ๊ฒฐ๋œ ์งˆ๋ฌธ ์ฐพ๊ธฐ๋Š” Answer ์—”ํ‹ฐํ‹ฐ์— question ์†์„ฑ์ด ์ •์˜๋˜์–ด ์žˆ์–ด์„œ ๋งค์šฐ ์‰ฝ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฐ˜๋Œ€์˜ ๊ฒฝ์šฐ๋„ ๊ฐ€๋Šฅํ• ๊นŒ? ์ฆ‰, ์งˆ๋ฌธ์—์„œ ๋‹ต๋ณ€์„ ์ฐพ์„์ˆ˜ ์žˆ์„๊นŒ?

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์งˆ๋ฌธ ์—”ํ‹ฐํ‹ฐ์— ์ •์˜ํ•œ answerList๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—ญ์‹œ ์‰ฝ๊ฒŒ ๊ตฌํ• ์ˆ˜ ์žˆ๋‹ค.

ํŒŒ์ผ๋ช…: /sbb/src/test/java/com/mysite/sbb/SbbApplicationTests.java

package com.mysite.sbb;

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
class SbbApplicationTests {

    @Autowired
    private QuestionRepository questionRepository;

    @Test
    void testJpa() {
        Optional<Question> oq = this.questionRepository.findById(2);
        assertTrue(oq.isPresent());
        Question q = oq.get();

        List<Answer> answerList = q.getAnswerList();

        assertEquals(1, answerList.size());
        assertEquals("๋„ค ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.", answerList.get(0).getContent());
    }
}

์งˆ๋ฌธ ๊ฐ์ฒด๋กœ๋ถ€ํ„ฐ ๋‹ต๋ณ€ ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ตฌํ•˜๋Š” ํ…Œ์ŠคํŠธ์ฝ”๋“œ์ด๋‹ค. id๊ฐ€ 2์ธ ์งˆ๋ฌธ์— ๋‹ต๋ณ€์„ ํ•œ ๊ฐœ ๋“ฑ๋กํ–ˆ์œผ๋ฏ€๋กœ ์œ„์™€ ๊ฐ™์ด ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.mysite.sbb.Question.answerList, could not initialize proxy - no Session
(... ์ƒ๋žต ...)

์™œ๋ƒํ•˜๋ฉด Question ๋ฆฌํฌ์ง€ํ„ฐ๋ฆฌ๊ฐ€ findById๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ Question ๊ฐ์ฒด๋ฅผ ์กฐํšŒํ•˜๊ณ  ๋‚˜๋ฉด DB์„ธ์…˜์ด ๋Š์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๊ทธ ์ดํ›„์— ์‹คํ–‰๋˜๋Š” q.getAnswerList() ๋ฉ”์„œ๋“œ๋Š” ์„ธ์…˜์ด ์ข…๋ฃŒ๋˜์–ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๋‹ต๋ณ€ ๋ฐ์ดํ„ฐ ๋ฆฌ์ŠคํŠธ๋Š” q ๊ฐ์ฒด๋ฅผ ์กฐํšŒํ• ๋•Œ ๊ฐ€์ ธ์˜ค์ง€ ์•Š๊ณ  q.getAnswerList() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์‹œ์ ์— ๊ฐ€์ ธ์˜ค๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ด๋ ‡๊ฒŒ ํ•„์š”ํ•œ ์‹œ์ ์— ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹์„ Lazy ๋ฐฉ์‹์ด๋ผ๊ณ  ํ•œ๋‹ค. ์ด์™€ ๋ฐ˜๋Œ€๋กœ q ๊ฐ์ฒด๋ฅผ ์กฐํšŒํ• ๋•Œ ๋‹ต๋ณ€ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ชจ๋‘ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹์€ Eager ๋ฐฉ์‹์ด๋ผ๊ณ  ํ•œ๋‹ค. @OneToMany, @ManyToOne ์• ๋„ˆํ…Œ์ด์…˜์˜ ์˜ต์…˜์œผ๋กœ fetch=FetchType.LAZY ๋˜๋Š” fetch=FetchType.EAGER ์ฒ˜๋Ÿผ ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ์‹์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด ์ฑ…์—์„œ๋Š” ๋”ฐ๋กœ ์ง€์ •ํ•˜์ง€ ์•Š๊ณ  ํ•ญ์ƒ ๋””ํดํŠธ ๊ฐ’์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค.

์‚ฌ์‹ค ์ด ๋ฌธ์ œ๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ๋งŒ ๋ฐœ์ƒํ•œ๋‹ค. ์‹ค์ œ ์„œ๋ฒ„์—์„œ JPA ํ”„๋กœ๊ทธ๋žจ๋“ค์„ ์‹คํ–‰ํ•  ๋•Œ๋Š” DB ์„ธ์…˜์ด ์ข…๋ฃŒ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์œ„์™€ ๊ฐ™์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ˆ˜ํ–‰ํ•  ๋•Œ ์œ„์™€ ๊ฐ™์€ ์˜ค๋ฅ˜๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ์ฒ˜๋Ÿผ @Transactional ์• ๋„ˆํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค. @Transactional ์• ๋„ˆํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ”์„œ๋“œ๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ DB ์„ธ์…˜์ด ์œ ์ง€๋œ๋‹ค.

package com.mysite.sbb;

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;
import org.springframework.transaction.annotation.Transactional;


@SpringBootTest
class SbbApplicationTests {

    @Autowired
    private QuestionRepository questionRepository;

    @Transactional
    @Test
    void testJpa() {
        Optional<Question> oq = this.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 ์• ๋„ˆํ…Œ์ด์…˜์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์˜ค๋ฅ˜์—†์ด ์ž˜ ์ˆ˜ํ–‰๋  ๊ฒƒ์ด๋‹ค.