본문 바로가기
MyBatis

[MyBatis] 동적쿼리 연습 (회원 검색)

by moca7 2024. 10. 14.

 

 

 

ㅁ 마이바티스의 핵심적인 특징 중의 하나가 동적쿼리다.

주로 검색 서비스 쪽에서 많이 사용된다.

 

 

 

ㅁ main.jsp

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

    <h3>1. insert연습 (공지사항 등록)</h3>
    <a href="${ contextPath }/regist.no">공지사항 등록페이지로 이동</a>

    <h3>2. select연습 - 한 행 (공지사항 상세조회)</h3>
    <form action="${ contextPath }/detail.no">
        검색할 글번호 : <input type="text" name="noticeNo">
        <button type="submit">검색</button>
    </form>

    <h3>3. select연습 - 여러행 (공지사항 전체목록조회)</h3>
    <a href="${ contextPath }/list.no">공지사항 전체 목록 페이지로 이동</a>

    <h4>4. select연습 - 여러행 + 페이징 처리 - RowBounds 적용</h4>
    <a href="${ contextPath }/list.no?page=1">공지사항 목록 페이지(페이징처리)로 이동</a>


    <h5>5. select연습 + update연습 (회원정보 상세조회 + 회원정보 변경) - TypeHandler 적용</h5>
    <form action="${ contextPath }/detail.me">
        검색할 회원번호 : <input type="text" name="userNo">
        <button type="submit">검색</button>
    </form>
   
   
    <h6>6. 동적쿼리 연습 (회원 검색)</h6>
    <form action="${ contextPath }/search.me">
        <select name="condition">
            <option value="no">번호</option>
            <option value="name">이름</option>
            <option value="id">아이디</option>
        </select>

        <input type="text" name="keyword">
        <button type="submit">검색</button>
    </form>
 

</body>
</html>
 

 

 

- "6. 동적쿼리 연습(회원 검색)" 부분을 추가한다.

- 사용자로부터 검색 조건과 검색 키워드를 입력받아 서블릿으로 요청하면서 넘긴다.

 

 

 

 

 

ㅁ com.br.mybatis.member.controller에 MemberSearchController 서블릿을 만든다.

 

 
package com.br.mybatis.member.controller;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.br.mybatis.member.dto.MemberDto;
import com.br.mybatis.member.service.MemberServiceImpl;

/**
 * Servlet implementation class MemberSearchController
 */
@WebServlet("/search.me")
public class MemberSearchController extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public MemberSearchController() {
        super();
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String condition = request.getParameter("condition"); // no | name | id
        String keyword = request.getParameter("keyword");

        Map<String, Object> map = new HashMap<>();
        map.put("condition", condition);
        map.put("keyword", keyword);
        // map의 형태 {condition=no|name|id, keyword=us}
       
        List<MemberDto> searchList = new MemberServiceImpl().selectSearchList(map);

       
        request.setAttribute("list", searchList);
        request.getRequestDispatcher("/WEB-INF/views/member/memberList.jsp").forward(request, response);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}
 

 

 

- map의 형태는 이렇다. {condition=no|name|id, keyword=us}

- condition이라는 key에 대한 value값에 따라서 where절(조건)이 매번 달라지기 때문에 동적쿼리라고 한다.

- 2개의 데이터(condition, keyword)가 쿼리문을 완성시키기 위해 필요하다.

 

 

 

 

 

※ 그런데 MemberDao에서 쿼리문으로 넘길 수 있는 값은 한 개뿐이다.

 
package com.br.mybatis.member.dao;

import org.apache.ibatis.session.SqlSession;

import com.br.mybatis.member.dto.MemberDto;

public class MemberDao {

    public MemberDto selectMember(SqlSession sqlSession, int userNo) {
        return sqlSession.selectOne("memberMapper.selectMember", userNo);
    }

    public int updateMember(SqlSession sqlSession, MemberDto m) {
        return sqlSession.update("memberMapper.updateMember", m);
    }

}

 

 

매개변수 갯수가 정해져있다.

 

 

- 여러개의 데이터를 넘기지 못하니 직접 넘길 값들이 담길 dto를 만들든지, 아니면 (일회용 dto같은)Map을 쓰면 된다. 

