날아라쩡글이의 블로그입니다.

다이나믹쿼리 (resultMap) 본문

중앙 HTA (2106기) story/spring java framwork story

다이나믹쿼리 (resultMap)

날아라쩡글이 2022. 1. 4. 14:04
728x90
반응형

opt와value의 값이 하나도 안들어가면 where이 없도록 동적인 다이나믹쿼리를 해주는 것이 where태그이다. 
where 태그를 이용해서 여러 값을 result해볼예정이다. 

  • pagination을 입력하자 
    요청 URL : localhost/book/list.do 
                 localhost/book/list.do?page= ? 이러면 page가 값이 없을 때 error가 발생한다. 
  • page가 null이거나 null이 아닐 때는 어떻게 해야할까? 
    • @GetMapping("/list.do")
      public String list(@RequestParam(name = "page", defaultValue = "1", required = false) String page,
      Criteria criteria ,Model model) {
    • @RequestParam(name="요청파라미터이름",required=필수요청파라미터의 여부, defultValue="기본값");
      • name = 요청파라미터의 이름이다. 
      • required = 필수요청파라미터인지의 여부, true/false의 값만 가능하다. 기본값은 tru이다. 
      • @RequestParam의 설정없이 사용한 요청 파라미터용 매개변수는 전부 required가 true인 상태다.
      • defaultValue = required가 false로 설정되어 있고, name에서 지정한 이름의 요청 파라미터가 존재하지 않을 때 변수에 대입한 기본값이다. 
    • <select id="searchBooks" parameterType="com.sample.form.Criteria" resultMap="BookResultMap">
         select *
         from (
         select A.*, row_number() over(order by book_no desc) rn
         from sample_spring_books A
         <where>
          <if test="opt != null and value != null">
          <!-- 다이나믹 쿼리, 반응형 쿼리  -->
          <choose>
          <when test="opt == '제목'">
          book_title like '%' || #{value} || '%'
          </when>
          <when test="opt == '저자'">
          book_author = #{value}
          </when>
          <when test="opt == '출판사'">
          book_publisher = #{value}
          </when>
          <when test="opt == '최소가격'">
          book_price >= to_number(#{value})
          </when>
          <when test="opt == '최대가격'">
          book_price &lt; = to_number(#{value}) <!-- < -->
          </when>
          </choose>
          </if>
          </where>
         )
          where rn between #{beginIndex} and #{endIndex}
          <!-- Criteria에 beginIndex과 endIndex가 있어야한다. -->
        </select>
    • 이렇게 where절을 from바로 아래에 적어서 사용할 수 있다.
    • 이부분은 pagination을 구하기 위하여 from절에 적어서 인라인뷰 쿼리로 where절을 사용할 수 있다. 
    • 만약 넘어오는 값이 없다면, where절은 없다고 인식이 된다. 
  • 이렇게 작성하다보면 select문이 여러개로 복잡화가 되는 것을 알 수 있다.
  • 원래는 select문으로 전체적으로 작성하지만 resultMap으로 작성하면 좀 더 복잡성이 줄어들도록 작성할 수 있다. 
    • xml의 문서에는 20~30개의 select문이 존재한다. 그래서 resultMap을 도입을 하여, column과 property를 지정하여 쿼리가 반복적이니, 명시하지 않고 *으로 해결하도록 작성한것이다. 
  • ResultMap : mapping시켜놓고, 연결을 지어놓았다. 
  • ResultType : classType이나 기본자료형의 값이 작성이 되야한다.
  • <resultMap id="BookResultMap" type="com.sample.vo.Book" >
    <!-- primarykey는 id라고 적는다.  -->
    <id column="book_no" property="no"/>
    <result column="book_title" property="title"/>
    <result column="book_author" property="author"/>
    <result column="book_publisher" property="publisher"/>
    <result column="book_price" property="price"/>
    <result column="book_discount_price" property="discountPrice"/>
    <result column="book_pub_date" property="pubDate"/>
    <result column="book_stock" property="stock"/>
    <result column="book_updated_date" property="updatedDate"/>
    <result column="book_created_date" property="createdDate"/>
    </resultMap>
  • <select />태그에서 정의된 resultMap을 사용하기 
    • select id="searchBooks" parameterType="com.sample.form.Criteria" resultMap="BookResultMap">
      <select id="getAllBooks" resultMap ="BookResultMap">
      selct * 
      from sample_spring_books
      </select> 
    • resultMap은 select문으로 조회된 데이터의 컬럼명과 resultType에서 지정한 클래스의 프로퍼티명이 서로 다를 때 컬럼과 프로퍼티를 mapping시킨다.
    • id 태그에는 primary key에 해당하는 컬럼을 정의한다.
    • 조회결과를 담기 위해서 resultMap을 사용할 때는 반드시 resultMap에서 매핑하는 컬럼이 조회결과에 모두 포함되어 있어야한다. 
    • select를 현재 column이 10개니까 10개를 select할 때만 사용할 수 있다. 
    • mybatis는 정의된 BookResultMap을 참조해서 type에 명시된 com.sample.vo.Book객체를 생성한다. 
    • mybatis는 조회결과의 컬럼이름과 Book객체의 멤버변수에 저장해야할지를 <result />태그의 정보를 참조한다. 
  • 페이지네이션 입력하기 

    •    select *
         from (
         select A.*, row_number() over(order by book_no desc) rn
         from sample_spring_books A
         <where>
          <if test="opt != null and value != null">
          <!-- 다이나믹 쿼리, 반응형 쿼리  -->
          <choose>
          <when test="opt == '제목'">
          book_title like '%' || #{value} || '%'
          </when>
          <when test="opt == '저자'">
          book_author = #{value}
          </when>
          <when test="opt == '출판사'">
          book_publisher = #{value}
          </when>
          <when test="opt == '최소가격'">
          book_price >= to_number(#{value})
          </when>
          <when test="opt == '최대가격'">
          book_price &lt; = to_number(#{value}) <!-- < -->
          </when>
          </choose>
          </if>
          </where>
         )
          where rn between #{beginIndex} and #{endIndex}

    • 위에서 이렇게 설정한 것을 확인 할 수 있다. 
    •  parameterType으로 온 com.sample.Criteria를 설정한다. 
      beginIndex와 endIndex을 넣고 setter/getter 를 지정한다. 
    • pagination의 파일을 넣고, controller의 값을 정의한다. 
      • // 검색조건에 해당하는 총 데이터 갯수 조회
        int totalRecords = bookService.getTotalRows(criteria);
        //그래서 책의 조건의 총갯수를 찾을 때에도 opt&value의 값을 찾으러 들어간다. 
        // 현재 페이지 번호와 총 데이터 갯수를 전달해서 페이징 처리에 필요한 정보를 제공하는 pagination객체를 생성한다. 
        Pagination pagination = new Pagination(page, totalRecords);

        //요청한 페이지에 대한 조회범위를 criteria에 저장
        criteria.setBeginIndex(pagination.getBegin());
        criteria.setEndIndex(pagination.getEnd());
        //검색조건(opt, value)와 조회범위(beginIndex, endIndex)가 포함된 criteria를 서비스에 전달해서 데이터 조회
        List<Book> books = bookService.searchBook(criteria);

        model.addAttribute("books",books);
        model.addAttribute("pagination", pagination);
    • Dao와 service에도 무조건 넣어준다.
    • int getBookTotalRows(Criteria criteria); -->총 입력값에 대한 dao -> where의 설정에 따라서 값이 다르게 나오는데?! 이럴때 where태그를 생각하고 입력한다. 
  • <select id="getBookTotalRows" parameterType="com.sample.form.Criteria" resultType="int">
    select count(*)
    from sample_spring_books
    <where>
    <if test="opt != null and value != null">
     <!-- 다이나믹 쿼리, 반응형 쿼리  -->
        <choose>
        <when test="opt == '제목'">
        book_title like '%' || #{value} || '%'
        </when>
        <when test="opt == '저자'">
        book_author = #{value}
        </when>
        <when test="opt == '출판사'">
        book_publisher = #{value}
        </when>
        <when test="opt == '최소가격'">
        book_price >= to_number(#{value})
        </when>
        <when test="opt == '최대가격'">
        book_price &lt; = to_number(#{value}) <!-- < -->
        </when>
        </choose>
      </if>
      </where> 
      <!-- 총 갯수에 대해서 다이나믹 쿼리를 짠다. -->
     </select>
  • public int getTotalRows(Criteria criteria) {
    return bookDao.getBookTotalRows(criteria);
    }
  • criteria내의 시작과 끝페이지가 setter/getter로 들어가있는 것을 기억하자 
  • 그리고 list.jsp로와서 페이지네이션을 설정한다. 
  • <c:if test="${pagination.totalRecords gt 0 }">
       <div class="row mb-3">
       <div class="col">
       <nav aria-label="Page navigation example">
       <!-- pagin을 담아두었음 사용할 수 있다.  -->
      <ul class="pagination justify-content-center">
      <!-- isExistPrev() 이전 블록 존재여부를 반환한다.  -->
        <li class="page-item">
         <a class="page-link ${pagination.existPrev ? '' : 'disabled' }" href="list.do=${pagination.prevPage }" data-page=${pagination.prevPage }>이전</a></li>
        <c:forEach var="num" begin="${pagination.beginPage }" end="${pagination.endPage }">
        <li class="page-item ${pagination.pageNo eq num ? 'active' : '' } ">
        <!-- pagination의 현재페이지를 출력하는데 num과 동일하면 active를 추가한다.  -->
        <a class="page-link" href="list.do?page=${num }" data-page=${num }>${num }</a></li>
        </c:forEach>
        <li class="page-item ${pagination.existNext ? '' : 'disabled' }">
         <a class="page-link" href="list.do=${pagination.nextPage }" data-page=${pagination.nextPage }>다음</a></li>
      </ul>
    </nav>
       </div>
       </div>
       </c:if>
  • 그리고 이벤트를 거는 것이다.  
  • 숫자를 클릭하면 opt와 value의 값이 함께 나오도록 submit이되도록 버튼을 입력하고 이전 버튼의 이벤트 핸들러 submit을 재정의한다.
  • //페이지네이션의 링크를 클릭했을 때 실행될 이벤트 핸들러 함수를 등록한다.
    $('.pagination a').click(function(event){
    event.preventDefault(); //form태그와 a태그만 막을 수 있음. 이뤄지는 이벤트가 실행되지 않게 함
    //클릭한 페이지내비게이션의 페이지번호 조회하기
    var pageNo = $(this).attr("data-page");
    //검색폼의 히든필드에 클릭한 페이지네비게이션의 페이지 번호 설정
    $(':input[name=page]').val(pageNo);
    //검색폼을 제출시키기 submit시키기
    $('#form-search-book').trigger('submit');
    }) 
  • //검색버튼을 클릭했을 때 실행될 이벤트 핸들러 함수를 등록한다.
    $("#btn-search-book").click(function(){
    //검색옵션 값과 입력값을 조회한다. 
    var opt = $("select[name=opt]").val();
    var value = $.trim($(":input[name=value]").val());
    //검색옵션 값과 입력값이 모두 존재하면 페이지 번호를 1설정하고, 폼에서 onsubmit이벤트를 발생시켜서 폼입력값이 서버로 제출되게 한다.
    if(opt && value) {
    $(':input[name=page]').val("1");
    $('#form-search-book').trigger('submit');
    } else {
    alert("검색조건 혹은 검색어를 입력하세요 ");
    }
    });
  • 먼저 검색기능을 코딩하고, pagination의 코딩을 하지 않을 경우 변경을 해야하는 경우가 생긴다. 
  • 만능이 되는 함수를 만들지 많아야하며, 유지보수가 어렵기때문에 
    객체지향원칙중 단일책임의 원칙처럼 만능이 아닌 한가지에 책임이 될 수 있도록 쿼리를 작성해야한다. 
반응형
Comments