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은 조회 결과를 저장할 객체
- VO엔 조회 시 사용할 WHERE절에 넣을 값과 조회결과를 담을 List
<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>
트랜잭션 처리
-
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));
}
}
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 구성
-
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) 플러그인 설치
- 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(@)을 사용해서 처리할 메소드를 설정
-
DispatchServlet
- 진입점
-
HandlerMapper
- 요청 URL을 처리할 수 있는 Controller를 찾아, 해당 Controller를 반환
-
Controller
- 요청처리, 관계유지, Service 사용
- Service에서 업무로직을 DB를 사용하여 처리
- 요청처리, 관계유지, Service 사용
-
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를 반환형으로 두기도 함
-
Web parameter를 메소드의 Parameter로 받고 보여줄 JSP명을 반환
@Controller
public class 클래스명{
@RequestMapping(value="/요청이름.do" method=RequestMethod.GET)
public String method명(매개변수, ...) { // 반환형은 반드시 String
...
return "응답할페이지명";
}
}