전자정부프레임워크와 MyBatis 연동 예제
개요
egovframe.org에서 제공하는 예제를 분석하고 실행해 보고자 합니다
MyBatis 연동을 위한 2개 형태 예제 제공
1. EgovAbstractMapper 추상클래스를 상속한 Mapper
@Repository("empMapper")
public class EmpMapper extends EgovAbstractMapper { }
- DI 사용하여 MyBatis가 생성해준 empMapper를 사용함
2. Mapper 를 인터페이스로 선언하고 MyBatis가 구현
@Mapper("deptMapper")
public interface DeptMapper { }
- DI사용하여 MyBatis 가 생성해준 deptMapper를 사용함
예제에 포함되어 있는 설명은 아래와 같습니다
1) DAO 클래스 대신 "Mapper 인터페이스"로 구현하는 방법과
2) EgovAbstractMapper 추상클래스를 상속하여 기존 DAO 클래스를 구현하는 방법으로 두 가지 예제를 제공한다.
[ 표준프레임워크 MyBatis 적용 시 준수사항 ]
1)의 경우, Mapper 인터페이스에 반드시 @Mapper를 선언해야 한다.
@Mapper 스캔을 위해 egovframework.rte.psl.dataaccess.mapper.MapperConfigurer 클래스를 빈으로 등록하고, basePackage 프로퍼티를 설정한다.
2)의 경우, EgovAbstractMapper를 상속하거나 확장하여 활용해야 한다.
참고
egovframework.rte.fdl.property.EgovPropertyService;
egovframework.rte.ptl.mvc.tags.ui.pagination.PaginationInfo;
egovframework.rte.fdl.property.EgovPropertyService;
EgovIdGnrService : 자동으로 아이디(숫자)가 증가하는 기능 제공, IDS 테이블을 사용하는 것으로 생각됨
PaginationInfo : 페이지 네비게이션을 위한 정보
예제에서 사용된 MyBatis 버전 정보
mybatis 3.2.8
mybatis-spring 1.2.2
MyBatis 예제 안에서 사용된 설정파일의 주요내용
WEB-INF/
└ web.xml ( WEB-INF/ )
- 일반 스프링 프로젝트와 동일
- 스프링 설정파일 2개의 xml (contextConfigLocation과 서블릿 컨텍스트 파일)의 경로 지정
- contextConfigLocation: classpath*:META-INF/spring/context-*.xml
- 서블릿 컨텍스트 파일 : /WEB-INF/spring/dispatcher-*.xml
WEB-INF/spring/
└ dispatcher-servlet.xml ( /WEB-INF/spring/ )
- 일반 스프링 프로젝트와 동일
- Pagination 기능을 위한 2개의 빈 설정
- PaginationManager, imageRender
META-INF/spring/
└ context-common.xml ( META-INF/spring/ )
- db.properties 경로 지정
└ context-datasource.xml ( META-INF/spring/ )
- dataSource 빈 설정 ( ddl.sql, dml.sql 경로 지정 )
- jdbc dataSource, 아파치 dbcp dataSource 중에서 한 가지를 선택하여 설정
└ context-idgen.xml ( META-INF/spring/ )
- 자동증가필드에 값을 삽입할 때 사용될 아이디 생성 빈 등록
- 사원번호, 부서번호를 새로 삽입할 때 등록된 빈을 코드에서 사용함
└ context-mybatis.xml ( META-INF/spring/ )
- sqlSession 빈 설정 (속성으로 dataSource 참조, sql-myabtis-config.xml 경로참조, mappers/*.xml 경로참조)
- @Mapper("deptMapper")으로 선언된 인터페이스 경로(패키지) 지정 -> DI을 이용하여 코드에서 인스턴스 사용가능
└ context-properties.xml ( /WEB-INF/spring/ )
- propertiesService 빈( EgovPropertyServiceImpl) 등록 (pageUnit, pageSize 정보 설정)
META-INF/sqlmap/mappers/
└ employee.xml ( employee 테이블 SQL 문장 )
└ department.xml ( department 테이블 SQL 문장 )
META-INF/sqlmap/config/
└ sql-mybatis-config.xml ( META-INF/sqlmap/config/ )
- EmpVO, DeptVO, SearchVO 에 대한 각 alias 선언
META-INF/property/
└ db.properties ( META-INF/property/ )
- driver, url, username, password 등
META-INF/db/
└ ddl.sql ( META-INF/db/ ) : 예제에 사용될 테이블을 생성하는 create table ~ 문장
- IDS, EMPLOYEE, DEPARTMENT 테이블 생성
- IDS 테이블은 각 테이블의 다음 id (숫자)가 저장됨, 즉 한 행이 추가될 때마다 다음에 추가될 행의 id(숫자)를 저장해두는 역할을 함
└ dml.sql ( META-INF/db/ ) : 예제로 사용될 테이블에 샘플 데이터 입력
- IDS, EMPLOYEE, DEPARTMENT 테이블에 샘플 레코드 입력
코드 분석
Employee CRUD 기능 ( EgovAbstractMapper 를 사용하는 경우 )
@Controller
public class EmpController
public interface EmpService
public class EmpVO
@Repository("empMapper")
public class EmpMapper extends EgovAbstractMapper
@Service("empService")
public class EmpServiceImpl extends EgovAbstractServiceImpl implements EmpService
emp/list.jsp
emp/detail.jsp
emp/form.jsp
Department CRUD 기능 ( Mapper 인터페이스를 사용하는 경우 )
@Controller
public class DeptController
public interface DeptService
public class DeptVO
@Mapper("deptMapper")
public interface DeptMapper
@Service("deptService")
public class DeptServiceImpl extends EgovAbstractServiceImpl implements DeptService
dept/list.jsp
dept/detail.jsp
dept/form.jsp
위의 2가지 기능에 공통으로 사용되는 클래스
EgovImgPaginationRenderer.java
JdbcLoggingExcepHndlr.java
SearchVO.java
이클립스에 새로 생성된 프로젝트를 아래처럼 설정하고 실행 테스트
eGovFrame v 3.5.1 개발자 환경 다운로드
압축해제 후 eclipse 폴더 내에서 이클립스 실행파일 더블클릭하여 이클립스 실행
eGovFrame Web Project 생성(예제코드 없이 생성)
MyBatis 예제의 모든 설정파일을 생성된 프로젝트의 각 대응 위치에 복사하기
MyBatis 예제의 모든 소스코드(*.java, *.jsp)를 생성된 프로젝트의 각 대응 위치에 복사하기
데이터베이스 접속을 위한 드라이버를 pom.xml의 dependencies 요소에 추가하기
MyBatis 예제에 포함된 *.sql 파일을 이용하여 샘플 데이터베이스 테이블을 생성하고 샘플 데이터를 저장한다
- ddl.sql, dml.sql 2개의 파일에는 테이블 생성/레코드 추가 문장이 저장되어 있다
모든 설정파일(context-*.xml)에 분산되어 있는 빈 설정내용을 모두 dispatcher-servlet.xml 파일에 복사한다
dispatcher-servlet.xml 파일에서 dataSource를 설정하는 부분을 편집하여 현재 데이터베이스에 맞도록 설정한다
dispatcher-servlet.xml 파일에서 sqlSession 빈을 설정하는 부분을 편집하여 필요한 만큼의 맵파일을 등록한다
4개의 리소스파일은 egovframework.sample.cmmn 패키지 안에 복사한다
- department.xml, employee.xml, message-common_ko.properties, sql-mybatis-config.xml
- 위의 4개 리소스 파일은 dispatcher-servlet.xml 파일에서 참조하도록 설정한다
department.xml, employee.xml파일에서 오라클 데이터베이스에 맞도록 SQL문장을 편집한다
프로젝트 위에서 마우스 우측 > Run As > Run on server
웹브라우저 화면에 보여지는 index.jsp 내용에서 2개의 링크 중에 하나를 클릭한다
dispatcher-servlet.xml 파일의 전체내용
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--
- The controllers are autodetected POJOs labeled with the @Controller annotation.
-->
<context:component-scan base-package="egovframework">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--아래의 component-scan은 현재 시스템에 맞게 설정한다 -->
<context:component-scan base-package="egovframework.rte.psl.dataaccess.mapper" />
<context:component-scan base-package="egovframework.sample.cmmn" />
<context:component-scan base-package="egovframework.sample.dept.service" />
<context:component-scan base-package="egovframework.sample.dept.service.impl" />
<context:component-scan base-package="egovframework.sample.dept.web" />
<context:component-scan base-package="egovframework.sample.emp.service" />
<context:component-scan base-package="egovframework.sample.emp.service.impl" />
<context:component-scan base-package="egovframework.sample.emp.web" />
<!--현재 시스템의 DB에 맞게 아래의 설정은 수정해야 한다-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
p:driverClassName="oracle.jdbc.OracleDriver"
p:url="jdbc:oracle:thin:@localhost:1521:xe"
p:username="scott"
p:password="TIGER"/>
<!-- SqlSession setup for MyBatis Database Layer -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:egovframework/sample/cmmn/sql-mybatis-config.xml" />
<property name="mapperLocations">
<list>
<value>classpath:egovframework/sample/cmmn/department.xml</value>
<value>classpath:egovframework/sample/cmmn/employee.xml</value>
</list>
</property>
</bean>
<!-- MapperConfigurer setup for MyBatis Database Layer with @Mapper("deptMapper") in DeptMapper Interface -->
<bean class="egovframework.rte.psl.dataaccess.mapper.MapperConfigurer">
<property name="basePackage" value="egovframework.sample.dept.service.impl" />
</bean>
<!-- 사원번호 생성 -->
<bean name="egovIdGnrServiceEmp" class="egovframework.rte.fdl.idgnr.impl.EgovTableIdGnrServiceImpl" destroy-method="destroy">
<property name="dataSource" ref="dataSource" />
<property name="strategy" ref="mixPrefixEmployee" />
<property name="blockSize" value="1" />
<property name="table" value="IDS" />
<property name="tableName" value="EMPLOYEE" />
</bean>
<bean name="mixPrefixEmployee" class="egovframework.rte.fdl.idgnr.impl.strategy.EgovIdGnrStrategyImpl">
<property name="prefix" value="EMPLOYEE-" />
<property name="cipers" value="8" />
<property name="fillChar" value="0" />
</bean>
<!-- 부서번호 생성 -->
<bean name="egovIdGnrServiceDept" class="egovframework.rte.fdl.idgnr.impl.EgovTableIdGnrServiceImpl" destroy-method="destroy">
<property name="dataSource" ref="dataSource" />
<property name="strategy" ref="mixPrefixDepartmet" />
<property name="blockSize" value="1" />
<property name="table" value="IDS" />
<property name="tableName" value="DEPARTMENT" />
</bean>
<bean name="mixPrefixDepartmet"
class="egovframework.rte.fdl.idgnr.impl.strategy.EgovIdGnrStrategyImpl">
<property name="prefix" value="DEPARTMENT-" />
<property name="cipers" value="8" />
<property name="fillChar" value="0" />
</bean>
<!--
- This bean resolves specific types of exceptions to corresponding logical view names for error views.
- The default behaviour of DispatcherServlet is to propagate all exceptions to the servlet container:
- this will happen here with all other types of exceptions.
-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="cmmn/bizError" />
<property name="exceptionMappings">
<props>
<prop key="org.springframework.dao.DataAccessException">/cmmn/dataAccessFailure</prop>
<prop key="org.springframework.transaction.TransactionException">/cmmn/transactionFailure</prop>
<prop key="egovframework.rte.fdl.cmmn.exception.EgovBizException">/cmmn/bizError</prop>
</props>
</property>
</bean>
<!--
- This bean configures the 'prefix' and 'suffix' properties of InternalResourceViewResolver, which resolves logical view names returned by Controllers.
- For example, a logical view name of "vets" will be mapped to "/WEB-INF/jsp/vets.jsp".
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:viewClass="org.springframework.web.servlet.view.JstlView"
p:prefix="/WEB-INF/jsp/egovframework/" p:suffix=".jsp" />
<!-- Pagination Tag -->
<bean id="imageRenderer" class="egovframework.sample.cmmn.EgovImgPaginationRenderer" />
<bean id="paginationManager" class="egovframework.rte.ptl.mvc.tags.ui.pagination.DefaultPaginationManager">
<property name="rendererType">
<map>
<entry key="image" value-ref="imageRenderer"/>
</map>
</property>
</bean>
<!-- Pagination Tag -->
<!-- Message Source-->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list>
<value>classpath:egovframework/sample/cmmn/message-common</value>
<value>classpath:/egovframework/rte/fdl/idgnr/messages/idgnr</value>
<value>classpath:/egovframework/rte/fdl/property/messages/properties</value>
</list>
</property>
<property name="cacheSeconds">
<value>60</value>
</property>
</bean>
<bean id="leaveaTrace" class="egovframework.rte.fdl.cmmn.trace.LeaveaTrace">
<property name="traceHandlerServices">
<list>
<ref bean="traceHandlerService" />
</list>
</property>
</bean>
<bean id="traceHandlerService" class="egovframework.rte.fdl.cmmn.trace.manager.DefaultTraceHandleManager">
<property name="reqExpMatcher">
<ref bean="antPathMater" />
</property>
<property name="patterns">
<list>
<value>*</value>
</list>
</property>
<property name="handlers">
<list>
<ref bean="defaultTraceHandler" />
</list>
</property>
</bean>
<bean id="antPathMater" class="org.springframework.util.AntPathMatcher" />
<bean id="defaultTraceHandler" class="egovframework.rte.fdl.cmmn.trace.handler.DefaultTraceHandler" />
<!-- context.common.xml -->
<bean name="propertiesService" class="egovframework.rte.fdl.property.impl.EgovPropertyServiceImpl" destroy-method="destroy">
<property name="properties">
<map>
<entry key="pageUnit" value="10"/>
<entry key="pageSize" value="10"/>
<!-- <entry key="fileDir" value="C:\\temp\\"/>
<entry key="filePath" value="C:\\temp\\"/>
<entry key="emailServer" value="smtp.gmail.com"/>
<entry key="emailAuthId" value="dongdo.lee@gmail.com"/>
<entry key="emailAuthPwd" value="dongdo04"/>
<entry key="emailFromAddr" value="egov@egov.gov"/>
<entry key="emailFromName" value="EgovFramwork"/>
<entry key="myAdmnstmachValue" value="001"/>
<entry key="cvplOthbcValueOpen" value="001"/>
<entry key="cvplOthbcCode" value="001"/>
<entry key="userDeptCode" value="002"/>
<entry key="userRspofcCode" value="003"/>
<entry key="admnstmachCode" value="004"/>
<entry key="marshallingDir" value="C:/svn_egovframework/SAMPLE/civilappealSample/trunk/egovframework-civilappealsample-web/src/main/webapp/WEB-INF/config/egovframework/"/>
-->
</map>
</property>
</bean>
</beans>
eGovFramework에서 MyBatis를 사용하는 2가지 방법
- DAO를 interface 형식으로 선언하고 MyBatis에서 구현하도록 하는 방법
- 전자정부프레임워크가 제공하는 EgovAbstractMapper 클래스를 상속하는 방법
참고: 전자정부프레임워크에서는 Service 클래스를 선언할 때 egovframework.rte.fdl.cmmn.EgovAbstractServiceImpl 클래스를 상속하여 작성한다
1. EgovAbstractMapper 클래스를 상속하여 DAO를 작성하는 예
@Repository("empMapper")
public class EmpMapper extends EgovAbstractMapper {
/**
* DB에서 사원목록을 조회한다.
*
* @param empVO
* @return List
* @throws Exception
*/
public List<EmpVO> retrieveEmpList(EmpVO empVO) throws Exception {
/* employee.xml 파일의 namespace는 '<mapper namespace="EmpMapper">'으로 선언되어 있으므로
* 소스코드에서 employee.xml 파일에 선언된 sql 문장을 참조할 때는 아래의 라인처럼 namespace를 이용하여
* 'EmpMapper.SQL문장의 아이디'와 같은 방법을 사용하여 접근할 수 있다
* 아래 라인에서 사용된 selectList()는 EgovAbstractMapper클래스의 멤버 메소드이다
*/
return selectList("EmpMapper.retrieveEmpList", empVO);
}
...........
............
}
참고: 위의 코드에서 참조하는 employee.xml 의 관련부분 (아래의 sql 문장은 오라클에서 실행되지 않지만 위의 코드와 연결되는 구조를 보여준다)
<?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="EmpMapper">
<select id="retrieveEmpList" parameterType="empVO" resultType="empVO">
<![CDATA[
SELECT
EMP.EMPNO,
EMP.EMPNM,
EMP.BIRTHDATE,
EMP.TELEPHONE,
EMP.ADDRESS
FROM EMPLOYEE EMP
WHERE 1=1
]]>
<if test="searchKeyword != null">
<choose>
<when test="searchCondition == 1">
AND EMP.EMPNO LIKE '%'|| #{searchKeyword} ||'%'
</when>
<otherwise>
AND EMP.EMPNM LIKE '%'|| #{searchKeyword} ||'%'
</otherwise>
</choose>
</if>
<![CDATA[
ORDER BY EMP.EMPNO DESC
LIMIT #{recordCountPerPage} OFFSET #{firstIndex}
]]>
</select>
..........
.........
</mapper>
위에서 생성한 EmpMapper 클래스를 사용하는 예
@Service("empService")
public class EmpServiceImpl extends EgovAbstractServiceImpl implements EmpService {
@Resource(name = "empMapper")
private EmpMapper empMapper;
@Resource(name = "egovIdGnrServiceEmp")
private EgovIdGnrService egovIdGnrService;
/**
* 사원목록조회 요청을 처리하기 위해 데이터처리를 요청한다.
*
* @param empVO
* @return List
* @throws Exception
*/
public List<EmpVO> retrieveEmpList(EmpVO empVO) throws Exception {
List<EmpVO> list = empMapper.retrieveEmpList(empVO);
return list;
}
2. DAO를 interface 형식으로 선언하고 MyBatis가 구현하도록 하는 방법
@Mapper("deptMapper")
public interface DeptMapper {
/**
* DB에서 부서목록을 조회한다.
* @param deptVO
* @return List
* @throws Exception
*/
public List<DeptVO> retrieveDeptList(DeptVO deptVO) throws Exception;
/**
* DB에서 부서정보를 상세조회한다.
*
* @param deptVO
* @return DeptVO
* @throws Exception
*/
public DeptVO retrieveDept(DeptVO deptVO) throws Exception;
.............
.............
}
위와같이 인터페이스를 선언하여 사용하려는 경우에는 서블릿 설정파일에 아래와 같이 등록해야 한다
<!-- MapperConfigurer setup for MyBatis Database Layer with @Mapper("deptMapper") in DeptMapper Interface -->
<bean class="egovframework.rte.psl.dataaccess.mapper.MapperConfigurer">
<property name="basePackage" value="egovframework.sample.dept.service.impl" /> <!--@Mapper 인터페이스 패키지 경로-->
</bean>
위에 선언한 인터페이스를 사용하는 예
@Service("deptService")
public class DeptServiceImpl extends EgovAbstractServiceImpl implements DeptService {
@Resource(name = "deptMapper")
private DeptMapper deptMapper; // 작성된 인터페이스의 참조
@Resource(name = "egovIdGnrServiceDept")
private EgovIdGnrService egovIdGnrService;
/**
* 부서목록조회 요청을 처리하기 위해 데이터처리를 요청한다.
*
* @param deptVO
* @return List
* @throws Exception
*/
public List<DeptVO> retrieveDeptList(DeptVO deptVO) throws Exception {
return deptMapper.retrieveDeptList(deptVO);
}
.........
...........
}