ㅁ 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>
<h4>4. select연습 - 여러행 + 페이징 처리 - RowBounds 적용</h4>
<h5>5. select연습 + update연습 (회원정보 상세조회 + 회원정보 변경) - TypeHandler 적용</h5>
<h6>6. 동적쿼리 연습 (회원 검색)</h6>
</body>
</html>
- 공지사항 상세조회는 한 행만 조회된다.
- 보통 한 행 조회면 vo 객체로 바로 조회시킨다.
ㅁ com.br.mybatis.notice.controller에 NoticeDetailController 서블릿을 만든다.
package com.br.mybatis.notice.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.notice.dto.NoticeDto;
import com.br.mybatis.notice.service.NoticeServiceImpl;
/**
* Servlet implementation class NoticeDetailController
*/
@WebServlet("/detail.no")
public class NoticeDetailController extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public NoticeDetailController() {
super();
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
int noticeNo = Integer.parseInt(request.getParameter("noticeNo"));
NoticeDto n = new NoticeServiceImpl().selectNotice(noticeNo);
request.setAttribute("n", n);
request.getRequestDispatcher("/WEB-INF/views/notice/noticeDetail.jsp").forward(request, response);
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
- 글번호를 요청시 입력받아서, 글번호로 해당하는 공지사항 게시글 하나를 보여준다.
ㅁ com.br.mybatis.notice.service의 NoticeServiceImpl에 selectNotice 메소드를 만든다.
package com.br.mybatis.notice.service;
import static com.br.mybatis.common.template.Template.getSqlSession;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import com.br.mybatis.common.dto.PageInfoDto;
import com.br.mybatis.notice.dao.NoticeDao;
import com.br.mybatis.notice.dto.NoticeDto;
public class NoticeServiceImpl implements NoticeService {
private NoticeDao noticeDao = new NoticeDao();
@Override
public int insertNotice(NoticeDto n) {
SqlSession sqlSession = /*Template.*/getSqlSession();
int result = noticeDao.insertNotice(sqlSession, n);
if(result > 0) {
sqlSession.commit();
}
sqlSession.close();
return result;
}
@Override
public NoticeDto selectNotice(int noticeNo) {
SqlSession sqlSession = getSqlSession();
NoticeDto n = noticeDao.selectNotice(sqlSession, noticeNo);
sqlSession.close();
return n;
}
@Override
public List<NoticeDto> selectList() {
return null;
}
@Override
public int selectListCount() {
return 0;
}
@Override
public List<NoticeDto> selectList(PageInfoDto pi) {
return null;
}
}
- MyBatis의 SqlSession 객체는 JDBC의 Connection 객체와 유사한 역할을 한다.
- MyBatis는 SqlSession을 통해 데이터베이스와의 통신을 관리하고, SQL 쿼리를 실행하며 트랜잭션을 처리한다.
ㅁ com.br.mybatis.notice.dao의 NoticeDao에 selectNotice 메소드를 만든다.
package com.br.mybatis.notice.dao;
import org.apache.ibatis.session.SqlSession;
import com.br.mybatis.notice.dto.NoticeDto;
public class NoticeDao {
public int insertNotice(SqlSession sqlSession, NoticeDto n) {
return sqlSession.insert("noticeMapper.insertNotice", n);
}
public int updateNotice(SqlSession sqlSession, NoticeDto n) {
return sqlSession.update("noticeMapper.updateNotice", n);
}
public int deleteNotice(SqlSession sqlSession, int noticeNo) {
return sqlSession.delete("noticeMapper.deleteNotice", noticeNo);
}
public NoticeDto selectNotice(SqlSession sqlSession, int noticeNo) {
return sqlSession.selectOne("noticeMapper.selectNotice", noticeNo);
}
}
- 조회결과가 한 행일 때는 selectOne 메소드, 조회결과가 여러행일 때는 selectList 메소드를 사용한다.
- select 메소드들의 반환형이 T다. 쿼리문의 resultType에 적은 type으로 반환된다.
ㅁ notice-mapper.xml에 "selectNotice" 쿼리를 추가한다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<mapper namespace="noticeMapper">
<insert id="insertNotice" parameterType="com.br.mybatis.notice.dto.NoticeDto">
insert
into notice
(
notice_no
, notice_title
, notice_content
, notice_writer
)
values
(
seq_nno.nextval
, #{ noticeTitle }
, #{ noticeContent }
, #{ noticeWriter }
)
</insert>
<update id="updateNotice" parameterType="com.br.mybatis.notice.dto.NoticeDto">
update
notice
set
notice_title = #{ noticeTitle }
, notice_content = #{ noticeContent }
where
notice_no = #{ noticeNo }
</update>
<delete id="deleteNotice" parameterType="_int">
delete
from notice
where notice_no = #{ noticeNo }
</delete>
<!-- (1) 별칭을 사용하는 방법
<select id="selectNotice" resultType="NoticeDto">
select
notice_no as "noticeNo"
, notice_title as "noticeTitle"
, notice_content as "noticeContent"
, user_id as "noticeWriter"
, to_char(regist_date, 'YY/MM/DD HH:MI') as "registDate"
from notice n
join member on (notice_writer=user_no)
where n.status = 'Y'
and notice_no = #{ noticeNo }
</select>
-->
<!-- (2) 별칭을 사용하지 않는 방법 -->
<resultMap id="noticeResultMap" type="NoticeDto">
<result column="notice_no" property="noticeNo" />
<result column="notice_title" property="noticeTitle" />
<result column="notice_content" property="noticeContent" />
<result column="user_id" property="noticeWriter" />
<result column="regist_date" property="registDate" />
</resultMap>
<select id="selectNotice" resultMap="noticeResultMap">
select
notice_no
, notice_title
, notice_content
, user_id
, to_char(regist_date, 'YY/MM/DD HH:MI') as "regist_date"
from notice n
join member on (notice_writer=user_no)
where n.status = 'Y'
and notice_no = #{ noticeNo }
</select>
</mapper>
- (1) 별칭을 사용하는 방법, (2) 별칭을 사용하지 않는 방법 전부 한번에 작성한 상태다.
- DB의 regist_date 컬럼은 DATE형이다.
NoticeDto 객체의 registDate 컬럼은 이번에 String으로 선언했었다.
DB에서 조회할 때 "to_char 함수"를 통해서 날짜값을 "문자열"로 받는다.
- 이전엔 조회 결과를 rset으로 받고 rset에서 하나씩 컬럼값을 뽑아서 우리가 원하는 dto 객체의 각 필드에 담았었다.
그럴 필요 없이, resultType인 NoticeDto 객체가 생성되어 필드에 조회된 값이 담겨서 반환된다.
====================================================================
※ select 태그
- select 태그는 resultType 또는 resultMap을 반드시 써야 한다.
- select는 결과에 대한 타입을 뭐로 반환할지 반드시 써야 한다.
(1) resultType 사용 - 조회 결과를 담아서 반환하고자 하는 객체 타입을 바로 제시하는 경우
(2) resultMap사용 - 매핑시키는 내용을 따로 정의해두는 경우
(1) resultType 사용 - 조회 결과를 담아서 반환하고자 하는 객체 타입을 바로 제시하는 경우
- 쿼리 실행 후
notice_no는 NoticeDto 객체의 noticeNo에,
notice_title은 NoticeDto 객체의 noticeTitle에,
notice_content는 NoticeDto 객체의 noticeContent에,
user_id는 NoticeDto 객체의 noticeWriter에,
to_char()는 NoticeDto 객체의 registDate에 담는다.
- 이때 db에서 조회되는 컬럼명이 담고자하는 객체의 필드명과 일치해야 한다.
일치하지 않으면 위와 같이 별칭을 사용해서 "필드명"으로 조회되도록 하면 된다.
※ resultType에 별칭 쓰기( mybatis-config.xml 파일 )
- type을 쓰는 곳엔 항상 풀클래스명 아니면 별칭이 들어간다.
- 별칭을 사용하고 싶다면 resources/config/mybatis-config.xml의 typeAliases 태그에 작성하면 된다.
<?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" />
</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>
- 별칭은 마음대로 정해도 되지만 보통 클래스명으로 쓴다.
ㅁ WEB-INF/views/notice/noticeDetail.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 n }">
검색된 공지사항이 없습니다.
</c:when>
<c:otherwise>
글번호 : ${ n.noticeNo } <br>
글제목 : ${ n.noticeTitle } <br>
글내용 : ${ n.noticeContent } <br>
작성자 : ${ n.noticeWriter } <br>
작성일 : ${ n.registDate } <br>
</c:otherwise>
</c:choose>
</body>
</html>
ㅁ 한 행 조회 해보기
====================================================================
(2) resultMap사용 - 매핑시키는 내용을 따로 정의해두는 경우
- resultMap을 사용하면 db의 컬럼명과 dto 객체의 필드명을 일치시키기 위해 쿼리문의 헤드부에 별칭을 쓰지 않아도 된다.
- ResultSet으로부터 조회된 컬럼값을 내가 지정한 dto객체의 필드에 매핑시켜주는 코드가 내부적으로 수행된다.
<resultMap id="식별자" type="조회결과를 담아서 반환하고자하는 dto 객체의 타입(풀클래스명 or 별칭)">
<result column="컬럼명" property="필드명" />
<result column="컬럼명" property="필드명" />
<result column="컬럼명" property="필드명" />
<resultMap>
- column 속성에 작성하는 컬럼명은 대소문자를 가리지 않음. db의 컬럼명이다.
- property에 작성되는 필드명은 대소문자를 가린다. dto 객체의 필드명만 써야 한다.
- column 속성값의 "컬럼명"으로 조회된 값이 "있으면" 뽑아서 (없다고 문제되진 않음)
property 속성값의 "필드명"의 필드에 mapping하고(담고)
type 속성값의 "dto 객체"로 반환하는 resultMap을 정의한다.
- select문에 resultMap 속성을 사용해서 해당 resultMap을 참조시키고,
resultMap의 type의 dto 객체로 반환한다.
ㅁ /mybatisProject/resources/mappers의 notice-mapper.xml에 수정한다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<mapper namespace="noticeMapper">
<insert id="insertNotice" parameterType="com.br.mybatis.notice.dto.NoticeDto">
insert
into notice
(
notice_no
, notice_title
, notice_content
, notice_writer
)
values
(
seq_nno.nextval
, #{ noticeTitle }
, #{ noticeContent }
, #{ noticeWriter }
)
</insert>
<update id="updateNotice" parameterType="com.br.mybatis.notice.dto.NoticeDto">
update
notice
set
notice_title = #{ noticeTitle }
, notice_content = #{ noticeContent }
where
notice_no = #{ noticeNo }
</update>
<delete id="deleteNotice" parameterType="_int">
delete
from notice
where notice_no = #{ noticeNo }
</delete>
<!-- (1) 별칭을 사용하는 방법
<select id="selectNotice" resultType="NoticeDto">
select
notice_no as "noticeNo"
, notice_title as "noticeTitle"
, notice_content as "noticeContent"
, user_id as "noticeWriter"
, to_char(regist_date, 'YY/MM/DD HH:MI') as "registDate"
from notice n
join member on (notice_writer=user_no)
where n.status = 'Y'
and notice_no = #{ noticeNo }
</select>
-->
<!-- (2) 별칭을 사용하지 않는 방법
<resultMap id="noticeResultMap" type="NoticeDto">
<result column="notice_no" property="noticeNo" />
<result column="notice_title" property="noticeTitle" />
<result column="notice_content" property="noticeContent" />
<result column="user_id" property="noticeWriter" />
<result column="regist_date" property="registDate" />
</resultMap>
<select id="selectNotice" resultMap="noticeResultMap">
select
notice_no
, notice_title
, notice_content
, user_id
, to_char(regist_date, 'YY/MM/DD HH:MI') as "regist_date"
from notice n
join member on (notice_writer=user_no)
where n.status = 'Y'
and notice_no = #{ noticeNo }
</select>
</mapper>
- select는 무조건 resultType, resultMap 둘 중에 하나만 작성해야 한다.
- select태그에 resultType이 아닌 resultMap 속성을 사용한다.
속성값으로 어떤 id의 resultMap을 참조시킬지 작성한다.
- 함수식은 길어서 별칭을 부여했다.
- resultType 보다 resultMap을 쓰는게 더 권장된다.
왜냐면 공지사항에 대해 조회할 때 마다 일일이 쿼리문에 별칭을 부여할 바에는
아예 resultMap을 하나 정의해두고 매번 select 태그에서 참조하게 해서 재사용이 하는 것이 좋다.
- resultType, resultMap 속성은 select 태그에서만 사용한다.
insert, update, delete 태그들은 반환값의 타입을 명시하지 않아도 되기 대문에 사용하지 않는다.