• Home
  • About
    • Young's Github Pages photo

      一日不作一日不食

    • About
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

Java EE 정리 32

11 Apr 2019

Reading time ~5 minutes

Java EE 정리 32 - MyBatis(7), Spring


MyBatis를 사용한 다양한 SELECT 예(6)

  • Procedure 사용(조회시)
  • 트랜잭션 처리

조회하는 PROCEDURE 사용

  • 조회를 할 때, Procedure 조회 결과를 받아와서 쓸 때
    • SYS_REFCURSOR는 커서의 제어권을 외부로 보내는 데이터형
    • 자바에선 ResultSet으로 받아 사용
CREATE OR REPLACE fPROCEDURE 프로시저명(
    매개변수 IN 데이터형, ...
    매개변수 OUT 데이터형, ...
    매개변수 OUT SYS_REFCURSOR
)
IS
    ...
BEGIN
    ...
END;
/
  • in parameter 처리
#{ getter명,mode=IN }
  • out parameter (단일형) 처리
    • jdbcType은 java.sql.Types에서 제공하는 타입을 사용
#{ setter명,mode=OUT,jdbcType=DB데이터형,javaType=Java데이터형 }
  • out parameter (CURSOR) 처리
    • resultMap은 조회되는 컬럼과 setter를 미리 맵핑시켜 놓는 노드
#{ setter명,mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=resultMapId }
  • 프로시저에서 VO를 사용
    • VO엔 조회 시 사용할 WHERE절에 넣을 값과 조회결과를 담을 List을 갖는다.
    • Domain은 조회 결과를 저장할 객체
<resultMap id="resultMapId" type="Domain">
    <result column="컬럼명" property="setter명"/>
    <result column="컬럼명" property="setter명"/>
    ...    
</resultMap>

<select id="프로시저사용" parameterType="VO">
    { call 프로시저명(
        #{ deptno,mode=IN },
        #{ list,mode=OUT,jdbcType=CURSOR,
            javaType=ResultSet,resultMap=resultMapId }) }
</select>
  • 프로시저를 사용할 땐 selectOne을 사용
    • 조회결과를 가진 List를 getter로 얻어 사용한다.
SqlSession.selectOne("프로시저사용", vo);

vo.getList(); // 조회결과를 가진 list를 받을 수 있다.
  • 조회 프로시저 사용 예
-- SYS_REFCURSOR를 이용한 프로시저결과 다중행 조회
-- 부서번호를 입력받아 emp테이블에서 부서별 사원정보를 조회하여
-- out parameter로 저장하는 프로시저 작성
-- * 사원번호, 사원명, 연봉, 부서번호, 부서명, 위치를 조회
-- * 입력값이 10~19번 입력되면 10번부서 조회
--            20~29번 입력되면 20번부서 조회
--            그외는 30번 부서를 조회

