👉 Mission. 댓글(Comment) 엔티티와 리파지터리를 만들고, 이를 테스트 하시오
1. 댓글 Entity 생성
파일명 : entity/Comment.java
@Entity
@Getter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Comment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne //해당 댓글 엔티티 여러개가, 하나의 Article에 연관된다 -> 관계 설정
@JoinColumn(name = "article_id") //"article_id" 컬럼에 Article의 대표값을 저장
private Article article;
@Column
private String nickname;
@Column
private String body;
}
➕ JPA에서 Entity를 매핑할 떄 다음과 같은 2가지를 관습적으로 사용한다.
1. @JoinColumn을 사용하는 FK 필드의 이름을 대상 Entity(대상 Table)의 이름으로 설정private Article article;
- @JoinColumn을 사용할 때 name 속성으로 대상 Entity(대상 Table)의 이름 + PK로 하드하게 지정
@ManytoOne @JoinColumn(name = "article_id") private Article article;
2. 댓글 더미 데이터 생성 (data.sql)
-- article 더미 데이터
INSERT INTO article(id, title, content) VALUES (4, '당신의 인생 영화는?', '댓글 ㄱ');
INSERT INTO article(id, title, content) VALUES (5, '당신의 소울 푸드는?', '댓글 ㄱㄱ');
INSERT INTO article(id, title, content) VALUES (6, '당신의 취미는?', '댓글 ㄱㄱㄱ');
-- comment 더미 데이터
---- 4번 게시글의 댓글들
INSERT INTO comment(id, article_id, nickname, body) VALUES (1, 4, 'Park', '굳 윌 헌팅');
INSERT INTO comment(id, article_id, nickname, body) VALUES (2, 4, 'Kim', '아이 엠 샘');
INSERT INTO comment(id, article_id, nickname, body) VALUES (3, 4, 'Choi', '쇼생크의 탈출');
---- 5번 게시글의 댓글들
INSERT INTO comment(id, article_id, nickname, body) VALUES (4, 5, 'Park', '치킨');
INSERT INTO comment(id, article_id, nickname, body) VALUES (5, 5, 'Kim', '샤브샤브');
INSERT INTO comment(id, article_id, nickname, body) VALUES (6, 5, 'Choi', '초밥');
---- 6번 게시글의 댓글들
INSERT INTO comment(id, article_id, nickname, body) VALUES (7, 6, 'Park', '조깅');
INSERT INTO comment(id, article_id, nickname, body) VALUES (8, 6, 'Kim', '유튜브');
INSERT INTO comment(id, article_id, nickname, body) VALUES (9, 6, 'Choi', '독서');
--
3. 댓글 Repository 구현
<네이티브 쿼리 사용방법 2가지>
@Query
어노테이션 사용=> 둘 사이에 정답은 없고, 보통 쿼리가 복잡하거나 최적화가 필요하면 xml, 쿼리가 단순하면 어노테이션을 사용하는 편
파일명 : repository/CommentRepository.java
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);
}
파일명 : 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.studyproject.entity.Comment">
<query>
<![CDATA[
SELECT
*
FROM
comment
WHERE
nickname = :nickname
]]>
</query>
</named-native-query>
</entity-mappings>
<named-native-query
부분 아래, name="Comment.findByNickname"
: Comment Repository에 있는 findByNickname 이라는 메서드는 <query>
안에 있는 쿼리 내용을 수행하도록 하겠다는 의미.result-class="com.example.studyproject.entity.Comment">
: 그리고 이 쿼리가 반환할 값의 타입은 Comment 엔티티라는 의미.닉네임에 "i" 가 들어간 유저가 작성한 모든 댓글을 조회하는 테스트 케이스를 작성해보자.
@Test
@DisplayName("특정 닉네임의 모든 댓글을 조회")
void findByNickname() {
/* Case 5 : "i"가 포함된 닉네임의 모든 댓글 조회 */
{
// 입력 데이터를 준비
String nickname = "i";
Article article4 = new Article(4L, "당신의 인생 영화는?", "댓글 ㄱ");
Article article5 = new Article(5L, "당신의 소울 푸드는?", "댓글 ㄱㄱ");
Article article6 = new Article(6L, "당신의 취미는?", "댓글 ㄱㄱㄱ");
Comment a = new Comment(2L, article4, "Kim", "아이 엠 샘");
Comment b = new Comment(5L, article5, "Kim", "샤브샤브");
Comment c = new Comment(8L, article6, "Kim", "유튜브");
Comment d = new Comment(3L, article4, "Choi", "쇼생크의 탈출");
Comment e = new Comment(6L, article5, "Choi", "초밥");
Comment f = new Comment(9L, article6, "Choi", "독서");
// 실제 수행
List<Comment> comments = commentRepository.findByNickname(nickname);
// 예상하기
List<Comment> expected = Arrays.asList(a, d, b, e, c, f);
// 검증
assertEquals(expected.toString(), comments.toString(), "닉네임에 i를 포함한 닉네임의 모든 댓글을 출력");
}
}
이렇게 테스트 케이스는 잘 만들었는데 동작은 잘 할까?
아니. 지금 상태의 코드로는 빈 리스트가 출력되어 '실패'하게 된다.
'i가 포함된'이 아니라 'i'인 닉네임의 댓글을 조회하기 때문이다.
그러니까 지금부터 nickname에 입력된 변수가 '포함된' 값을 찾을 수 있게 본 코드를 고쳐보자.
SQL에서 문자열의 부분 일치를 조회할 때는 'LIKE' 연산자와 '%', '_' 기호를 활용한다.
SELECT * FROM comment WHERE nickname LIKE '%i%'
comment 테이블의 nickname 컬럼에서 i 를 포함하는 값을 확인한다.
SELECT * FROM comment WHERE nickname LIKE '_i_'
comment 테이블의 nickname 컬럼에서 i 앞뒤로 각각 1개의 문자가 있는 문자열인 값을 확인한다.
그럼 query 문에 LIKE '%:nickname%'
를 추가하면 해결이 될까해서 추가해봤으나 테스트케이스는 여전히 실패였다.
왜 그럴까? SQL문에는 문자열을 연결해주는 concat 함수가 있다.
concat 함수는 기본적으로 사용자가 지정한 것(문자열, 컬럼)등의 글자를 합쳐주거나 일괄적으로 글자를 추가하려고 할 때 사용한다.
<query>
<![CDATA[
SELECT
*
FROM
comment
WHERE
nickname
LIKE
concat('%', :nickname, '%')
]]>
</query>
즉 위와 같이 코드를 작성해주면 nickname 변수에 들어온 값을 포함한 닉네임을 DB에서 찾아낼 수 있게 된다!
강의 출처 : https://www.youtube.com/watch?v=_vDACE13Ubc&list=PLyebPLlVYXCiYdYaWRKgCqvnCFrLEANXt&index=1 [스프링 부트 입문 - 홍팍]