ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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>

     

Designed by Tistory.