CREATE OR REPLACE PROCEDURE select_emp(
    deptno NUMBER,
    cur_join OUT SYS_REFCURSOR
                       IS
    temp_deptno NUMBER := deptno;
BEGIN

    -- 입력되는 부서번호를 10또는 20으로 생성
    temp_deptno := TRUNC(temp_deptno/10, 0)*10;


    IF temp_deptno NOT IN (10, 20) THEN
        temp_deptno := 30;
    END IF;      
    
    OPEN cur_join
            FOR SELECT e.empno, e.ename, e.sal, d.deptno, d.dname, d.loc
                  FROM emp e, dept d
                  WHERE (e.deptno = d.deptno)
                      AND d.deptno = temp_deptno;
END;
/
package kr.co.sist.exam.domain;
public class EmpProcedure {
      
      private String ename, dname, loc;
      private int empno, sal, deptno;
      // getter, setter
      ...
package kr.co.sist.exam.vo;
...
public class CursorVO {
      
      private int deptno; // 조회할 부서 번호(사용자 입력값)

      // CURSOR로 조회한 값 - MyBatis가 입력하는 값
      private List<EmpProcedure> empList;
      // getter, setter
      ...
<!-- exam_mapper2.xml -->
...
      <!-- cursor -->
      <resultMap type="kr.co.sist.exam.domain.EmpProcedure"  id="epResult">
            <result column="ename" property="ename"/>
            <result column="dname" property="dname"/>
            <result column="loc" property="loc"/>
            <result column="empno" property="empno"/>
            <result column="sal" property="sal"/>
            <result column="deptno" property="deptno"/>
      </resultMap>
      
      <select id="selectProcedure"  parameterType="kr.co.sist.exam.vo.CursorVO" statementType="CALLABLE">
            {
                  call select_emp(
                        #{ deptno,mode=IN },
                        #{  empList,mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=epResult  })
            }
      </select>
...
  • 객체의 주소값은 유일하므로 반환이 필요 없다.
public class MyBatisDAO1 {
      
      public void selectProc(CursorVO cv) {
            SqlSession ss =  MyBatisDAO.getInstance().getSessionFactory().openSession();
            ss.selectOne("selectProcedure",cv);
            ss.close();
      }
...
public class MyBatisService1 {
      
      public void selectProcedure(CursorVO cv) {
            MyBatisDAO1 mb_dao = new MyBatisDAO1();
            mb_dao.selectProc(cv);
      }
...
<!-- main_menu.jsp -->
...
<li><a href="main.jsp?page=date0411/select_procedure">select 프로시저  사용</a></li>
...
<!-- select_procedure.jsp -->
...
<%
      String deptno = request.getParameter("deptno");
      if(deptno != null && !"".equals(deptno)) {
            MyBatisService1 mbs = new MyBatisService1();
            CursorVO cv = new CursorVO();
            cv.setDeptno(Integer.parseInt(deptno));
            
            // 프로시저를 호출하여 실행 결과가 cv에 저장됨
            mbs.selectProcedure(cv); 
            pageContext.setAttribute("empList", cv.getEmpList());
      }
%>
<form name="frm" action="main.jsp">
<input type="hidden" name="page" value="date0411/select_procedure"/>
<label>부서번호</label>
<input type="text" name="deptno" class="inputBox"/>
<input type="submit" value="부서별 사원조회" class="btn"/>
</form>
<div>
<c:if test="${ not empty param.deptno }">
      <table border="1">
            <tr>
                  <th width="80">번호</th>
                  <th width="80">사원번호</th>
                  <th width="120">사원명</th>
                  <th width="80">연봉</th>
                  <th width="80">부서번호</th>
                  <th width="150">부서명</th>
                  <th width="100">지역</th>
            </tr>
            <c:if test="${ empty empList }">
            <tr>
                  <td colspan="6" align="center">
                        <strong>${ param.deptno }</strong>번 부서에는  사원이 존재하지 않습니다.
                  </td>
            </tr>
            </c:if>
            <c:forEach var="emp" items="${ empList }">
            <c:set var="i" value="${ i+1 }"/>
            <tr>
                  <td><c:out value="${ i }"/></td>
                  <td><c:out value="${ emp.empno }"/></td>
                  <td><c:out value="${ emp.ename }"/></td>
                  <td><c:out value="${ emp.sal }"/></td>
                  <td><c:out value="${ emp.deptno }"/></td>
                  <td><c:out value="${ emp.dname }"/></td>
                  <td><c:out value="${ emp.loc }"/></td>
            </tr>
            </c:forEach>
      </table>
</c:if>
</div>

01

트랜잭션 처리

02

  • test_transaction1,test_transaction2 테이블에 레코드를 같이 추가하는 트랜잭션 작업
    • test_transaction2 테이블의 subject 컬럼 크기가 작아 제목이 길면 추가되지 않는다.
    • 둘 다 INSERT되면 commit을 아니면 rollback을 수행
package kr.co.sist.exam.vo;
public class TransactionVO {
      
      private String subject, writer;
      // 인자있는 생성자, getter, setter
      ...
  • 보통 VO는 인자있는 생성자를 생성(개발자가 set하기 쉽게)
    • Domain은 MyBatis에서 setter를 사용하여 set하므로 생성자가 필요 없다.
<!-- exam_mapper2.xml -->
...
      <!-- transaction -->
      <insert id="tr1" parameterType="kr.co.sist.exam.vo.TransactionVO">
            INSERT INTO test_transaction1(subject, writer, input_date)
            VALUES(#{ subject }, #{ writer }, SYSDATE)
      </insert>
      <insert id="tr2" parameterType="kr.co.sist.exam.vo.TransactionVO">
            INSERT INTO test_transaction2(subject, writer, input_date)
            VALUES(#{ subject }, #{ writer }, SYSDATE)
      </insert>
...
public class MyBatisDAO1 {
      
      public int insertTransaction(TransactionVO tvo) {
            int cnt = 0;
            
            SqlSession ss =  MyBatisDAO.getInstance().getSessionFactory().openSession();
            cnt = ss.insert("tr1",tvo);
            cnt += ss.insert("tr2",tvo);

            if (cnt == 2) { // 성공시에만 commit 처리
                  ss.commit();
            }
            // 둘 중에 하나 실패하면 자동으로 rollback 됨
            ss.close();

            return cnt;
      }
...
      public static void main(String[] args) {
            
            MyBatisDAO1 md = new  MyBatisDAO1();
            TransactionVO tvo = new TransactionVO("있다는", "김정윤");
            System.out.println(md.insertTransaction(tvo));
      }
}

03

Spring Framework

  • Rod Johson 창시
    • EJB없이 업무로직을 구현하기 위해 시작
  • Spring IoC(Inversion of Control, 제어의 반전(역행))
    • 프로그래머가 작성한 프로그램이 재사용 라이브러리의 흐름 제어를 받게 되는 소프트웨어 디자인 패턴
    • 약결합으로 객체간의 관계를 객체 외부에서 설정, 업무를 분해하고 결합하기가 편함
  • DI(Dependency Injection, 의존성 주입)
    • 어떤 업무를 처리할 때 객체를 의존성 주입 받아서 처리하는 것
    • Spring core
  • EJB의 단점을 해결한 Framework
    • EJB Container에 의존적
    • 무겁다(안정적, 느리다)
    • 구조가 복잡하다(배포는 .ear)
  • 스프링은 Container의 의존성이 없다
  • 가볍다(빠르고 안정적)
  • 분산 환경을 지원하지 않는다.
    • Web 환경에서 해결가능
  • 다른 Framework과 연동이 편하다
  • Spring.io에서 제공

Spring 구성

04

  • Spring DI
    • 약결합
  • Spring AOP(Aspect Oriented Programming)
    • 횡단 관심사 처리
  • Spring JDBC
    • JDBC를 편하게 사용하도록 제공
  • Spring ORM(Object Relation Mapping)
    • DB Framework과 연동(MyBatis, Hibernate)
  • Spring JNDI
    • 이름으로 객체를 찾는 기술
  • Spring Web
    • Web Framework(Struts) 연동
    • Struts의 기능을 이미 Spring이 갖고 있어 굳이 연동 안함
  • Spring MVC
    • MVC 패턴을 미리 구현해 놓은 것

이클립스 STS(Spring Tool Suite) 플러그인 설치

05

  • eclipse에서는 Spring 개발을 편하게 하기 위해서 STS(Spring Tool Suite) 제공
  • eclipse Marketplace에서 STS 검색
    • Spring Tools 3 Add-On 설치

Spring MVC

  • MVC는 Model, View, Controller의 약자
    • 유지보수를 위해 적용된 패턴
    • Model - DB처리(DAO)
    • View - 업무 처리 결과를 가지고 화면 구현
    • Controller - 진입점, 업무를 구분하여 처리
  • MVC Pattern을 구현하기 편하도록 미리 만들어 둔 것
  • Controller에서 요청을 method로 처리
    • XML이 아니라 annotation(@)을 사용해서 처리할 메소드를 설정

06

  • DispatchServlet
    • 진입점
  • HandlerMapper
    • 요청 URL을 처리할 수 있는 Controller를 찾아, 해당 Controller를 반환
  • Controller
    • 요청처리, 관계유지, Service 사용
      • Service에서 업무로직을 DB를 사용하여 처리
  • ViewResolver
    • View할 JSP를 찾는 일을 함
  • View(JSP)
    • 처리된 데이터를 가지고 응답을 해준다.
  • 흐름 정리
    • 요청이 URL로 들어오면 servlet-mapping으로 DispatcherServlet 객체가 생성되고 HandlerMapper에서 요청URL을 처리하는 Controller가 있는지 확인 후 Controller명 반환, 처리할 Controller 객체가 생성된 후 URL mapping이 된 method가 호출되면서 Service, DAO를 사용하여 업무로직을 처리, 처리된 결과를 저장 후 응답할 jsp명을 반환, 반환된 jsp명은 ViewResolver에 의해 prefix, suffix가 붙어 해당 경로에 jsp가 존재하는지 확인 후 존재하면 처리 결과를 사용한 jsp를 응답하게 된다.

Controller

  • 요청 URL을 method로 처리
  • 작성법
    • Handler Mapper가 찾을 수 있도록 클래스 위에 @Controller를 설정
    • URL을 처리하는 method 위에 @RequestMapping을 설정
    • 메소드는 Spring Framework에서 호출
      • Web parameter를 메소드의 Parameter로 받고 보여줄 JSP명을 반환
        • HttpServletRequest를 파라미터로 받을 수 있다.(비권장)
        • 요청한 url과 반환형이 같은경우 void를 반환형으로 두기도 함
@Controller
public class 클래스명{

    @RequestMapping(value="/요청이름.do" method=RequestMethod.GET)
    public String method명(매개변수, ...) { // 반환형은 반드시 String

        ... 

        return "응답할페이지명";
    }
}


Java EEMyBatisSpring