-
0914. 검색처리카테고리 없음 2023. 9. 15. 09:07
게시물의 검색 기능
like 를 써서 '%xxx%'
제목, 내용, 작성자와 같이 단일 항목 검색
단일 말고 이 중 두 개씩, 3개 다 묶어서 할 수도 있다.
게시물 검색 기능 7가지 경우 어떻게 쓸지는 사용자가 정하므로 MyBatis 동적 SQL 이 필요하다.
**MyBatis의 동적태그들**
-if : 조건이 참인 경우에만 추가해라. else 없음
-choose(when, otherwise) : 다중 if문 역할
-trim(where, set) : 덧붙이거나 뺄 때
-foreach : 루프 돌릴 때
<if>
내용이 false면 추가 안 됨.
*T : title, C: content, W: writer
검색 대상 : type == ~ .
T, C, W 이면 문자열로 변환시켜서 빨간 화살표 옆을 넣어라
❗CONCAT (문자열 합치기) 을 쓰는 이유
CONCAT('%',#{keyword},'%'))
3개의 문자열을 연결해라. '%' 랑 #{keyword} 랑 '%'
MyBatis에서는 title like '%#{keyword}%' 라고 써도 되지만
이걸 JDBC에서 쓰면 prepared DB를 쓰므로 title like '%?%' 라고 되는데 keyword'xx'일 때
실제 SQL 에서는 문자열이므로 자동으로 따옴표를 붙여서 에러가 난다.
title like '%'xx'%' 이렇게 문장이 만들어진다.
오라클이었다면 like'%'||#{keyword}||'%'
|| 문자열 결합 기능. MySQL에서는 이 수직바 연산자가 없어서 함수로 연결해 줘야 한다.
<choose>
otherwise는 옵션이다.
trim, where, set
<where> 태그 안에 내용이 있을 수도 있고 없을 수도 있다. 만약 <if>태그에 결과가 있으면 (=bno가 있으면) where가 추가.
존재하지 않으면 where가 빠진다. set도 설정할 게 있으면 넣고 아님 만다. 큰 활용도는 없다.
<trim> 자르기와 다듬기 기능
주로 다듬는 기능
<trim prefix="OR">trim 태그로 감싼 기능을 추가할 때는 앞에 OR을 붙여서 추가하란 뜻. 뒤는 suffix
OR을 다시 제거하려면 Overrides
단독으로 쓰이기보다 주로 foreach랑 같이 사용
foreach문을 돌리려면 collection이 있어야 한다. 주로 문자열 배열을 전달해서 넘긴다.
Criteria 에 필드를 두 개 추가할 것이다. type, keyword
TC : title, content / TCW : 세 군데 다 / C: content 만
검색대상에 대한 문자열 배열이 필요하므로
스트링 메서드 split 스트링 메서드 split은 매개변수로 빈 문자열을 준다. 그러면 글자단위로 잘라준다.
글자 하나가 문자열이 되게끔 만들 수 있다. => 이것이 foreach 로 넘어간다.
// 생성자 public String[] getTypeArr() { return type == null ? new String[] {} : // 빈 배열 리턴 type.split(""); // 한글자 단위로 분리된 배열 리턴 }
getTypeArr() 의 프로퍼티명은 typeArr 로 camelcase로.. => #{typeArr}
처음엔 foreach로 다 묶어주고 or 로 묶어야 하니 prefix="OR" 로 했다가,
(전체 검색 성질 7개를 묶으려면 or로 묶어야 하니까)
where 뒤에 OR은 문법에러라 빠져야하므로 prefixOverrides로 다시 빼준다. (suffix로 했을 경우도 마찬가지)
BoardMapperTest
@Test public void testSearch() { Criteria cri = new Criteria(); // 첫 번째 페이지 cri.setKeyword("새로"); cri.setType("TC"); // 제목, 내용에서 검색 List<BoardVO> list = mapper.getListWithPaging(cri); for (BoardVO board : list) { log.info(board); } }
디폴트 생성자 호출하면 pageNum : 1 ,amount : 10이 된다. (첫 번째 페이지)
PageDTO에 필요한 것
<select id="getListWithPaging" resultType="BoardVO"> select * from tbl_board <where> <trim prefixOverrides="OR"> <foreach item="type" collection="typeArr"> <trim prefix="OR"> <choose> <when test="type == 'T'.toString()"> title like CONCAT('%',#{keyword},'%') </when> <when test="type == 'C'.toString()"> content like CONCAT('%',#{keyword},'%') </when> <when test="type == 'W'.toString()"> writer like CONCAT('%',#{keyword},'%') </when> </choose> </trim> </foreach> </trim> </where> order by bno desc limit #{offset}, #{amount} </select> <select id="getTotalCount" resultType="int"> select count(*) from tbl_board </select>
중복코드 피하려고 include로 간단히 해 줄 수 있다.
include 처리하고 sql 문으로 trim 부분은 빼주기 이렇게 하니까 된다!
더보기이렇게 하니까 된다!
<?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.galapagos.mapper.BoardMapper"> <select id="getTotalCount" resultType="int"> select count(*) from tbl_board; <where> <include refid="criteria"></include> </where> </select> <select id="getList" resultType="BoardVO"> <![CDATA[ select * from tbl_board order by bno desc ]]> </select> <select id="getListWithPaging" resultType="BoardVO"> select * from tbl_board <where> <include refid="criteria"></include> </where> order by bno desc limit #{offset} , #{amount} </select> <insert id="insert"> insert into tbl_board (title, content, writer) values (#{title}, #{content}, #{writer}) </insert> <insert id="insertSelectKey"> insert into tbl_board (title, content, writer) values (#{title}, #{content}, #{writer}) <selectKey resultType="Long" keyProperty="bno" keyColumn="bno" order="AFTER"> SELECT LAST_INSERT_ID() </selectKey> </insert> <select id="read" resultType="BoardVO"> select * from tbl_board where bno = #{bno} </select> <delete id="delete"> delete from tbl_board where bno = #{bno} </delete> <update id="update"> update tbl_board set title = #{title}, content = #{content}, writer = #{writer}, update_date = now() where bno = #{bno} </update> <sql id="criteria"> <trim prefixOverrides="OR"> <foreach item="type" collection="typeArr"> <trim prefix="OR"> <choose> <when test="type == 'T'.toString()"> title like CONCAT('%', #{keyword}, '%') </when> <when test="type == 'C'.toString()"> content like CONCAT('%', #{keyword}, '%') </when> <when test="type == 'W'.toString()"> writer like CONCAT('%', #{keyword}, '%') </when> </choose> </trim> </foreach> </trim> </sql> </mapper>
검색에서 아무 것도 입력 안 하고 검색버튼 누르면 뜨는 안내창
==> 필수요소 체크하는 required 성질과 같다고 보면 된다. ❗❗
list 상세보기에서 목록 누르면 전체목록 말고 검색했던 값 유지한 상태의 페이지로 가게 하기
하다보니 form 태그가 너무 지저분하다.
UriComponentBuilder 를 이용하는 링크 생성
빌드업 패턴이 일반적으로 static으로 구성되어 있다.
알아서 url 문자열을 만들어준다.
path 설정 -> 파라미터 설정 -> 최종 문자열 요구하면 .queryParam("amount", 10) 이렇게 리턴해 줌
여기서 10같은 정보는 Criteria 가 다 갖고 있다.
Criteria.java에서 작업하고 위에 builder 살펴보기
base(url), pageNum : 지역변수. 전달받아서 처리할 수 있게.
amount, type, keyword : 멤버변수
1. 매개변수 없는 건 빈 문자열 "" / 여기서 pageNum은 멤버변수
2. 여기서 pageNum은 지역변수
1과 2에서 pageNum은 ?를 만들어주는 것이다. => 형태는 ?xx=yyy& ~
3. base url 만 바꾸겠다.
⭐4.Criteria에서 만들어준 문자열에다가 bno를 하나 더 추가해 달라.(bno정보는 criteria에 없기 때문에)
=> criteria에 없는 정보를 더 추가하는 역할
자바 스크립트로 Form 처리하는 것 잊지 말기
get.jsp
글 쓰다가 있던 페이지로 돌아가고 싶을 때(=뒤로 가기)
href 부분을 list에서 바꿨다.
🧙javascript:history.back()
브라우저에서 돌아가기 기능과 동일함
pagination.jsp로 일반화하기
pagination으로 만들어서 관리 페이지네이션 부분을 common 부분으로 빼서 쓰기 위해서는
기존 list.jsp 부분에 이 코드를 넣어줘야 한다. (위치 맞춰서)
<%@include file="../common/pagination.jsp"%>
해당 ul 태그는 list.jsp 에서 지워주면 된다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!-- 나중에 가져다 쓸 pagination --> <ul class="pagination justify-content-center"> <c:if test="${pageMaker.cri.pageNum > 1}"> <li class="page-item"> <a class="page-link" href="${cri.getLink(1)}"> <i class="fa-solid fa-backward-step"></i> </a> </li> </c:if> <c:if test="${pageMaker.prev}"> <li class="page-item"> <a class="page-link" href="${cri.getLink(pageMaker.startPage-1)}"> <i class="fa-solid fa-angle-left"></i> </a> </li> </c:if> <c:forEach begin="${pageMaker.startPage}" end="${pageMaker.endPage}" var="num"> <li class="page-item ${cri.pageNum == num ? 'active' : '' }"> <a class="page-link" href="${cri.getLink(num)}">${num}</a> </li> </c:forEach> <c:if test="${pageMaker.next}"> <li class="page-item"> <a class="page-link" href="${cri.getLink(pageMaker.endPage+1)}"> <i class="fa-solid fa-angle-right"></i> </a> </li> </c:if> <c:if test="${pageMaker.cri.pageNum < pageMaker.totalPage}"> <li class="page-item"> <a class="page-link" href="${cri.getLink(pageMaker.totalPage)}"> <i class="fa-solid fa-forward-step"></i> </a> </li> </c:if> </ul>