03N. κ²μ
03N. κ²μ κ΄λ ¨
μ΄λ²μλ SBBμ κ²μκΈ°λ₯μ μΆκ°ν΄ 보μ.
μ°Έκ³ λ‘ κ²μμ μ΄ μ± μμ λ€λ£¨λ κ°μ₯ μ΄λ €μ΄ λΆλΆμ΄λ€. μ°¨λΆν λ§μμΌλ‘ λ°λΌμ€κΈ° λ°λλ€.
κ²μ κΈ°λ₯
SBBλ μ§λ¬Έκ³Ό λ΅λ³μ λν λ°μ΄ν°κ° κ³μ μμ¬κ°λ κ²μνμ΄λ―λ‘ κ²μκΈ°λ₯μ νμλΌκ³ ν μ μλ€. κ²μμ λμμ μ§λ¬Έμ μ λͺ©, μ§λ¬Έμ λ΄μ©, μ§λ¬Έ μμ±μ, λ΅λ³μ λ΄μ©, λ΅λ³ μμ±μ μ λλ‘ νλ©΄ μ λΉν κ²μ΄λ€. μ¦, "μ€νλ§"μ΄λΌκ³ κ²μμ νλ©΄ "μ€νλ§" μ΄λΌλ λ¬Έμμ΄μ΄ μ λͺ©, λ΄μ©, μ§λ¬Έ μμ±μ, λ΅λ³, λ΅λ³ μμ±μμ μ‘΄μ¬νλμ§ μ°Ύμλ³΄κ³ κ·Έ κ²°κ³Όλ₯Ό νλ©΄μ 보μ¬μ£Όμ΄μΌ νλ€.
μ΄λ° 쑰건μΌλ‘ κ²μνλ €λ©΄ λ€μκ³Ό κ°μ SQL μΏΌλ¦¬κ° μ€νλμ΄μΌ νλ€.
SELECT
DISTINCT q.id
, q.author_id
, q.content
, q.create_date
, q.modify_date
, q.subject
FROM
question q
LEFT OUTER JOIN site_user u1 ON q.author_id=u1.id
LEFT OUTER JOIN answer a ON q.id=a.question_id
LEFT OUTER JOIN site_user u2 ON a.author_id=u2.id
WHERE 1=1
AND q.subject LIKE '%μ€νλ§%'
OR q.content LIKE '%μ€νλ§%'
OR u1.username LIKE '%μ€νλ§%'
OR a.content LIKE '%μ€νλ§%'
OR u2.username LIKE '%μ€νλ§%'
쿼리μ μ΅μνμ§ μλ€λ©΄ μ 쿼리λ₯Ό μ΄ν΄νκΈ° νλ€μλ μλ€. μ μ μμ 쿼리μ λν΄μ μμ보μ.
μ 쿼리λ "μ€νλ§" μ΄λΌλ λ¬Έμμ΄μ΄ ν¬ν¨λ λ°μ΄ν°λ₯Ό question
, answer
, site_user
ν
μ΄λΈμ λμμΌλ‘ κ²μνλ 쿼리μ΄λ€. κ·Έλ¦¬κ³ question
ν
μ΄λΈμ κΈ°μ€μΌλ‘ answer
, site_user
ν
μ΄λΈμ μμ°ν° μ‘°μΈνμ¬ "μ€νλ§" μ΄λΌλ λ¬Έμμ΄μ κ²μνλ€. μμ°ν°(OUTER
) μ‘°μΈ λμ μ΄λ(INNER
) μ‘°μΈμ μ¬μ©νλ©΄ ν©μ§ν©μ΄ μλ κ΅μ§ν©μΌλ‘ κ²μλμ΄ κ²°κ³Όκ° λλ½λ μ μμ΄ μ£Όμν΄μΌ νλ€. κ·Έλ¦¬κ³ μ΄ 3κ°μ ν
μ΄λΈμ λμμΌλ‘ μμ°ν° μ‘°μΈνμ¬ κ²μνλ©΄ μ€λ³΅λ κ²°κ³Όκ° λμ¬μ μκΈ° λλ¬Έμ SELECT
λ¬Έμ DISTINCT
λ₯Ό μ£Όμ΄ μ€λ³΅μ μ κ±°νλ€.
JPAλ₯Ό μ¬μ©νλ©΄ μμ 쿼리λ₯Ό μλ°μ½λλ‘ λ§λ€μ μλ€. λ€μμ λ°λΌν΄ 보μ.
Specification
μμ 쿼리μμ λ³Έ κ²κ³Ό κ°μ΄ μ¬λ¬ ν μ΄λΈμμ λ°μ΄ν°λ₯Ό κ²μν΄μΌ ν κ²½μ°μλ JPAκ° μ 곡νλ Specification μΈν°νμ΄μ€λ₯Ό μ¬μ©νλ κ²μ΄ νΈλ¦¬νλ€. Specificationμ μ΄λ»κ² μ¬μ©ν μ μλμ§ μμ λ₯Ό ν΅ν΄μ μμ보μ.
Specification
Specificationμ λ³΄λ€ μ κ΅ν 쿼리μ μμ±μ λμμ£Όλ JPAμ λꡬμ΄λ€. λ³΄λ€ μμΈν λ΄μ©μ λ€μμ λ¬Έμλ₯Ό μ°Έκ³ ν΄ λ³΄μ.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#specifications
λ€μκ³Ό κ°μ΄ QuestionService
μ search
λ©μλλ₯Ό μΆκ°ν΄ 보μ.
νμΌλͺ :
/sbb/src/main/java/com/mysite/sbb/question/
QuestionService.java
// (... μλ΅ ...)
import com.mysite.sbb.answer.Answer;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.springframework.data.jpa.domain.Specification;
// (... μλ΅ ...)
public class QuestionService {
private final QuestionRepository questionRepository;
private Specification<Question> search(String kw) {
return new Specification<>() {
private static final long serialVersionUID = 1L;
@Override
public Predicate toPredicate(Root<Question> q, CriteriaQuery<?> query, CriteriaBuilder cb) {
query.distinct(true); // μ€λ³΅μ μ κ±°
Join<Question, SiteUser> u1 = q.join("author", JoinType.LEFT);
Join<Question, Answer> a = q.join("answerList", JoinType.LEFT);
Join<Answer, SiteUser> u2 = a.join("author", JoinType.LEFT);
return cb.or(cb.like(q.get("subject"), "%" + kw + "%"), // μ λͺ©
cb.like(q.get("content"), "%" + kw + "%"), // λ΄μ©
cb.like(u1.get("username"), "%" + kw + "%"), // μ§λ¬Έ μμ±μ
cb.like(a.get("content"), "%" + kw + "%"), // λ΅λ³ λ΄μ©
cb.like(u2.get("username"), "%" + kw + "%")); // λ΅λ³ μμ±μ
}
};
}
// (... μλ΅ ...)
}
μΆκ°ν search
λ©μλλ κ²μμ΄(kw
)λ₯Ό μ
λ ₯λ°μ 쿼리μ μ‘°μΈλ¬Έκ³Ό WHERE
λ¬Έμ μμ±νμ¬ λ¦¬ν΄νλ λ©μλμ΄λ€. μ½λλ₯Ό μμΈν 보면 μμμ μμλ³Έ 쿼리λ₯Ό μλ° μ½λλ‘ κ·Έλλ‘ μ¬νν κ²μμ μμ μλ€.
μ μ½λμμ μ¬μ©ν λ³μλ€μ λν΄μ μμΈν μ΄ν΄λ³΄μ.
q
: Root, μ¦ κΈ°μ€μ μλ―Ένλ Question μν°ν°μ κ°μ²΄ (μ§λ¬Έ μ λͺ©κ³Ό λ΄μ©μ κ²μνκΈ° μν΄ νμ)u1
: Question μν°ν°μSiteUser
μν°ν°λ₯Ό μμ°ν° μ‘°μΈ(JoinType.LEFT
)νμ¬ λ§λSiteUser
μν°ν°μ κ°μ²΄.Question
μν°ν°μSiteUser
μν°ν°λ author μμ±μΌλ‘ μ°κ²°λμ΄ μκΈ° λλ¬Έμq.join("author")
μ κ°μ΄ μ‘°μΈν΄μΌ νλ€. (μ§λ¬Έ μμ±μλ₯Ό κ²μνκΈ° μν΄ νμ)a
- Question μν°ν°μ Answer μν°ν°λ₯Ό μμ°ν° μ‘°μΈνμ¬ λ§λ Answer μν°ν°μ κ°μ²΄. Question μν°ν°μ Answer μν°ν°λanswerList
μμ±μΌλ‘ μ°κ²°λμ΄ μκΈ° λλ¬Έμq.join("answerList")
μ κ°μ΄ μ‘°μΈν΄μΌ νλ€. (λ΅λ³ λ΄μ©μ κ²μνκΈ° μν΄ νμ)u2
- λ°λ‘ μμμ μμ±νa
κ°μ²΄μ λ€μ νλ²SiteUser
μν°ν°μ μμ°ν° μ‘°μΈνμ¬ λ§λSiteUser
μν°ν°μ κ°μ²΄ (λ΅λ³ μμ±μλ₯Ό κ²μνκΈ° μν΄μ νμ) κ·Έλ¦¬κ³ κ²μμ΄(kw
)κ° ν¬ν¨λμ΄ μλμ§λ₯Ό likeλ‘ κ²μνκΈ° μν΄ μ λͺ©, λ΄μ©, μ§λ¬Έ μμ±μ, λ΅λ³ λ΄μ©, λ΅λ³ μμ±μ κ°κ°μcb.likeλ₯Ό
μ¬μ©νκ³ μ΅μ’ μ μΌλ‘cb.or
λ‘OR
κ²μλκ² νμλ€. μμμ μμλ‘ λ 쿼리μ λΉκ΅ν΄ 보면 μ½λκ° μ΄λ»κ² ꡬμ±λμλμ§ μ½κ² μ΄ν΄λ κ²μ΄λ€.
QuestionRepository
κ·Έλ¦¬κ³ μμμ μμ±ν Specification
μ μ¬μ©νκΈ° μν΄μ QuestionRepository
λ₯Ό λ€μκ³Ό κ°μ΄ μμ νμ.
νμΌλͺ :
/sbb/src/main/java/com/mysite/sbb/question/
QuestionRepository.java
package com.mysite.sbb.question;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
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);
Page<Question> findAll(Pageable pageable);
Page<Question> findAll(Specification<Question> spec, Pageable pageable);
}
Specificationκ³Ό Pageable
κ°μ²΄λ₯Ό μ
λ ₯μΌλ‘ Question
μν°ν°λ₯Ό μ‘°ννλ findAll
λ©μλλ₯Ό μ μΈνλ€.
QuestionService
κ·Έλ¦¬κ³ QuestionService
μ getList
λ©μλλ₯Ό λ€μκ³Ό κ°μ΄ μμ νμ.
νμΌλͺ :
/sbb/src/main/java/com/mysite/sbb/question/
QuestionService.java
// (... μλ΅ ...)
public class QuestionService {
// (... μλ΅ ...)
public Page<Question> getList(int page, String kw) {
List<Sort.Order> sorts = new ArrayList<>();
sorts.add(Sort.Order.desc("createDate"));
Pageable pageable = PageRequest.of(page, 10, Sort.by(sorts));
Specification<Question> spec = search(kw);
return this.questionRepository.findAll(spec, pageable);
}
// (... μλ΅ ...)
}
κ²μμ΄λ₯Ό μλ―Ένλ 맀κ°λ³μ kw
λ₯Ό getList
μ μΆκ°νκ³ kw
κ°μΌλ‘ Specification
κ°μ²΄λ₯Ό μμ±νμ¬ findAll
λ©μλ νΈμΆμ μ λ¬νμλ€.
QuestionController
κ·Έλ¦¬κ³ QuestionService
μ getList
λ©μλμ μ
λ ₯νλͺ©μ΄ λ³κ²½λμμΌλ―λ‘ QuestionController
λ λ€μκ³Ό κ°μ΄ μμ ν΄μΌ νλ€.
νμΌλͺ :
/sbb/src/main/java/com/mysite/sbb/question/
QuestionController.java
// (... μλ΅ ...)
public class QuestionController {
// (... μλ΅ ...)
@GetMapping("/list")
public String list(Model model, @RequestParam(value = "page", defaultValue = "0") int page,
@RequestParam(value = "kw", defaultValue = "") String kw) {
Page<Question> paging = this.questionService.getList(page, kw);
model.addAttribute("paging", paging);
model.addAttribute("kw", kw);
return "question_list";
}
// (... μλ΅ ...)
}
κ²μμ΄μ ν΄λΉνλ kw
νλΌλ―Έν°λ₯Ό μΆκ°νκ³ λν΄νΈκ°μΌλ‘ λΉ λ¬Έμμ΄μ μ€μ νλ€. κ·Έλ¦¬κ³ νλ©΄μμ μ
λ ₯ν κ²μμ΄λ₯Ό νλ©΄μ μ μ§νκΈ° μν΄ model.addAttribute("kw", kw)
λ‘ kw
κ°μ μ μ₯νλ€. μ΄μ νλ©΄μμ kw
κ°μ΄ νλΌλ―Έν°λ‘ λ€μ΄μ€λ©΄ ν΄λΉ κ°μΌλ‘ μ§λ¬Έ λͺ©λ‘μ΄ κ²μλμ΄ μ‘°νλ κ²μ΄λ€.
κ²μ νλ©΄
μ΄μ νλ©΄μ κ²μκΈ°λ₯μ μΆκ°ν΄ 보μ.
κ²μ μ°½
κ²μμ΄λ₯Ό μ λ ₯ν μ μλ ν μ€νΈμ°½μ λ€μκ³Ό κ°μ΄ μ§λ¬Έ λͺ©λ‘ ν νλ¦Ώμ μΆκ°νμ.
νμΌλͺ :
/sbb/src/main/resources/templates/
question_list.html
<html layout:decorate="~{layout}">
<div layout:fragment="content" class="container my-3">
<div class="row my-3">
<div class="col-6">
<a th:href="@{/question/create}" class="btn btn-primary">μ§λ¬Έ λ±λ‘νκΈ°</a>
</div>
<div class="col-6">
<div class="input-group">
<input type="text" id="search_kw" class="form-control" th:value="${kw}">
<button class="btn btn-outline-secondary" type="button" id="btn_search">μ°ΎκΈ°</button>
</div>
</div>
</div>
<table class="table">
<!-- (... μλ΅ ...) -->
</table>
<!-- νμ΄μ§μ²λ¦¬ μμ -->
<!-- (... μλ΅ ...) -->
<!-- νμ΄μ§μ²λ¦¬ λ -->
<!-- <a th:href="@{/question/create}" class="btn btn-primary">μ§λ¬Έ λ±λ‘νκΈ°</a> -->
</div>
</html>
<table>
νκ·Έ μλ¨ μ°μΈ‘μ κ²μμ΄λ₯Ό μ
λ ₯ν μ μλ ν
μ€νΈμ°½μ μμ±νμλ€. 맨 λ°μ μλ ["μ§λ¬Έ λ±λ‘νκΈ°"]
λ²νΌμ κ²μ μ°½μ μ’μΈ‘μΌλ‘ μ΄λνλ€. κ·Έλ¦¬κ³ μλ° μ€ν¬λ¦½νΈμμ μ΄ ν
μ€νΈμ°½μ μ
λ ₯λ κ°μ μ½κΈ° μν΄ λ€μμ²λΌ ν
μ€νΈμ°½ id
μμ±μ "search_kw"
λΌλ κ°μ μΆκ°ν μ μ μ£Όλͺ©νμ.
<input type="text" id="search_kw" class="form-control" th:value="${kw}">
κ²μ νΌ
κ·Έλ¦¬κ³ page
μ kw
λ₯Ό λμμ GETμΌλ‘ μμ²ν μ μλ searchForm
μ λ€μκ³Ό κ°μ΄ μΆκ°νμ.
νμΌλͺ :
/sbb/src/main/resources/templates/
question_list.html
<!-- (... μλ΅ ...) -->
<!-- νμ΄μ§μ²λ¦¬ λ -->
<form th:action="@{/question/list}" method="get" id="searchForm">
<input type="hidden" id="kw" name="kw" th:value="${kw}">
<input type="hidden" id="page" name="page" th:value="${paging.number}">
</form>
</div>
</html>
GET λ°©μμΌλ‘ μμ²ν΄μΌ νλ―λ‘ method μμ±μ "get"μ μ€μ νλ€. kw
μ page
λ μ΄μ μ μμ²νλ κ°μ κΈ°μ΅νκ³ μμ΄μΌ νλ―λ‘ value
μ κ°μ μ μ§ν μ μλλ‘ νλ€.
μ΄μ μ μμ²νλ
kw
μpage
μ κ°μ 컨νΈλ‘€λ¬λ‘λΆν° λ€μ μ λ¬ λ°λλ€.
κ·Έλ¦¬κ³ action μμ±μ "νΌμ΄ μ μ‘λλ URL"μ΄λ―λ‘ μ§λ¬Έ λͺ©λ‘ URLμΈ /question/list
λ₯Ό μ§μ νλ€.
GET λ°©μμ μ¬μ©νλ μ΄μ
page
, kw
λ₯Ό GETμ΄ μλ POST λ°©μμΌλ‘ μ λ¬νλ λ°©λ²μ μΆμ²νκ³ μΆμ§ μλ€. λ§μ½ GETμ΄ μλ POST λ°©μμΌλ‘ κ²μκ³Ό νμ΄μ§μ μ²λ¦¬νλ€λ©΄ μΉ λΈλΌμ°μ μμ "μλ‘κ³ μΉ¨" λλ "λ€λ‘κ°κΈ°"λ₯Ό νμ λ "λ§λ£λ νμ΄μ§μ
λλ€."λΌλ μ€λ₯λ₯Ό μ’
μ’
λ§λκ² λ κ²μ΄λ€. μλνλ©΄ POST λ°©μμ λμΌν POST μμ²μ΄ λ°μν κ²½μ° μ€λ³΅ μμ²μ λ°©μ§νκΈ° μν΄ "λ§λ£λ νμ΄μ§μ
λλ€." λΌλ μ€λ₯λ₯Ό λ°μμν€κΈ° λλ¬Έμ΄λ€. 2νμ΄μ§μμ 3νμ΄μ§λ‘ κ°λ€κ° λ€λ‘κ°κΈ°λ₯Ό νμ λ 2νμ΄μ§λ‘ κ°λκ²μ΄ μλλΌ μ€λ₯κ° λ°μνλ€λ©΄ μλ§μ΄ λ κ²μ΄λ€.
μ΄λ¬ν μ΄μ λ‘ μ¬λ¬ νλΌλ―Έν°λ₯Ό μ‘°ν©νμ¬ κ²μλ¬Ό λͺ©λ‘μ μ‘°νν λλ GET λ°©μμ μ¬μ©νλ κ²μ΄ μ’λ€.
νμ΄μ§
κ·Έλ¦¬κ³ κΈ°μ‘΄ νμ΄μ§μ μ²λ¦¬νλ λΆλΆλ ?page=1
μ²λΌ μ§μ URLμ λ§ν¬νλ λ°©μμμ κ°μ μ½μ΄ νΌμ μ€μ ν μ μλλ‘ λ€μμ²λΌ λ³κ²½ν΄μΌ νλ€. μλνλ©΄ κ²μμ΄κ° μμ κ²½μ° κ²μμ΄μ νμ΄μ§ λ²νΈλ₯Ό ν¨κ» μ μ‘ν΄μΌ νκΈ° λλ¬Έμ΄λ€.
νμΌλͺ :
/sbb/src/main/resources/templates/
question_list.html
<!-- (... μλ΅ ...) -->
<!-- νμ΄μ§μ²λ¦¬ μμ -->
<div th:if="${!paging.isEmpty()}">
<ul class="pagination justify-content-center">
<li class="page-item" th:classappend="${!paging.hasPrevious} ? 'disabled'">
<a class="page-link" href="javascript:void(0)" th:data-page="${paging.number-1}">
<span>μ΄μ </span>
</a>
</li>
<li th:each="page: ${#numbers.sequence(0, paging.totalPages-1)}"
th:if="${page >= paging.number-5 and page <= paging.number+5}"
th:classappend="${page == paging.number} ? 'active'" class="page-item">
<a th:text="${page}" class="page-link" href="javascript:void(0)" th:data-page="${page}"></a>
</li>
<li class="page-item" th:classappend="${!paging.hasNext} ? 'disabled'">
<a class="page-link" href="javascript:void(0)" th:data-page="${paging.number+1}">
<span>λ€μ</span>
</a>
</li>
</ul>
</div>
<!-- νμ΄μ§μ²λ¦¬ λ -->
<!-- (... μλ΅ ...) -->
λͺ¨λ νμ΄μ§ λ§ν¬λ₯Ό href
μμ±μ μ§μ μ
λ ₯νλ λμ data-page
μμ±μΌλ‘ κ°μ μ½μ μ μλλ‘ λ³κ²½νλ€.
μ¦, λ€μκ³Ό κ°μ λ§ν¬λ₯Ό
<a class="page-link" th:href="@{|?page=${paging.number-1}|}">
<span>μ΄μ </span>
</a>
λ€μμ²λΌ μμ νλ€.
<a class="page-link" href="javascript:void(0)" th:data-page="${paging.number-1}">
<span>μ΄μ </span>
</a>
κ²μ μ€ν¬λ¦½νΈ
κ·Έλ¦¬κ³ page
, kw
νλΌλ―Έν°λ₯Ό λμμ μμ²ν μ μλ μλ°μ€ν¬λ¦½νΈλ₯Ό λ€μκ³Ό κ°μ΄ μΆκ°νμ.
νμΌλͺ :
/sbb/src/main/resources/templates/
question_list.html
<!-- (... μλ΅ ...) -->
<!-- νμ΄μ§μ²λ¦¬ λ -->
<form th:action="@{/question/list}" method="get" id="searchForm">
<input type="hidden" id="kw" name="kw" th:value="${kw}">
<input type="hidden" id="page" name="page" th:value="${paging.number}">
</form>
</div>
<script layout:fragment="script" type='text/javascript'>
const page_elements = document.getElementsByClassName("page-link");
Array.from(page_elements).forEach(function(element) {
element.addEventListener('click', function() {
document.getElementById('page').value = this.dataset.page;
document.getElementById('searchForm').submit();
});
});
const btn_search = document.getElementById("btn_search");
btn_search.addEventListener('click', function() {
document.getElementById('kw').value = document.getElementById('search_kw').value;
document.getElementById('page').value = 0; // κ²μλ²νΌμ ν΄λ¦ν κ²½μ° 0νμ΄μ§λΆν° μ‘°ννλ€.
document.getElementById('searchForm').submit();
});
</script>
</html>
μμ μΆκ°ν μλ°μ€ν¬λ¦½νΈ μ½λλ₯Ό μμΈν μ΄ν΄λ³΄μ. λ§μ½ λ€μκ³Ό κ°μ΄ class
μμ±κ°μΌλ‘ "page-link"
λΌλ κ°μ κ°μ§κ³ μλ λ§ν¬λ₯Ό ν΄λ¦νλ©΄
<a class="page-link" href="javascript:void(0)" th:data-page="${paging.number-1}">
<span>μ΄μ </span>
</a>
μ΄ λ§ν¬μ data-page
μμ±κ°μ μ½μ΄ searchForm
μ page
νλμ μ€μ νμ¬ searchForm
μ μμ²νλλ‘ λ€μκ³Ό κ°μ μ€ν¬λ¦½νΈλ₯Ό μΆκ°νλ€.
const page_elements = document.getElementsByClassName("page-link");
Array.from(page_elements).forEach(function(element) {
element.addEventListener('click', function() {
document.getElementById('page').value = this.dataset.page;
document.getElementById('searchForm').submit();
});
});
κ·Έλ¦¬κ³ κ²μλ²νΌμ ν΄λ¦νλ©΄ κ²μμ΄ ν
μ€νΈμ°½μ μ
λ ₯λ κ°μ searchForm
μ kw
νλμ μ€μ νμ¬ searchForm
μ μμ²νλλ‘ λ€μκ³Ό κ°μ μ€ν¬λ¦½νΈλ₯Ό μΆκ°νλ€.
const btn_search = document.getElementById("btn_search");
btn_search.addEventListener('click', function() {
document.getElementById('kw').value = document.getElementById('search_kw').value;
document.getElementById('page').value = 0; // κ²μλ²νΌμ ν΄λ¦ν κ²½μ° 0νμ΄μ§λΆν° μ‘°ννλ€.
document.getElementById('searchForm').submit();
});
κ·Έλ¦¬κ³ κ²μλ²νΌμ ν΄λ¦νλ κ²½μ°λ μλ‘μ΄ κ²μμ ν΄λΉλλ―λ‘ page
μ νμ 0μ μ€μ νμ¬ μ²« νμ΄μ§λ‘ μμ²νλλ‘ νλ€.
κ²μ νμΈ
@Query
쿼리μ μ΅μνλ€λ©΄ 볡μ‘ν 쿼리λ μλ°μ½λλ‘ μμ±νκΈ° 보λ€λ μ§μ 쿼리λ₯Ό μμ±νλκ² ν¨μ¬ νΈνκ² μ¬κ²¨μ§ κ²μ΄λ€. μ΄λ²μλ Specification
λμ μ§μ 쿼리λ₯Ό μμ±νμ¬ μννλ λ°©λ²μ λν΄μ μμ보μ.
.QuestionRepository
μ λ€μκ³Ό κ°μ λ©μλλ₯Ό μΆκ°ν΄ 보μ.
// (... μλ΅ ...)
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface QuestionRepository extends JpaRepository<Question, Integer> {
// (... μλ΅ ...)
@Query("select "
+ "distinct q "
+ "from Question q "
+ "left outer join SiteUser u1 on q.author=u1 "
+ "left outer join Answer a on a.question=q "
+ "left outer join SiteUser u2 on a.author=u2 "
+ "where "
+ " q.subject like %:kw% "
+ " or q.content like %:kw% "
+ " or u1.username like %:kw% "
+ " or a.content like %:kw% "
+ " or u2.username like %:kw% ")
Page<Question> findAllByKeyword(@Param("kw") String kw, Pageable pageable);
}
@Query
μ λν
μ΄μ
μ΄ μ μ©λ findAllByKeyword
λ©μλλ₯Ό μΆκ°νλ€. μμμ μμλ³Έ 쿼리λ₯Ό @Query
μ ꡬνν κ²μ΄λ€. κ·Έλ¦¬κ³ @Query
λ₯Ό μμ±ν λμλ λ°λμ ν
μ΄λΈ κΈ°μ€μ΄ μλ μν°ν° κΈ°μ€μΌλ‘ μμ±ν΄μΌ νλ€. μ¦, site_user
μ κ°μ ν
μ΄λΈλͺ
λμ SiteUser
μ²λΌ μν°ν°λͺ
μ μ¬μ©ν΄μΌ νκ³ μ‘°μΈλ¬Έμμ 보λ―μ΄ q.author_id=u1.id
μ κ°μ 컬λΌλͺ
λμ q.author=u1
μ²λΌ μν°ν°μ μμ±λͺ
μ μ¬μ©ν΄μΌ νλ€.
κ·Έλ¦¬κ³ @Query
μ νλΌλ―Έν°λ‘ μ λ¬ν kw
λ¬Έμμ΄μ λ©μλμ 맀κ°λ³μμ @Param("kw")
μ²λΌ @Param
μ λν
μ΄μ
μ μ¬μ©ν΄μΌ νλ€. κ²μμ΄λ₯Ό μλ―Ένλ kw
λ¬Έμμ΄μ @Query
μμμ :kw
λ‘ μ°Έμ‘°λλ€.
μμ±ν findAllByKeyword
λ©μλλ₯Ό μ¬μ©νκΈ° μν΄ QuestionService
λ₯Ό λ€μκ³Ό κ°μ΄ μμ νμ.
// (... μλ΅ ...)
public class QuestionService {
// (... μλ΅ ...)
public Page<Question> getList(int page, String kw) {
List<Sort.Order> sorts = new ArrayList<>();
sorts.add(Sort.Order.desc("createDate"));
Pageable pageable = PageRequest.of(page, 10, Sort.by(sorts));
return this.questionRepository.findAllByKeyword(kw, pageable);
}
// (... μλ΅ ...)
}
.Specification
μ μ¬μ©ν λμ λμΌνκ² λμν κ²μ΄λ€.