Map도 결국 키밸류 세트로 데이터를 담기 때문에 dto에 키밸류를 담는것처럼 사용할 수 있다.

- 데이터 가공은 주로 컨트롤러에서 한다.

 

 

 

 

 

 

ㅁ MemberService 인터페이스에 회원 검색 관련 메소드 만들기

 

 
package com.br.mybatis.member.service;

import java.util.List;
import java.util.Map;

import com.br.mybatis.member.dto.MemberDto;

public interface MemberService {

   
    // 인터페이스에서 public abstract를 붙이지 않아도 모든 메서드는 자동으로 추상 메서드로 취급됩니다.
   
   
    // 회원정보 상세조회
    MemberDto selectMember(int userNo);
   
    // 회원정보 변경
    int updateMember(MemberDto m);
   
    // 회원 검색
    List<MemberDto> selectSearchList(Map<String, Object> map);
   
   
}
 

 

 

 

 

 

ㅁ MemberServiceImpl에 selectSearchList 메소드 생성

 

 
package com.br.mybatis.member.service;

import org.apache.ibatis.session.SqlSession;

import static com.br.mybatis.common.template.Template.*;

import java.util.List;
import java.util.Map;

import com.br.mybatis.member.dao.MemberDao;
import com.br.mybatis.member.dto.MemberDto;

public class MemberServiceImpl implements MemberService {

    private MemberDao memberDao = new MemberDao();
   
   
    @Override
    public MemberDto selectMember(int userNo) {
        SqlSession sqlSession = /*Template.*/getSqlSession();
        MemberDto m = memberDao.selectMember(sqlSession, userNo);
       
        sqlSession.close();
        return m;
    }

    @Override
    public int updateMember(MemberDto m) {
        SqlSession sqlSession = getSqlSession();
        int result = memberDao.updateMember(sqlSession, m);
       
        if(result > 0) {
            sqlSession.commit();
        }
       
        sqlSession.close();
        return result;
    }

    @Override
    public List<MemberDto> selectSearchList(Map<String, Object> map) {
        SqlSession sqlSession = /*Template.*/getSqlSession();
        List<MemberDto> list = memberDao.selectSearchList(sqlSession, map);
       
        sqlSession.close();
        return list;
    }

}

 

 

 

 

 

ㅁ MemberDao에 selectSearchList 메소드 생성

 

 
package com.br.mybatis.member.dao;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.session.SqlSession;

import com.br.mybatis.member.dto.MemberDto;

public class MemberDao {

    public MemberDto selectMember(SqlSession sqlSession, int userNo) {
        return sqlSession.selectOne("memberMapper.selectMember", userNo);
    }

    public int updateMember(SqlSession sqlSession, MemberDto m) {
        return sqlSession.update("memberMapper.updateMember", m);
    }

    public List<MemberDto> selectSearchList(SqlSession sqlSession, Map<String, Object> map) {
        return sqlSession.selectList("memberMapper.selectSearchList", map);
    }

}
 

 


- keyword검색이어서 검색 결과가 여러 행일 수 있다. 그래서 selectOne이 아닌 selectList 메소드를 사용한다.

 

 

 

 

 

 

 

ㅁ member-mapper.xml에 select 태그를 생성한다.

 

 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<mapper namespace="memberMapper">

    <resultMap id="memberResultMap" type="MemberDto">
        <result column="user_no" property="userNo" />
        <result column="user_id" property="userId" />
        <result column="user_name" property="userName" />
        <result column="phone" property="phone" />
        <result column="email" property="email" />
        <result column="address" property="address" />
        <result column="interest" property="interest" typeHandler="StringArrTypeHandler"/>
    </resultMap>


    <select id="selectMember" resultMap="memberResultMap">
        select
                user_no
              , user_id
              , user_name
              , phone
              , email
              , address
              , interest
          from  member
         where  status in ('U', 'A')
           and  user_no = #{ userNo }          
    </select>


    <update id="updateMember">
        update
                member
           set  
                user_name = #{userName}    
              , phone = #{phone}    
              , email = #{email}
              , address = #{address}
              , interest = #{interest, typeHandler=StringArrTypeHandler}
         where user_no = #{userNo}    
    </update>


    <select id="selectSearchList" resultMap="memberResultMap">
        select
                user_no
              , user_id
              , user_name
              , address
          from  member
         where  status in ('U', 'A')    
         <choose>
            <when test="condition == 'no'">
                and user_no like '%' || #{keyword} || '%'
            </when>

            <when test='condition == "name"'>
                and user_name like '%' || #{keyword} || '%'
            </when>

            <otherwise>
                and user_id like '%${keyword}%'  
            </otherwise>
         </choose>
    </select>

