다연이네

[days14] 페이징 처리 및 [검색 기능] 구현 (2/2) 본문

Spring

[days14] 페이징 처리 및 [검색 기능] 구현 (2/2)

 다연  2021. 2. 22. 21:22
반응형

앞의 페이징 처리에 이어서 검색 기능 추가 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);  
반응형
Comments