일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- isinterrupted()
- include지시자
- first-of-child
- 리눅스셋팅
- 스레드그룸
- StringWriter
- ObjectInputStream
- sleep()메소드
- String char[] 형변환
- 아이디중복
- 상관 서브 쿼리
- Daemon()
- include액션태그
- interrupt()
- 동기화
- ThreadGroup()
- InputDialog
- StringReader
- include 지시자
- Linux셋팅
- interrupted()
- MemoryStream
- 메모리스트림
- 상관서브쿼리
- char[] String 형변환
- 리눅스세팅
- Linux세팅
- 표현 언어
- ID중복
- first-child
- Today
- Total
다연이네
[days14] 페이징 처리 및 [검색 기능] 구현 (2/2) 본문
앞의 페이징 처리에 이어서 검색 기능 추가 p325
게시물의 검색 기능은 다음과 같이 분류가 가능하다.
- 제목/내용/작성자와 같이 단일 항목 검색
- 제목,내용/제목,작성자 등 다중 항목 검색
** AND와 OR이 섞여있는 SQL을 작성할 때는 우선순위 연산자인 '()'를 이용해 OR 조건을 처리해야한다.
MyBatis의 동적 SQL
MyBatis는 동적 태그 기능을 통해서 SQL을 파라미터들의 조건에 맞게 조정할 수 있는 기능을 제공한다.
MyBatis의 동적 태그는 약간의 구문을 이용해 전달되는 파라미터를 가공해서 경우에 따라 다른 SQL을 만들어 실행할 수 있다.
- MyBatis의 동적 태그들
if / choose(when, otherwise) / trim(where, set) / foreach
if - 검색 조건이 'T'이면 title이 keyword인 항목 검색
<if test="type == 'T'.toString()">
( title like '%' || #{keyword} || '%' )
</if>
<if ... >
:
</if>
<choose> - 여러 상황들 중 하나의 상황에만 동작 (if~else 처럼)
<choose>
<when test="type == 'T'.toString()">
(title like '%' || #{keyword} || '%' )
</when>
<when ...>
:
</when>
<otherwise>
(title like '%' || #{keyword} || '%' OR content like '%' || #{keyword} || '%')
</otherwise>
</choose>
<trim> <where> <set> - 단독으로 사용되지 않고 <if>, <choose>와 같은 태그들을 내포하여 SQL들을 연결,
앞 뒤에 필요한 구문들 (AND, OR, WHERE 등)을 추가하거나 생략하는 역할
<where>의 경우 태그 안쪽에서 SQL이 생성될 때는 WHERE 구문이 붙고, 그렇지 않은 경우 생성되지 않는다.
select * from tbl_board
<where>
<if test="bno != null">
bno = #{bno}
</if>
</where>
- bno 값이 존재하는 경우 : select * from tbl_board where bno=33
- bno 값이 존재하지 않는 경우 : select * from tbl_board
<trim>은 하위에서 만들어지는 SQL문을 조사하여 앞 쪽에 추가적인 SQL을 넣을 수 있다.
prefix, suffix, prefixOverrides, suffixOverrides 속성을 지정 가능하다.
select * from tbl_board
<where>
<if test = "bno != null">
bno = #{bno}
</if>
<trim prifix = "and">
rownum = 1
</trim>
</where>
- bno 값이 존재하는 경우 : select * from tbl_board where bno = 33 and rownum = 1
- bno 값이 존재하지 않는 경우 : select * from tbl_board where rownum=1
<foreach>는 List, 배열, 맵 등을 이용해 루프를 처리할 수 있다. 주로 IN 조건에서 많이 사용하지만, 경우에 따라서는 복잡한 WHERE 조건을 만들 때에도 사용할 수 있다.
foreach를 배열이나 List를 이용하는 경우에는 item 속성만을 이용하면 되고, Map의 형태로 key와 value를 이용해야 할 때에는 index와 item속성을 둘 다 이용한다.
19.
검색 조건 처리를 위한 Criteria의 변화
-> 필드 type(조건)과 keyword(검색어) 추가, 메소드 생성 2개
package org.zerock.domain;
import org.springframework.web.util.UriComponentsBuilder;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@ToString
@Setter
@Getter
public class Criteria {
private int pageNum;
private int amount;
private String type;
private String keyword;
public Criteria() {
this(1, 10);
}
public Criteria(int pageNum, int amount) {
this.pageNum = pageNum;
this.amount = amount;
}
public String[] getTypeArr() {
return type == null? new String[] {}: type.split("");
//TC 이면 T하나 C하나 잘라 배열로 만듦
}
// p 349
// 가장 편리한 점 한글 처리에 신경쓰지 않아도 된다.
// 쿼리스트링(queryString)
// UriComponentsBuilder 클래스 라이브러리 사용해서 자동으로
// ?pageNum=2&amount=10&keyword=kenik&type=W 이렇게 뒤에 붙는다. (jsp에서 지저분하게 안해도 됨)
public String getListLink() {
String path = "";
UriComponentsBuilder builder =
UriComponentsBuilder.fromPath(path)
.queryParam("pageNum", this.pageNum)
.queryParam("amount", this.amount)
.queryParam("type", this.getType())
.queryParam("keyword", this.keyword)
;
return builder.toUriString();
}
}
20.
list.jsp
검색을 위한 태그
<form id='searchForm' action="/board/list" method='get'>
<select name='type'>
<option value=""
<c:out value="${pageMaker.cri.type == null?'selected':''}"/>>--</option>
<option value="T"
<c:out value="${pageMaker.cri.type eq 'T'?'selected':''}"/>>제목</option>
<option value="C"
<c:out value="${pageMaker.cri.type eq 'C'?'selected':''}"/>>내용</option>
<option value="W"
<c:out value="${pageMaker.cri.type eq 'W'?'selected':''}"/>>작성자</option>
<option value="TC"
<c:out value="${pageMaker.cri.type eq 'TC'?'selected':''}"/>>제목
or 내용</option>
<option value="TW"
<c:out value="${pageMaker.cri.type eq 'TW'?'selected':''}"/>>제목
or 작성자</option>
<option value="TWC"
<c:out value="${pageMaker.cri.type eq 'TWC'?'selected':''}"/>>제목
or 내용 or 작성자</option>
</select>
<input type='text' name='keyword' value='<c:out value="${pageMaker.cri.keyword}"/>' />
<input type='hidden' name='pageNum' value='<c:out value="${pageMaker.cri.pageNum}"/>' />
<input type='hidden' name='amount' value='<c:out value="${pageMaker.cri.amount}"/>' />
<button class='btn btn-default'>Search</button>
</form>
검색을 위한 스크립트
<script>
//p342 검색을 위한
var searchForm = $("#searchForm");
$("#searchForm button").on( "click", function(e) {
if (!searchForm.find("option:selected").val()) {
alert("검색종류를 선택하세요");
return false;
}
if (!searchForm.find( "input[name='keyword']").val()) {
alert("키워드를 입력하세요");
return false;
}
searchForm.find("input[name='pageNum']").val("1");
e.preventDefault();
searchForm.submit();
});
</script>
21.
총 레코드 수 가져올때도 검색된 것의 총 레코드 수를 가져와야 페이징 처리 가능
=> 반복적으로 써지기 때문에
BoardMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.zerock.mapper.BoardMapper">
<sql id="criteria">
<trim prefix="(" suffix=") AND " prefixOverrides="OR">
<foreach item='type' collection="typeArr">
<trim prefix="OR">
<choose>
<when test="type == 'T'.toString()">
title like '%'||#{keyword}||'%'
</when>
<when test="type == 'C'.toString()">
content like '%'||#{keyword}||'%'
</when>
<when test="type == 'W'.toString()">
writer like '%'||#{keyword}||'%'
</when>
</choose>
</trim>
</foreach>
</trim>
</sql>
<select id="getList" resultType="org.zerock.domain.BoardVO">
<![CDATA[
select * from tbl_board where bno > 0
]]>
</select>
<insert id="insert">
insert into tbl_board (bno,title,content,writer)
values (seq_board.nextval, #{title}, #{content}, #{writer})
</insert>
<insert id="insertSelectKey">
<selectKey keyProperty="bno" order="BEFORE"
resultType="long">
select seq_board.nextval from dual
</selectKey>
insert into tbl_board (bno,title,content, writer)
values (#{bno},
#{title}, #{content}, #{writer})
</insert>
<select id="read" resultType="org.zerock.domain.BoardVO">
select * from tbl_board where bno =
#{bno}
</select>
<delete id="delete">
delete tbl_board where bno = #{bno}
</delete>
<update id="update">
update tbl_board
set title= #{title},
content=#{content},
writer = #{writer},
updateDate = sysdate
where bno =
#{bno}
</update>
<!-- <select id="getListWithPaging" resultType="org.zerock.domain.BoardVO">
<![CDATA[ select bno, title, content, writer, regdate, updatedate from (
select /*+INDEX_DESC(tbl_board pk_board) */ rownum rn, bno, title, content,
writer, regdate, updatedate from tbl_board where rownum <= #{pageNum} * #{amount}
) where rn > (#{pageNum} -1) * #{amount} ]]> </select> -->
<select id="getListWithPaging"
resultType="org.zerock.domain.BoardVO">
<![CDATA[
select
bno, title, content, writer, regdate, updatedate
from
(
select /*+INDEX_DESC(tbl_board pk_board) */
rownum rn, bno, title, content, writer, regdate, updatedate
from
tbl_board
where
]]>
<include refid="criteria"></include>
<![CDATA[
rownum <= #{pageNum} * #{amount}
)
where rn > (#{pageNum} -1) * #{amount}
]]>
</select>
<select id="getTotalCount" resultType="int">
select count(*) from tbl_board where
<include refid="criteria"></include>
bno > 0
</select>
</mapper>
22.
페이징 토탈 카운트할때도 위 trim 코딩이 필요하니 이 코딩을 sql이라는 태그로 하나 빼자
=> 중복해서 사용되는 동적SQL 쿼리 부분을 sql 태그 사용해서 하나 뺌
<sql id="criteria"></sql>
필요한 곳에 <include refid="criteria"></include>
23.
test하기
24.
25.
검색 버튼의 이벤트 처리 - list.jsp
<form id='actionForm' action="/board/list" method='get'>
<input type='hidden' name='pageNum' value='<c:out value="${pageMaker.cri.pageNum}"/>' />
<input type='hidden' name='amount' value='<c:out value="${pageMaker.cri.amount}"/>' />
<input type='hidden' name='type' value='<c:out value="${ pageMaker.cri.type }"/>'>
<input type='hidden' name='keyword' value='<c:out value="${ pageMaker.cri.keyword }"/>'>
</form>
26.
조회 페이지에서 검색 추가 -
get.jsp operForm에
<input type='hidden' name='keyword' value='<c:out value="${cri.keyword}"/>'>
<input type='hidden' name='type' value='<c:out value="${cri.type}"/>'>
modify.jsp
<input type='hidden' name='type' value='<c:out value="${cri.type }"/>'>
<input type='hidden' name='keyword' value='<c:out value="${cri.keyword }"/>'>
27.
컨트롤러 1) modify에 추가, 2) remove 수정
@PostMapping("/modify")
public String modify(BoardVO board, @ModelAttribute("cri") Criteria cri, RedirectAttributes rttr) {
log.info("modify:" + board);
if (service.modify(board)) {
rttr.addFlashAttribute("result", "success");
}
rttr.addAttribute("pageNum", cri.getPageNum());
rttr.addAttribute("amount", cri.getAmount());
rttr.addAttribute("type", cri.getType()); //얘네 두개 추가
rttr.addAttribute("keyword", cri.getKeyword()); //얘네 두개 추가
return "redirect:/board/list";
}
// p.220
/*
@PostMapping("/remove")
public String remove(@RequestParam("bno") Long bno, RedirectAttributes rttr)
{
log.info("remove..." + bno);
if (service.remove(bno)) {
rttr.addFlashAttribute("result", "success");
}
return "redirect:/board/list";
}
*/
@PostMapping("/remove")
public String remove(@RequestParam("bno") Long bno, Criteria cri, RedirectAttributes rttr) {
log.info("remove..." + bno);
if (service.remove(bno)) {
rttr.addFlashAttribute("result", "success");
}
rttr.addAttribute("pageNum", cri.getPageNum());
rttr.addAttribute("amount", cri.getAmount());
rttr.addAttribute("type", cri.getType());
rttr.addAttribute("keyword", cri.getKeyword());
return "redirect:/board/list";
}
28.
modify.jsp 가서 주석 풀기
var keywordTag = $("input[name='keyword']").clone();
var typeTag = $("input[name='type']").clone();
formObj.append(keywordTag);
formObj.append(typeTag);
'Spring' 카테고리의 다른 글
[days15] Ajax 댓글 처리 (0) | 2021.02.23 |
---|---|
[days15] REST 방식으로 전환 (0) | 2021.02.23 |
[days14] [페이징 처리] 및 검색 기능 구현 (1/2) (0) | 2021.02.22 |
[days13] 기본적인 웹 게시물 관리 - CRUD(등록, 수정, 삭제, 조회) (0) | 2021.02.19 |
[days13] 예외 발생 처리 - Controller의 Exception 처리 (0) | 2021.02.19 |