</mapper>
 
 

 

 

 

- "memberResultMap"이라는 resultMap을 재사용한다.

 

- where 뒤에 붙는 조건이 매번 다르다.

(1) 번호로 검색했을 경우, and user_no like '%' || 검색어 || '%'

(2) 이름으로 검색했을 경우, and user_name like '%' || 검색어 || '%'

(3) id로 검색했을 경우, and user_id like '%' || 검색어 || '%'

 

 

 

- 마이바티스를 쓰면 동적 쿼리문을 MyBatis의 Mapper 파일에서 작성할 수 있다.

MyBatis에서는 <if>, <choose>, <when>, <otherwise>와 같은 태그를 사용하여 SQL문 내에서 조건 처리를 직접 할 수 있다.

 

- 마이바티스를 쓰지 않으면 mapper 파일에서 조건문을 쓸 수 없다.

sql문을 dao단으로 가져와서 dao단에서 조건검사를 하고 자바코드로 sql문에 추가적인 조건을 붙였어야 했다.

 

 

 

 

- <when> 태그의 test 속성값으로 조건을 작성할 때 바깥이 쌍따옴표면 'no', 홑따옴표면 "no"로 작성한다.

EL은 쌍따옴표와 홑따옴표를 구분하지 않는다.

 

- # { } 안이 문자열일 경우 ' '가 붙어서 나온다.

#{keyword}도 문자열이니까 홑따옴표로 감싸져서 나온다.

 

 

- 마이바티스 쿼리에서 JSTL과 달리 when의 test 속성 값에 EL 구문을 쓰지 않고, 그냥 condition만 작성해도 condition이 꺼내진다. 

- 쿼리문이 실행될 때 dao에서 map이 전달된다. map에서 codition이라는 키값에 해당하는 밸류값이 뽑혀져서 쿼리문이 완성된다. 

 

- 위의 쿼리는 else if, else로 처리했고 단일 조건이면 if 태그 써도 된다. 

 

 

- <otherwise> 부분. || 안쓰고 작성하기.

#{keyword} 대신 ${keyword}도 사용할 수 있다. ${ }는 문자열이어도 따옴표없이 채워진다.

'%?%' -> '%${keyword}%' 

 

 

 

 

 

 

 

ㅁ 마이바티스 공식 사이트

- 왼쪽의 메뉴바에 "동적 SQL"이 있다.

 

 

 

- 엘리먼트 = 태그다. JSTL의 태그랑 비슷하다.

- jstl과의 차이점은 앞에 "c:"이라는 접두어가 붙지 않고, test 조건문 부분에 el구문으로 작성하지 않는다.

- jstl에서 조건식은 무조건 el구문으로 작성한다.

 

 

 

 

 

 

ㅁ memberList.jsp를 만든다.

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>


    <h3>회원 검색 결과</h3>

    <c:choose>
        <c:when test="${ empty list }">
            검색 결과가 없습니다.
        </c:when>
       
        <c:otherwise>
            <table border="1">
                <thead>
                    <tr>
                        <th>번호</th>
                        <th>아이디</th>
                        <th>이름</th>
                        <th>주소</th>
                    </tr>
                </thead>
                <tbody>
                    <c:forEach var="m" items="${ list }">
                        <tr>
                            <td>${ m.userNo }</td>
                            <td>${ m.userId }</td>
                            <td>${ m.userName }</td>
                            <td>${ m.address }</td>
                        </tr>
                    </c:forEach>
                </tbody>
            </table>
        </c:otherwise>
    </c:choose>

</body>
</html>
 

 

 

 

 

 

 

 

 

 

 

 

 

- 서버키고 접속해서 번호, 이름, 아이디로 검색하면 잘 나온다.