ㅁ 마이바티스의 핵심적인 특징 중의 하나가 동적쿼리다.
주로 검색 서비스 쪽에서 많이 사용된다.
ㅁ main.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<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"%>
<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>
- 서버키고 접속해서 번호, 이름, 아이디로 검색하면 잘 나온다.