package com.example.demo.ch5;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.example.demo.dao.TestDao;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/test/testList")
public class TestServlet extends HttpServlet {
Logger logger = LoggerFactory.getLogger(TestServlet.class);
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
logger.info("doGet");//405 - Restful API 메소드 이름 오타발동
TestDao tDao = new TestDao();
Map<String,Object> pMap = new HashMap<>();
tDao.procEmpcursor(pMap);
logger.info(pMap.toString());
req.setAttribute("pMap", pMap);
//view계층 - 연결 - ReactJS, Vue.js
RequestDispatcher view = req.getRequestDispatcher("./testList.jsp");
view.forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
logger.info("doPost");
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.util.Map, com.google.gson.Gson" %>
<%!
//<select id="proc_empcursor" parameterType="java.util.Map" statementType="CALLABLE">
//variable rc_emp refcursor;
//exec proc_empcursor(:rc_emp);
//print rc_emp
//{ call proc_empcursor(#{key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet, resultMap=empVO})}
//</select>
//{key=[vo,vo,vo,, ,,,,]}
//page directive
//디클러레이션 - 자바코드
//전변, 메소드 선언할 수 있음
//잊으세요
//인스턴스화를 못함 - 플랫폼 독립적이어서 서버마다 자바클래스이름의 명명규칙이 달라서.
//공통코드, DB연동하는 코드, 재사용성 고려한 메소드이라면 XXX.java
%>
<%
//스크립틀릿 - 지변 - service()코드가 생성되니까 메소드 안에서 선언한 변수이니까
Map<String,Object> rmap = (Map)request.getAttribute("pMap");
Gson g = new Gson();
String temp = g.toJson(rmap);
//out.print(rmap);//out은 내장객체 - PrintWriter
out.print(temp);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>사원목록 - 프로시저</title>
</head>
<body>
</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.mapper.TestMapper">
<resultMap id="empVO" type="com.vo.EmpVO"/>
<select id="proc_login1" parameterType="java.util.Map" statementType="CALLABLE">
{ call proc_login1(#{m_id, jdbcType=VARCHAR, mode=IN, javaType=java.lang.String}
,#{m_pw, jdbcType=VARCHAR, mode=IN, javaType=java.lang.String}
,#{r_msg, jdbcType=VARCHAR, mode=OUT, javaType=java.lang.String}
)}
</select>
<select id="proc_empcursor" parameterType="java.util.Map" statementType="CALLABLE">
{ call proc_empcursor(#{key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet, resultMap=empVO})}
</select>
<select id="currentTime" parameterType="string" resultType="string">
SELECT to_char(sysdate, 'YYYY-MM-DD') FROM dual
</select>
</mapper>
public void procEmpcursor(Map<String,Object> pMap) {
//Map<String,Object> pMap = new HashMap<>();
logger.info("procEmpcursor" + pMap);
try {
sqlSession = sqlSessionFactory.openSession();
sqlSession.selectOne("proc_empcursor",pMap);
logger.info(pMap.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
[테스트 케이스]
HTML로 폼태그나 태그를 사용하지 않고 진행하겠다.
수동으로 url 주소를 입력하고 엔터를 눌렀을 떄 발생하는 상황에 대해서 얘기하겠다.
클라이언트 사이드에서 http://localhost:8000/test/testList를 검색했을 때 나오는 결과에 대해 서버사이드 / 클라이언트는 어떤 동작을 할까?
우선 클라이언트는 서버에 요청한다.(url 주소는 http://localhost:8000/test/testList) 이 주소이며, 이 값을 응답으로 내보내는 형식은 GET방식이다. 지금 작성하고 있는 글은 RestFul Api에 대해서 작성하는게 아니기 떄문에 생략하겠다.
자, 그러면 Get방식 안에서 어떻게 동작할까? 이전 게시글에서 doGet(req-요청,res-응답)에 대해 언급했다. 요청,응답객체는 톰캣 WAS에서 객체를 주입해주고 서버사이드 쪽에서 forward
방식으로 View
계층에 전달한다. 그렇다면 (요청, 응답) 객체는 살아 있을까? -> Yes
필자는 마이바티스를 연동 했기 떄문에 오라클 DB에 저장되어 있는 프로시저를 호출하려고 한다.
<select id="proc_empcursor" parameterType="java.util.Map" statementType="CALLABLE">
{ call proc_empcursor(#{key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet, resultMap=empVO})}
</select>
이 문장을 나는 이렇게 해석할 것이다.
<select>
- id가 "proc_empcursor를 호출 할떄, parameterType은 java.util.Map 타입으로 해주고
- statementType = "CALLABLE"로 지정하면, 프로시저에서 OUT으로 지정한 값들이 사용한 파라미터 map에 담긴다.
</select>
그리고 selet태그 안쪽 해석
- proc_empcursor를 불러줘 key는 내가 지정한 것이고, jdbcType은 CURSOR로 지정하고, mode는 OUT이야
- JavaType은 sql.ResultSet : n건을 담아오는데. 그 한 건을 VO에 담는다.
- 그 resultMap은 복잡한 결과 매핑을 간편하게 만들어주는 태그이다. VO는 empVO이다.
해당 proc_empcursor는 ref커서이기 때문에 행을 리턴하는 것이 아니라, 전체 행을 반환한다.
프로시저
를 부를 떈 call
를 한다.
resultMap
은 empVO
이다. (복잡한 결과 매핑을 간편하게 만들어주기 위해 만들어진 태그다.)
ResultSet
- 커서 조작에 대한 인터페이스이름-n건 그 한 건을 VO에 담는다. (단 한 건만 VO에 담는다.)
하지만 parameterType="java.util.Map"
으로 했기 때문에, Map객체로 쌓인다.
statementType="CALLABLE"
로 지정하면 프로시저에서 out로 지정한 값들이 proc_empcursor를 요청할때 사용한 파라미터 map에 담긴다.
프로시저는 리턴이 없음 mode = OUT(내보낸다) jbdbType은 refcursor니깐
위에서 얘기한 값을 TestDao로 인스턴스화 마이바티스 객체를 받아온다.
public void procEmpcursor(Map<String,Object> pMap) {
//Map<String,Object> pMap = new HashMap<>();
logger.info("procEmpcursor" + pMap);
try {
sqlSession = sqlSessionFactory.openSession();
sqlSession.selectOne("proc_empcursor",pMap);
logger.info(pMap.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
실행을 제어한다.
@WebServlet("/test/testList")
public class TestServlet extends HttpServlet {
Logger logger = LoggerFactory.getLogger(TestServlet.class);
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
logger.info("doGet");//405 - Restful API 메소드 이름 오타발동
TestDao tDao = new TestDao();
Map<String,Object> pMap = new HashMap<>();
tDao.procEmpcursor(pMap);
logger.info(pMap.toString());
req.setAttribute("pMap", pMap);
//view계층 - 연결 - ReactJS, Vue.js
RequestDispatcher view = req.getRequestDispatcher("./testList.jsp");
view.forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
logger.info("doPost");
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ page import="java.util.Map, com.google.gson.Gson" %>
<%!
//<select id="proc_empcursor" parameterType="java.util.Map" statementType="CALLABLE">
//variable rc_emp refcursor;
//exec proc_empcursor(:rc_emp);
//print rc_emp
//{ call proc_empcursor(#{key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet, resultMap=empVO})}
//</select>
//{key=[vo,vo,vo,, ,,,,]}
//page directive
//디클러레이션 - 자바코드
//전변, 메소드 선언할 수 있음
//잊으세요
//인스턴스화를 못함 - 플랫폼 독립적이어서 서버마다 자바클래스이름의 명명규칙이 달라서.
//공통코드, DB연동하는 코드, 재사용성 고려한 메소드이라면 XXX.java
%>
<%
//스크립틀릿 - 지변 - service()코드가 생성되니까 메소드 안에서 선언한 변수이니까
Map<String,Object> rmap = (Map)request.getAttribute("pMap");
Gson g = new Gson();
String temp = g.toJson(rmap);
//out.print(rmap);//out은 내장객체 - PrintWriter
out.print(temp);
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>사원목록 - 프로시저</title>
</head>
<body>
</body>
</html>