ㅁ 공지사항 서비스는 이제 끝났고 회원 서비스를 한다.
ㅁ 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>
</body>
</html>
- "5. select연습 + update연습 (회원정보 상세조회 + 회원정보 변경) - TypeHandler 적용" 부분을 작성했다.
ㅁ com.br.mybatis.member.controller에 MemberDetailController 서블릿을 만든다.
package com.br.mybatis.member.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class MemberDetailController
*/
@WebServlet("/detail.me")
public class MemberDetailController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public MemberDetailController() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int userNo = Integer.parseInt(request.getParameter("userNo"));
MemberDto m = new MemberServiceImpl().selectMember(userNo);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- 완성된 상태는 아니다.
ㅁ com.br.mybatis.member.service에 인터페이스 MemberService를 만든다.
package com.br.mybatis.member.service;
import com.br.mybatis.member.dto.MemberDto;
public interface MemberService {
// 인터페이스에서 public abstract를 붙이지 않아도 모든 메서드는 자동으로 추상 메서드로 취급됩니다.
// 회원정보 상세조회
MemberDto selectMember(int userNo);
// 회원정보 변경
int updateMember(MemberDto m);
// 회원 검색
}
ㅁ com.br.mybatis.member.service에 인터페이스 MemberService를 구현하는 MemberServiceImpl 클래스를 만든다
package com.br.mybatis.member.service;
import org.apache.ibatis.session.SqlSession;
import static com.br.mybatis.common.template.Template.*;
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) {
return 0;
}
}
ㅁ com.br.mybatis.member.dao에 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);
}
}
ㅁ mybatis-config.xml에 별칭을 등록한다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
<configuration>
<settings>
<setting name="jdbcTypeForNull" value="NULL" />
</settings>
<typeAliases>
<typeAlias type="com.br.mybatis.notice.dto.NoticeDto" alias="NoticeDto" />
<typeAlias type="com.br.mybatis.member.dto.MemberDto" alias="MemberDto" />
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
<property name="username" value="SERVER" />
<property name="password" value="SERVER" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/notice-mapper.xml" />
<mapper resource="mappers/member-mapper.xml" />
</mappers>
</configuration>
- typeAliases 태그에 MemberDto의 별칭을 등록한다.
ㅁ member-mapper.xml
<?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" />
</resultMap>
<select id="selectMember" resultType|Map="">
select
user_no
, user_id
, user_name
, phone
, email
, address
, interest
from member
where status in ('U', 'A')
and user_no = #{ userNo }
</select>
</mapper>
- resultMap은 mapper 파일 상단에 다 몰아서 작성하는 것이 좋다.
- 숫자든 문자열이든 상관없이, MyBatis에서 SQL 쿼리문에 변수를 바인딩할 때는 #{ }를 사용해야 한다.
- ${ }는 단순히 문자열을 대체하는 방식이므로, PreparedStatement 방식처럼 ?에 값을 바인딩하지 못한다.
ㅁ 이전에 만든 MemberDto를 살펴본다.
package com.br.mybatis.member.dto;
import java.sql.Date;
import java.util.Arrays;
public class MemberDto {
private int userNo;
private String userId;
private String userPwd;
private String userName;
private String phone;
private String email;
private String address;
private String[] interest;
private Date enrollDate;
private Date modifyDate;
private String status;
public MemberDto() {
super();
}
public MemberDto(int userNo, String userId, String userPwd, String userName, String phone, String email,
String address, String[] interest, Date enrollDate, Date modifyDate, String status) {
super();
this.userNo = userNo;
this.userId = userId;
this.userPwd = userPwd;
this.userName = userName;
this.phone = phone;
this.email = email;
this.address = address;
this.interest = interest;
this.enrollDate = enrollDate;
this.modifyDate = modifyDate;
this.status = status;
}
public int getUserNo() {
return userNo;
}
public void setUserNo(int userNo) {
this.userNo = userNo;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserPwd() {
return userPwd;
}
public void setUserPwd(String userPwd) {
this.userPwd = userPwd;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String[] getInterest() {
return interest;
}
public void setInterest(String[] interest) {
this.interest = interest;
}
public Date getEnrollDate() {
return enrollDate;
}
public void setEnrollDate(Date enrollDate) {
this.enrollDate = enrollDate;
}
public Date getModifyDate() {
return modifyDate;
}
public void setModifyDate(Date modifyDate) {
this.modifyDate = modifyDate;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
@Override
public String toString() {
return "MemberDto [userNo=" + userNo + ", userId=" + userId + ", userPwd=" + userPwd + ", userName=" + userName
+ ", phone=" + phone + ", email=" + email + ", address=" + address + ", interest="
+ Arrays.toString(interest) + ", enrollDate=" + enrollDate + ", modifyDate=" + modifyDate + ", status="
+ status + "]";
}
}
- DB에서 INTEREST 컬럼값은 컴마라는 구분자로 여러개의 문자들이 연결된 하나의 문자열로 저장되어 있다.
이 하나의 문자열을 MemberDto의 String[] 타입의 interest 필드에 담으려 한다.
- 그러려면 형변환 과정이 필요하다. MyBatis에서 TypeHandler로 가능하다.
TypeHandler 기술을 이용하려면 MyBatis에서 제공하는 타입핸들러 인터페이스를 구현하는 클래스를 만들어야 한다.
ㅁ com.br.mybatis.common.handler에 StringArrTypeHandler라는 클래스를 만든다.
- add에서 인터페이스 TypeHandler를 검색해서 어떤 인터페이스를 구현하는 클래스로 둘 지 결정한다.
- 바로 finish하지 말고, interfaces 부분에서 수정이 가능하다.
- T(Type의 약자) 라고 되어있는 제네릭 타입이 반환형이다.
- T대신 변환시키고 싶은 String[] 타입을 적는다.
- String을 String[]로, String[]을 String으로 변환하는 코드를 작성할 예정이다.
- String -> String[] 변환뿐만 다른 타입으로의 변환도 가능하다.
직접 형변환하는 자바코드를 쓸거라 다른 타입으로의 형변환도 가능하다.
package com.br.mybatis.common.handler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
public class StringArrTypeHandler implements TypeHandler<String[]> {
@Override
public String[] getResult(ResultSet rs, String columnName) throws SQLException {
String resultDB = rs.getString(columnName); // "낚시,운동,게임"
return resultDB == null ? null : resultDB.split(","); // ["낚시", "운동", "게임"]
}
@Override
public String[] getResult(ResultSet rs, int columnIndex) throws SQLException {
return null;
}
@Override
public String[] getResult(CallableStatement cs, int columnIndex) throws SQLException {
return null;
}
@Override
public void setParameter(PreparedStatement ps, int i, String[] parameter, JdbcType jdbcType) throws SQLException {
}
}
- TypeHandler 인터페이스를 구현하는 구현 클래스로 만들어졌다.
- getResult 메소드 중에서 CallableStatemenet는 쓰지 않을거고, ResultSet을 매개변수로 쓰는 두 메소드를 수정한다.
매개변수의 ResultSet은 우리가 아는 조회결과가 담겨 있는 ResultSet이다.
- ResultSet에서 컬럼값을 뽑을 때 보통 컬럼명을 데이터를 뽑았는데 컬럼의 순번으로도 뽑을 수 있었다.
첫번째 메소드는 컬럼명이, 두번째 메소드는 컬럼 순번이 매개변수로 선언되어 있다.
- DB로부터 ResultSet 객체에 getXXX("컬럼명" | 컬럼순번)으로 조회된 컬럼값을 뽑아서, 원하는 타입으로 형변환한다.
- interest 컬럼에서 조회된 값을 뽑아서 String[]로 바꿀 예정이다.
- 그런데 interest 컬럼은 null 허용이라 그냥 split하면 null pointer exception 발생한다.
삼항연산자를 사용해서 null이면 split 메소드를 호출하지 않고 null을 반환한다.
ㅁ 별칭등록
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
<configuration>
<settings>
<setting name="jdbcTypeForNull" value="NULL" />
</settings>
<typeAliases>
<typeAlias type="com.br.mybatis.notice.dto.NoticeDto" alias="NoticeDto" />
<typeAlias type="com.br.mybatis.member.dto.MemberDto" alias="MemberDto" />
<typeAlias type="com.br.mybatis.common.handler.StringArrTypeHandler" alias="StringArrTypeHandler" />
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
<property name="username" value="SERVER" />
<property name="password" value="SERVER" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mappers/notice-mapper.xml" />
<mapper resource="mappers/member-mapper.xml" />
</mappers>
</configuration>
- StringArrTypeHandler 구현 클래스의 별칭을 등록한다.
ㅁ 다시 member-mapper.xml
<?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>
</mapper>
- resultMap의 interest 컬럼에 typeHandler 속성을 추가한다.
- select 태그에 resultMap 속성을 추가한다.
- TypeHandler 인터페이스를 구현하는 클래스에는 3개의 getResult 메소드가 있다.
interest 컬럼에 typeHandler 속성을 추가하면 타입핸들러가 동작한다.
- "interest"라는 컬럼으로 조회된 값이 MemberDto 객체의 "interest" 필드에 담길 때 타입핸들러의 getResult 메소드가 자동으로 작동한다.
- <resultMap> 태그 중 typeHandler 속성이 있는 <result> 태그의 column 속성값이
TypeHandler를 구현하는 클래스의 getResult(ResultSet rs, String columnName) 메소드의 매개변수로 전달된다.
- getResult 메소드에서 ResultSet객체.getString(columnName)으로 컬럼값을 뽑아서 split(",") 메소드로 String[]로 변환한 다음 반환한다.
- String[]로 결과가 반환되면 이 반환값이 MemberDto의 "interest" 필드에 담긴다.
ㅁ 컨트롤러를 완성한다.
package com.br.mybatis.member.controller;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Servlet implementation class MemberDetailController
*/
@WebServlet("/detail.me")
public class MemberDetailController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public MemberDetailController() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int userNo = Integer.parseInt(request.getParameter("userNo"));
MemberDto m = new MemberServiceImpl().selectMember(userNo);
request.setAttribute("m", m);
request.getRequestDispatcher("WEB-INF/views/member/myInfo.jsp").forward(request, response);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
ㅁ typeHandler가 필수는 아니다.
- typeHandler 없이 화면단에서든 자바단에서든 split으로 나눠도 된다.
ㅁ myInfo.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 m }">
조회된 회원이 없습니다
</c:when>
<c:otherwise>
<form>
회원번호 : <input type="text" value="${ m.userNo }" readonly> <br>
아이디 : <input type="text" value="${ m.userId }" readonly> <br>
이름 : <input type="text" value="${ m.userName }"> <br>
전화번호 : <input type="text" value="${ m.phone }"> <br>
이메일 : <input type="text" value="${ m.email }"> <br>
주소 : <input type="text" value="${ m.address }"> <br>
관심분야 :
<input type="checkbox" name="interest" value="운동">운동
<input type="checkbox" name="interest" value="등산">등산
<input type="checkbox" name="interest" value="낚시">낚시
<input type="checkbox" name="interest" value="요리">요리
<input type="checkbox" name="interest" value="게임">게임
<input type="checkbox" name="interest" value="영화">영화
<script>
$(function(){
let interest = '${ fn:join(m.interest, ",") }'; // '등산,요리,게임' <- 배열을 하나의 문자열로 만듦.
$(":checkbox").each(function(){ // db로부터 이 회원의 interest를 가져와서 해당하는 체크박스에 체크해서 보여주기.
if(interest.search($(this).val()) != -1){
$(this).prop("checked", true);
}
})
})
</script>
</form>
</c:otherwise>
</c:choose>
</body>
</html>
- EL 구문은 null 체크 안해도 됨. null일 경우 그냥 빈문자열이 출력된다.
- jQuery 사용을 위한 jQuery 라이브러리 코드 구문 1줄을 05_jspServlet-workspace의 header.jsp에서 가져온다.
- functions 라이브러리를 사용하기 위한 taglib 지시어를 상단에 작성한다.
el 구문 내에서 쓸수있는 함수를 제공해주는 라이브러리다.
- 자바에서의 String[]에 담겨있는 값들을 하나의 문자열로 합쳐주는 함수를 functions 라이브러리에서 제공한다.
fn:join(m.interest, ",")
- 자바스크립트에서 문자열로 인식하기 위해서는 '${ fn:join(m.interest, ",") }'로 감싸줘야 한다.
- jQuery의 each 메소드로 각 체크박스 요소에 순차적으로 접근한다.
================================================================================
[ update ]
ㅁ myInfo.jsp에 버튼을 두고 input에 name을 둔다
<%@ 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 m }">
조회된 회원이 없습니다
</c:when>
<c:otherwise>
<form action="${ contextPath }/update.me" method="post">
회원번호 : <input type="text" name="userNo" value="${ m.userNo }" readonly> <br>
아이디 : <input type="text" value="${ m.userId }" readonly> <br>
이름 : <input type="text" name="userName" value="${ m.userName }"> <br>
전화번호 : <input type="text" name="phone" value="${ m.phone }"> <br>
이메일 : <input type="text" name="email" value="${ m.email }"> <br>
주소 : <input type="text" name="address" value="${ m.address }"> <br>
관심분야 :
<input type="checkbox" name="interest" value="운동">운동
<input type="checkbox" name="interest" value="등산">등산
<input type="checkbox" name="interest" value="낚시">낚시
<input type="checkbox" name="interest" value="요리">요리
<input type="checkbox" name="interest" value="게임">게임
<input type="checkbox" name="interest" value="영화">영화
<button type="submit">정보변경</button>
<script>
$(function(){
let interest = '${ fn:join(m.interest, ",") }'; // '등산,요리,게임' <- 배열을 하나의 문자열로 만듦.
$(":checkbox").each(function(){ // db로부터 이 회원의 interest를 가져와서 해당하는 체크박스에 체크해서 보여주기.
if(interest.search($(this).val()) != -1){
$(this).prop("checked", true);
}
})
})
</script>
</form>
</c:otherwise>
</c:choose>
</body>
</html>
ㅁ MemberUpdateController 서블릿을 만든다.
package com.br.mybatis.member.controller;
import java.io.IOException;
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 MemberUpdateController
*/
@WebServlet("/update.me")
public class MemberUpdateController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public MemberUpdateController() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
MemberDto m = new MemberDto();
m.setUserNo(Integer.parseInt(request.getParameter("userNo")));
m.setUserName(request.getParameter("userNo"));
m.setPhone(request.getParameter("phone"));
m.setEmail(request.getParameter("email"));
m.setAddress(request.getParameter("address"));
m.setInterest(request.getParameterValues("interest")); // MemberDto 객체의 setInterest메소드가 String 배열을 받는다.
int result = new MemberServiceImpl().updateMember(m);
if(result > 0) {
System.out.println("수정 성공");
response.sendRedirect(request.getContextPath());
}else {
System.out.println("수정 실패");
}
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
ㅁ MemberServiceImpl에 updateMember 메소드 만들기
package com.br.mybatis.member.service;
import org.apache.ibatis.session.SqlSession;
import static com.br.mybatis.common.template.Template.*;
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;
}
}
ㅁ MemberDao에 updateMember 메소드 만들기
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);
}
}
ㅁ member-mapper.xml에 update 쿼리 만들기
<?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>
</mapper>
- 파라미터를 설정하는 중괄호 블록 안에 typeHandler가 동작되게끔 할 수 있다.
파라미터를 설정하는 곳에 타입핸들러를 실행하게끔 하면 getResult가 아닌 setParameter 메소드가 동작한다.
- MyBatis에서 #{}에 값이 채워질 때, typeHandler를 사용할 수 있다.
#{ }는 단순한 값 바인딩뿐 아니라 값을 바인딩할 때 사용되는 설정들을 같이 지정할 수 있다.
ㅁ com.br.mybatis.common.handler의 StringArrTypeHandler 수정
package com.br.mybatis.common.handler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
public class StringArrTypeHandler implements TypeHandler<String[]> {
@Override
public String[] getResult(ResultSet rs, String columnName) throws SQLException {
String resultDB = rs.getString(columnName); // "낚시,운동,게임"
return resultDB == null ? null : resultDB.split(","); // ["낚시", "운동", "게임"]
}
@Override
public String[] getResult(ResultSet rs, int columnIndex) throws SQLException {
return null;
}
@Override
public String[] getResult(CallableStatement cs, int columnIndex) throws SQLException {
return null;
}
@Override
public void setParameter(PreparedStatement ps, int i, String[] parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter == null ? null : String.join(",", parameter));
}
}
- setParameter 메소드의 매개변수에 PreparedStatement가 있다. 여기에 setXXX 메소드로 물음표의 값을 채울 수 있다.
- 쿼리에서 #{ } 부분이 물음표고, int i는 접근되는 홀더(물음표) 순번이다. 여기서 i의 값은 5다.
- String[] parameter에는 MemberDto 객체의 interest 필드값인 String[]이 온다.
- ps.setXXX(홀더순번, 대체할값);
ps.setString(i, parameter값을 String으로 변환한 값);으로 형변환하는 코드를 작성하면 된다.
- '정보변경' 버튼을 누르면 메인페이지로 가지고 다시 회원번호를 입력해서 검색하면 update가 잘 되었음을 확인할 수 있다.