본문 바로가기

Spring 3/MyBatis Stored Procedures

MyBatis Oracle Stored Procedure example

Spring, MyBatis, Oracle 환경에서 Stored Procedure 호출 예


Oracle Stored Procedure - 테스트 대상 프로시저 (이 프로시저는 Cursor 를 리턴한다 )

create or replace PROCEDURE GETEMP 

(

  EMP_CURSOR OUT SYS_REFCURSOR

) AS 

BEGIN

  OPEN EMP_CURSOR FOR

  SELECT empno, ename, deptno, sal, job FROM emp2;

END GETEMP;


EmpMapper.xml

<?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="org.kdea.mybatis.EmpDAO"> <!--이 sql 문장과 매핑될 인터페이스의 완전한 경로-->


    <resultMap id="empvoMap" type="org.kdea.mybatis.EmpVO">

    </resultMap>

    

    <select id="procedureCall" parameterType="java.util.Map" statementType="CALLABLE">

    { CALL GETEMP( #{ key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet,resultMap=empvoMap } ) }

    </select>

</mapper>


위의 내용에서 프로시저를 호출하는 문장은 다음과 같이 이해할 수 있다

#{ }  안의 내용은 한개의 파라미터이므로 프로시저 정의를 보면 OUT 파라미터라는 것을 의미한다. OUT 파라미터는 프로시저 외부에서 프로시저 내부로 빈 공간을 전달하는 것과 같으며 OUT 파라미터의 이름은 'key' 로 표현되어 있다. resultMap은 반드시 요구되는 속성이며 resultType은 사용할 수 없다. OUT 파라미터가 'key' 라는 이름으로 사용되므로 프로시저를 호출한 후에도 'key' 라는 이름을 사용하여 값을 추출할 수 있다. 프로시저가 리턴한 것은 오라클의 CURSOR이고 자바에서는 ResultSet 으로 표현되는데 MyBatis 에 의해 ResultSet은 resultMap=empvoMap 설정에 따라 EmpVO에 매핑되고 List<EmpVO>형으로 최종적으로 리턴된다


EmpDAO.java

import java.util.List;

import java.util.Map;


public interface EmpDAO {

  ...............

     ........

public void procedureCall(Map<String,Object>map);

}


EmpMBController.java

package org.kdea.mybatis;


import java.util.HashMap;

import java.util.List;


import org.mybatis.spring.SqlSessionTemplate;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.*;

import org.springframework.web.servlet.ModelAndView;


@Controller

@RequestMapping("/mb/")

public class EmpMBController {


@Autowired

private SqlSessionTemplate sqlSessionTemplate; // 설정파일에 빈으로 등록되었기 때문에 생성자나 Setter 없이 자동으로 주입

@RequestMapping("procedure")

public ModelAndView procedureCall(){

EmpDAO dao = sqlSessionTemplate.getMapper(EmpDAO.class);

HashMap<String,Object> map = new HashMap<String,Object>();

dao.procedureCall(map);

List<EmpVO> list = (List<EmpVO>)map.get("key");

ModelAndView mv = new ModelAndView("mb/procedureView", "list",list);

return mv;

}

}



procedureView.jsp

<%@ page language="java" contentType="text/html; charset=utf-8"

    pageEncoding="utf-8"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>프로시저 실행결과</title>

</head>

<body><p>

<c:forEach var="emp" items="${list}">

${emp.empno} ${emp.ename} ${emp.deptno} ${emp.job}<br>

</c:forEach>

</body>

</html>



프로시저가 IN, OUT 파라미터를 가진 경우


호출대상 프로시저

create or replace PROCEDURE GETEMP2 

(

  P_EMPNO IN NUMBER 

, EMP_CURSOR OUT SYS_REFCURSOR

) AS 

BEGIN

  update emp2 set sal=sal+1 where empno=p_empno;

  OPEN EMP_CURSOR FOR

  SELECT * FROM emp2 WHERE empno=P_EMPNO;

END GETEMP2;


EmpMapper.xml

<?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="org.kdea.mybatis.EmpDAO"> <!--이 sql 문장과 매핑될 인터페이스의 완전한 경로-->


    <resultMap id="empvoMap" type="org.kdea.mybatis.EmpVO">

    </resultMap>

    

    <select id="procedureCall" parameterType="java.util.Map" statementType="CALLABLE">

    {CALL GETEMP(#{key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet,resultMap=empvoMap})}

    </select>

    <select id="procedureCall2" parameterType="java.util.Map" statementType="CALLABLE">

    {CALL GETEMP2(#{empno, mode=IN},

    #{key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet,resultMap=empvoMap})}

    </select>

</mapper>


EmpDAO.java

import java.util.List;

import java.util.Map;


public interface EmpDAO {

public void procedureCall(Map<String,Object>map);

public void procedureCall2(Map<String,Object>map);

}


EmpMBController.java

package org.kdea.mybatis;


import java.util.HashMap;

import java.util.List;


import org.mybatis.spring.SqlSessionTemplate;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.*;

import org.springframework.web.servlet.ModelAndView;


@Controller

@RequestMapping("/mb/")

public class EmpMBController {


@Autowired

private SqlSessionTemplate sqlSessionTemplate; // 설정파일에 빈으로 등록되었기 때문에 생성자나 Setter 없이 자동으로 주입


@RequestMapping("proc2")

public ModelAndView procedureCall2(){

EmpDAO dao = sqlSessionTemplate.getMapper(EmpDAO.class);

HashMap<String,Object> map = new HashMap<String,Object>();

map.put("empno", 7369);

dao.procedureCall2(map);

List<EmpVO> list = (List<EmpVO>)map.get("key");

ModelAndView mv = new ModelAndView("mb/procedureView", "list",list);

return mv;

}

}



procedureView.jsp 는 앞선 예제와 동일함



프로시저가 커서를 리턴하지 않고 HashMap에 저장하여 리턴하는 예


테스트용으로 사용할 오라클 프로시저

create or replace PROCEDURE GETENAME 

(

  p_EMPNO IN NUMBER,

  p_ENAME OUT emp.ename%type

) AS 

BEGIN

  select ename into p_ENAME from emp where empno=p_EMPNO;

END GETENAME;


EmpMapper.xml

<?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="org.kdea.mybatis.EmpDAO"> <!--이 sql 문장과 매핑될 인터페이스의 완전한 경로-->


    <resultMap id="empvoMap" type="org.kdea.mybatis.EmpVO">

    <result property="ename" column="p_ename"/>

    </resultMap>

    

    <resultMap id="myMap" type="hashmap">

    </resultMap>

    

    <select id="procedureCall" parameterType="java.util.Map" statementType="CALLABLE">

    {CALL GETEMP(#{key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet,resultMap=empvoMap})}

</select>

<select id="procedureCall2" parameterType="java.util.Map" statementType="CALLABLE">

    {CALL GETEMP2(#{empno, mode=IN},

     #{key, jdbcType=CURSOR, mode=OUT, javaType=java.sql.ResultSet,resultMap=empvoMap})}

</select>

<select id="procedureCall3" parameterType="java.util.Map" statementType="CALLABLE">

     {CALL GETENAME(#{empno, mode=IN},  #{ename, mode=OUT, jdbcType=VARCHAR, resultMap=myMap}) }

</select>

</mapper>


EmpDAO.java

import java.util.List;

import java.util.Map;


public interface EmpDAO {

public void procedureCall(Map<String,Object>map);

public void procedureCall2(Map<String,Object>map);

public void procedureCall3(Map<String,Object>map);

}


EmpMBController.java

package org.kdea.mybatis;


import java.util.HashMap;

import java.util.List;


import org.mybatis.spring.SqlSessionTemplate;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.servlet.ModelAndView;


@Controller

@RequestMapping("/mb/")

public class EmpMBController {


@Autowired

private SqlSessionTemplate sqlSessionTemplate; // 설정파일에 빈으로 등록되었기 때문에 생성자나 Setter 없이 자동으로 주입


@RequestMapping("proc")

public ModelAndView procedureCall(){

EmpDAO dao = sqlSessionTemplate.getMapper(EmpDAO.class);

HashMap<String,Object> map = new HashMap<String,Object>();

map.put("empno", 7369);

map.put("ename", null);

dao.procedureCall3(map);

String ename = (String) map.get("ename");

ModelAndView mv = new ModelAndView("mb/procedureView","ename",ename);

return mv;

}

}