스프링 부트, 입문!

스프링 부트, 입문!

쉽고 빠르게 배우는, 스프링 부트 첫걸음!

22 댓글 엔티티와 리파지터리(feat. 데이터의 관계)

# 댓글 엔티티와 리파지터리(feat. 데이터의 관계) ## 미션 댓글(Comment)을 위한 엔티티와 리파지터리를 만들고, 이를 테스트하시오. ![홍팍-스프링-부트-입문--미션](http://drive.google.com/uc?export=view&id=1eByGImTr7WQofNTgi-Egztbk7gvn7_eb) ## 02:46 댓글 엔티티 구현 - @ManyToOne, @JoinColumn #### ../entity/Comment ``` package com.example.firstproject.entity; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.ToString; import javax.persistence.*; @Entity @Getter @ToString @AllArgsConstructor @NoArgsConstructor public class Comment { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne @JoinColumn(name = "article_id") private Article article; @Column private String nickname; @Column private String body; } ``` ## 07:08 더미 데이터 추가 #### ../resources/data.sql ``` -- 15강: article 더미 데이터 -- 17강: article 더미 데이터에서 id 정보를 제거 INSERT INTO article(title, content) VALUES('가가가가', '1111'); INSERT INTO article(title, content) VALUES('나나나나', '2222'); INSERT INTO article(title, content) VALUES('다다다다', '3333'); -- 22강: article 더미 데이터 INSERT INTO article(title, content) VALUES('당신의 인생 영화는?', '댓글 ㄱ'); INSERT INTO article(title, content) VALUES('당신의 소울 푸드는?', '댓글 ㄱㄱ'); INSERT INTO article(title, content) VALUES('당신의 취미는?', '댓글 ㄱㄱㄱ'); -- 22강: comment 더미 데이터 ---- 4번 게시글의 댓글들 INSERT INTO comment(article_id, nickname, body) VALUES(4, 'Park', '굳 윌 헌팅'); INSERT INTO comment(article_id, nickname, body) VALUES(4, 'Kim', '아이 엠 샘'); INSERT INTO comment(article_id, nickname, body) VALUES(4, 'Choi', '쇼생크의 탈출'); ---- 5번 게시글의 댓글들 INSERT INTO comment(article_id, nickname, body) VALUES(5, 'Park', '치킨'); INSERT INTO comment(article_id, nickname, body) VALUES(5, 'Kim', '샤브샤브'); INSERT INTO comment(article_id, nickname, body) VALUES(5, 'Choi', '초밥'); ---- 6번 게시글의 댓글들 INSERT INTO comment(article_id, nickname, body) VALUES(6, 'Park', '조깅'); INSERT INTO comment(article_id, nickname, body) VALUES(6, 'Kim', '유튜브'); INSERT INTO comment(article_id, nickname, body) VALUES(6, 'Choi', '독서'); ``` ## 12:09 SQL 연습 #### h2 콘솔에서 SQL 연습 ``` -- 4번 게시글의 모든 댓글 SELECT * FROM comment WHERE article_id = 4 ; -- 닉네임이 Park인 모든 댓글 SELECT * FROM comment WHERE nickname = 'Park' ; ``` ## 13:41 댓글 리파지터리 구현 - @Query #### ../repository/CommentRepository ``` package com.example.firstproject.repository; import com.example.firstproject.entity.Comment; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import java.util.List; public interface CommentRepository extends JpaRepository<Comment, Long> { @Query(value = "SELECT * " + "FROM comment " + "WHERE article_id = :articleId", nativeQuery = true) List<Comment> findByArticleId(Long articleId); List<Comment> findByNickname(String nickname); } ``` #### ⚠️ articleId를 찾지 못해 에러 발생 시, @Param 어노테이션으로 파라미터 정보 추가 ``` @Query(value = "SELECT * " + "FROM comment " + "WHERE article_id = :articleId", nativeQuery = true) List<Comment> findByArticleId(@Param("articleId") Long articleId); ``` ## 17:28 네이티브 쿼리 - named-native-query, orm.xml #### ../resources/META-INF/orm.xml ``` <?xml version="1.0" encoding="utf-8" ?> <entity-mappings xmlns="https://jakarta.ee/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence/orm https://jakarta.ee/xml/ns/persistence/orm/orm_3_0.xsd" version="3.0"> <named-native-query name="Comment.findByNickname" result-class="com.example.firstproject.entity.Comment"> <query> <![CDATA[ SELECT * FROM comment WHERE nickname = :nickname ]]> </query> </named-native-query> </entity-mappings> ``` ## 20:24 리파지터리 테스트 #### ../test/.../repository/CommentRepositoryTest ``` package com.example.firstproject.repository; import com.example.firstproject.entity.Article; import com.example.firstproject.entity.Comment; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @DataJpaTest class CommentRepositoryTest { @Autowired CommentRepository commentRepository; @Test void findByArticleId() { } @Test void findByNickname() { } } ``` #### ../test/.../repository/CommentRepositoryTest ``` package com.example.firstproject.repository; import com.example.firstproject.entity.Article; import com.example.firstproject.entity.Comment; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @DataJpaTest class CommentRepositoryTest { @Autowired CommentRepository commentRepository; @Test @DisplayName("특정 게스글의 모든 댓글 조회") void findByArticleId() { /* Case 1: 4번 게시글의 모든 댓글 조회 */ { // 준비 Long articleId = 4L; // 수행 List<Comment> comments = commentRepository.findByArticleId(articleId); // 예상 Article article = new Article(4L, "당신의 인생 영화는?", "댓글 ㄱ"); Comment a = new Comment(1L, article, "Park", "굳 윌 헌팅"); Comment b = new Comment(2L, article, "Kim", "아이 엠 샘"); Comment c = new Comment(3L, article, "Choi", "쇼생크의 탈출"); List<Comment> expected = Arrays.asList(a, b, c); // 검증 assertEquals(expected.toString(), comments.toString(), "4번 글의 모든 댓글을 출력!"); } /* Case 2: 1번 게시글의 모든 댓글 조회 */ { // 준비 Long articleId = 1L; // 수행 List<Comment> comments = commentRepository.findByArticleId(articleId); // 예상 Article article = new Article(1L, "가가가가", "1111"); List<Comment> expected = Arrays.asList(); // 검증 assertEquals(expected.toString(), comments.toString(), "1번 글은 댓글이 없음"); } } @Test @DisplayName("특정 닉네임의 모든 댓글 조회") void findByNickname() { /* Case 1: "Park"의 모든 댓글 조회 */ { // 준비 String nickname = "Park"; // 수행 List<Comment> comments = commentRepository.findByNickname(nickname); // 예상 Comment a = new Comment(1L, new Article(4L, "당신의 인생 영화는?", "댓글 ㄱ"), nickname, "굳 윌 헌팅"); Comment b = new Comment(4L, new Article(5L, "당신의 소울 푸드는?", "댓글 ㄱㄱ"), nickname, "치킨"); Comment c = new Comment(7L, new Article(6L, "당신의 취미는?", "댓글 ㄱㄱㄱ"), nickname, "조깅"); List<Comment> expected = Arrays.asList(a, b, c); // 검증 assertEquals(expected.toString(), comments.toString(), "Park의 모든 댓글을 출력!"); } } } ``` ## 🔥 구글링 훈련하기 - 일대다 다대일 관계 - @ManyToOne - @JoinColumn - JpaRepository - @Query - JPA native query orm xml - @DataJpaTest @SpringBootTest 비교