전자정부표준프레임워크를 잘 정리한 요약 자료...
전자정부 표준프레임워크 개요 요약 (1-1) http://tobewiseys.tistory.com/52
전자정부 표준프레임워크 개요 요약 (1-2) http://tobewiseys.tistory.com/63
- 교육 (실행환경을 중심으로 ...)
실행환경:
화면처리, 업무처리, 데이터처리, 연계통합, 공통기반
AOP, IoC/DI 에 대한 설명..
무지하게 졸린다. 열심히 읽는다. 잘 읽기는 하지만 듣기가 힘들다.
실습
lab204-dataaccess
xxx.sql 파일을 열고 type, name, database 를 지정하고 마우스 오른쪽 버튼을 눌러 'Execute All 을 선택'
----------------------------------------------------------
실습환경 구성
- lab204-dataaccess 프로젝트 import
- maven repository 설정 및 dependency library 맞춤
Step 1. 구동 환경 설정
1) Hsqldb 초기화 스크립트
. /lab204-dataaccess/src/test/resources/META-INF/testdata/sample_schema_hsql.sql 를 확인한다.
현 실습 프로젝트에서는 편의상 매 테스트 케이스 재실행 시 관련 table 을 drop/create 하고 있음.
2) dataSource 설정
. /lab204-dataaccess/src/test/resources/META-INF/spring/context-common.xml 에 context:property-placeholder 설정을 추가한다.
/**************************************************************************************/
<context:property-placeholder
location="classpath:/META-INF/spring/jdbc.properties" />
/**************************************************************************************/
. /lab204-dataaccess/src/test/resources/META-INF/spring/context-datasource.xml 를 설정한다.
/**************************************************************************************/
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${driver}" />
<property name="url" value="${dburl}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
<property name="defaultAutoCommit" value="false" />
<property name="poolPreparedStatements" value="true" />
</bean>
/**************************************************************************************/
. /lab204-dataaccess/src/test/resources/META-INF/spring/jdbc.properties 를 작성한다.
/**************************************************************************************/
driver=org.hsqldb.jdbcDriver
dburl=jdbc:hsqldb:mem:testdb
#dburl=jdbc:hsqldb:hsql://localhost/sampledb
username=sa
password=
/**************************************************************************************/
. cf.) 위에서 기본으로 memory DB 형식으로 자동 구동하도록 되어 있으나 dburl=jdbc:hsqldb:hsql://localhost/sampledb 과 같이 변경시에는
/lab204-dataaccess/db 상에서 (외부 탐색기에서) runHsqlDB.cmd 를 실행하여 DB Server 를 구동하고 테스트할 수도 있다.
3) transaction 설정
. /lab204-dataaccess/src/test/resources/META-INF/spring/context-transaction.xml 를 작성한다.
/**************************************************************************************/
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
/**************************************************************************************/
. cf.) 여기서는 transaction manager 만을 설정하였고, TestCase 내에서 전역 @Transactional 설정으로 트랜잭션을 일괄 지정하고 있으나 보통 AOP 형식(tx:aop)의 트랜잭션 대상 지정으로 비즈니스 서비스의 메서드에 일괄 지정하는 경우가 많다. cf2.) @Transactional Annotation 으로 대상 메서드에 개별로 따로 지정할 수도 있다.
4) Spring 의 iBATIS 연동 설정
. /lab204-dataaccess/src/test/resources/META-INF/spring/context-sqlMap.xml 를 작성한다.
/**************************************************************************************/
<!-- SqlMap setup for iBATIS Database Layer -->
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation"
value="classpath:/META-INF/sqlmap/sql-map-config.xml" />
<!--
<property name="mappingLocations"
value="classpath:/META-INF/sqlmap/mappings/lab-*.xml" />
-->
<property name="dataSource" ref="dataSource" />
</bean>
/**************************************************************************************/
. 최신 프레임워크 환경에서는 sql-map-config.xml 내에 개별 sql 맵핑 파일일 일일이 지정하는 것이 아니라 위의 mappingLocations 영역을 주석 해제하여 Spring 의 ResourceLoader 형식으로 패턴 매칭에 의거한 일괄 로딩으로 처리가 가능하다. (단, 테스트 결과 CacheModel 등의 일부 기능에서 문제가 발생하는 경우가 있으므로 사용에 유의할것!!)
5) iBATIS 의 sql-map-config 설정 파일 작성
. /lab204-dataaccess/src/test/resources/META-INF/sqlmap/sql-map-config.xml 를 작성한다.
/**************************************************************************************/
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN"
"http://www.ibatis.com/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<settings useStatementNamespaces="false"
cacheModelsEnabled="true"
/>
<!-- Spring 2.5.5 이상, iBATIS 2.3.2 이상에서는 iBATIS 연동을 위한 SqlMapClientFactoryBean 정의 시 mappingLocations 속성으로
Sql 매핑 파일의 일괄 지정이 가능하다.
("sqlMapClient" bean 설정 시 mappingLocations="classpath:/META-INF/sqlmap/mappings/lab-*.xml" 로 지정하였음)
단, sql-map-config-2.dtd 에서 sqlMap 요소를 하나 이상 지정하도록 되어 있으므로 아래의 dummy 매핑 파일을 설정하였다.
-->
<sqlMap resource="META-INF/sqlmap/mappings/lab-dummy.xml" />
<sqlMap resource="META-INF/sqlmap/mappings/lab-dept.xml" />
<sqlMap resource="META-INF/sqlmap/mappings/lab-emp.xml" />
<sqlMap resource="META-INF/sqlmap/mappings/lab-emp-cachemodel.xml" />
<sqlMap resource="META-INF/sqlmap/mappings/lab-emp-dept-resultmap.xml" />
</sqlMapConfig>
/**************************************************************************************/
6) Id Generation Service 설정
. /lab204-dataaccess/src/test/resources/META-INF/spring/context-idgen.xml 를 작성한다.
/**************************************************************************************/
<bean name="primaryTypeSequenceIds"
class="egovframework.rte.fdl.idgnr.impl.EgovSequenceIdGnrService"
destroy-method="destroy">
<property name="dataSource" ref="dataSource" />
<property name="query" value="SELECT NEXT VALUE FOR empseq FROM DUAL" />
</bean>
/**************************************************************************************/
. 여기서는 Hsqldb 를 사용하여 Oracle 의 DUAL 테이블 역할을 할 수 있도록 초기화 스크립트 sql 에 create 하였으며, DB Sequence 기반의 Id Generation 을 사용한 예이다. (위에서 select next value for seq_id from xx 는 Hsqldb 의 특화된 sequence 사용 문법임에 유의!)
7) common 설정
. /lab204-dataaccess/src/test/resources/META-INF/spring/context-common.xml 를 확인한다.
/**************************************************************************************/
<!-- PropertyPlaceholderConfigurer 설정 -->
..
<!-- MessageSource 설정 -->
..
<!-- 전자정부 TraceHandler 설정 관련 -->
..
<!-- 스테레오 타입 Annotation 을 인식하여 Spring bean 으로 자동 등록하기 위한 component-scan 설정 -->
..
/**************************************************************************************/
. 외부 properties 파일을 Container 구동 시 미리 Spring Bean 설정 파일의 속성값으로 대체하여 처리해주는 PropertyPlaceholderConfigurer 설정
. Locale 에 따른 다국어 처리를 쉽게 해주는 messageSource 설정. 여기서는 전자정부 실행환경의 id generation 서비스와 properties 서비스의 메시지 파일과 업무 어플리케이션을 위한 사용자 메시지(/message/message-common - message-common_en_US.properties, message-common_ko_KR.properties 를 확인할 것) 를 지정하였다.
. exception 처리 Handler 와 유사하게 특정한 상황에서 사용자가 Trace Handler 를 지정하여 사용할 수 있도록 전자정부 프레임워크에서 가이드하고 있는 TraceHandler 설정
. component-scan 설정
8) aspect 설정
. /lab204-dataaccess/src/test/resources/META-INF/spring/context-aspect.xml 를 작성한다.
/**************************************************************************************/
<aop:config>
<aop:pointcut id="serviceMethod"
expression="execution(* egovframework.lab..impl.*Impl.*(..))" />
<aop:aspect ref="exceptionTransfer">
<aop:after-throwing throwing="exception"
pointcut-ref="serviceMethod" method="transfer" />
</aop:aspect>
</aop:config>
<bean id="exceptionTransfer" class="egovframework.rte.fdl.cmmn.aspect.ExceptionTransfer">
<property name="exceptionHandlerService">
<list>
<ref bean="defaultExceptionHandleManager" />
</list>
</property>
</bean>
<bean id="defaultExceptionHandleManager"
class="egovframework.rte.fdl.cmmn.exception.manager.DefaultExceptionHandleManager">
<property name="reqExpMatcher" ref="antPathMater" />
<property name="patterns">
<list>
<value>**service.impl.*</value>
</list>
</property>
<property name="handlers">
<list>
<ref bean="egovHandler" />
</list>
</property>
</bean>
<bean id="egovHandler"
class="egovframework.lab.dataaccess.common.JdbcLoggingExcepHndlr" />
/**************************************************************************************/
. Spring AOP(xml 설정 방식) 를 사용하여 비지니스 메서드에서 exception 이 발생한 경우 일괄적으로 ExceptionTransfer 의 transfer 메서드 기능(Advice) 를 수행해 주게 됨. --> Exception logging 및 BizException 형태로 wrapping 하여 재처리하는 Exception 공통처리 후 ExceptionHandleManager 에 의해 관리(설정) 되는 Handler (ex. exception 내용을 메일링 한더던지.. 사용자 구현 가능) 가 자동적으로 추가 수행될 수 있음.
2. EmpService 서비스 작성
1) Interface 작성
. /lab204-dataaccess/src/main/java/egovframework/lab/dataaccess/service/EmpService.java 를 작성한다.
/**************************************************************************************/
public interface EmpService {
public BigDecimal insertEmp(EmpVO empVO) throws Exception;
public void updateEmp(EmpVO empVO) throws Exception;
public void deleteEmp(EmpVO empVO) throws Exception;
public EmpVO selectEmp(EmpVO empVO) throws Exception;
public EmpWithDeptVO selectEmpWithDept(EmpVO empVO) throws Exception;
public EmpIncludesDeptVO selectEmpIncludesDept(EmpVO empVO) throws Exception;
public List<EmpVO> selectEmpList(EmpVO searchVO) throws Exception;
}
/**************************************************************************************/
2) VO 작성
. /lab204-dataaccess/src/main/java/egovframework/lab/dataaccess/service/EmpVO.java 를 작성한다.
/**************************************************************************************/
public class EmpVO extends SearchVO {
private static final long serialVersionUID = -8049578957221741495L;
private BigDecimal empNo;
private String empName;
private String job;
private BigDecimal mgr;
private Date hireDate;
private BigDecimal sal;
private BigDecimal comm;
private BigDecimal deptNo;
public BigDecimal getEmpNo() {
return empNo;
}
public void setEmpNo(BigDecimal empNo) {
this.empNo = empNo;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public BigDecimal getMgr() {
return mgr;
}
public void setMgr(BigDecimal mgr) {
this.mgr = mgr;
}
public Date getHireDate() {
return hireDate;
}
public void setHireDate(Date hireDate) {
this.hireDate = hireDate;
}
public BigDecimal getSal() {
return sal;
}
public void setSal(BigDecimal sal) {
this.sal = sal;
}
public BigDecimal getComm() {
return comm;
}
public void setComm(BigDecimal comm) {
this.comm = comm;
}
public BigDecimal getDeptNo() {
return deptNo;
}
public void setDeptNo(BigDecimal deptNo) {
this.deptNo = deptNo;
}
}
/**************************************************************************************/
. /lab204-dataaccess/src/main/java/egovframework/lab/dataaccess/service/EmpWithDeptVO.java 를 작성한다.
/**************************************************************************************/
public class EmpWithDeptVO extends EmpVO {
/** serialVersionUID */
private static final long serialVersionUID = 2916598732662272200L;
private BigDecimal deptNo;
private String deptName;
private String loc;
public BigDecimal getDeptNo() {
return deptNo;
}
public void setDeptNo(BigDecimal deptNo) {
this.deptNo = deptNo;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
}
/**************************************************************************************/
. /lab204-dataaccess/src/main/java/egovframework/lab/dataaccess/service/EmpIncludesDeptVO.java 를 작성한다.
/**************************************************************************************/
public class EmpIncludesDeptVO extends EmpVO {
/** serialVersionUID */
private static final long serialVersionUID = 2178085836444199596L;
private DeptVO deptVO;
public DeptVO getDeptVO() {
return deptVO;
}
public void setDeptVO(DeptVO deptVO) {
this.deptVO = deptVO;
}
}
/**************************************************************************************/
3) Annotation 을 적용한 Impl
. /lab204-dataaccess/src/main/java/egovframework/lab/dataaccess/service/impl/EmpServiceImpl.java 를 작성한다.
/**************************************************************************************/
@Service("empService")
public class EmpServiceImpl extends AbstractServiceImpl implements EmpService {
@Resource(name = "primaryTypeSequenceIds")
EgovIdGnrService egovIdGnrService;
@Resource(name = "empDAO")
private EmpDAO empDAO;
public BigDecimal insertEmp(EmpVO empVO) throws Exception {
// ID generation Service 를 사용하여 key 를 땀. 여기서
// primaryTypeSequenceIds 는 Sequence 기반임.
BigDecimal generatedEmpNo = egovIdGnrService.getNextBigDecimalId();
log.debug("EmpServiceImpl.insertEmp - generated empNo : " + generatedEmpNo);
empVO.setEmpNo(generatedEmpNo);
empDAO.insertEmp(empVO);
return generatedEmpNo;
}
public void updateEmp(EmpVO empVO) throws Exception {
empDAO.updateEmp(empVO);
}
public void deleteEmp(EmpVO empVO) throws Exception {
empDAO.deleteEmp(empVO);
}
public EmpVO selectEmp(EmpVO empVO) throws Exception {
EmpVO resultVO;
resultVO = empDAO.selectEmp(empVO);
if (resultVO == null) {
throw processException("info.nodata.msg");
}
return resultVO;
}
public List<EmpVO> selectEmpList(EmpVO searchVO) throws Exception {
return empDAO.selectEmpList(searchVO);
}
public EmpWithDeptVO selectEmpWithDept(EmpVO empVO) throws Exception {
EmpWithDeptVO resultVO;
resultVO = empDAO.selectEmpWithDept(empVO);
if (resultVO == null) {
throw processException("info.nodata.msg");
}
return resultVO;
}
public EmpIncludesDeptVO selectEmpIncludesDept(EmpVO empVO) throws Exception {
EmpIncludesDeptVO resultVO;
resultVO = empDAO.selectEmpIncludesDept(empVO);
if (resultVO == null) {
throw processException("info.nodata.msg");
}
return resultVO;
}
}
/**************************************************************************************/
4) DAO 작성
. /lab204-dataaccess/src/main/java/egovframework/lab/dataaccess/service/impl/EmpDAO.java 를 작성한다.
/**************************************************************************************/
@Repository("empDAO")
public class EmpDAO extends EgovAbstractDAO {
public void insertEmp(EmpVO vo) {
insert("insertEmp", vo);
}
public int updateEmp(EmpVO vo) {
return update("updateEmp", vo);
}
public int deleteEmp(EmpVO vo) {
return delete("deleteEmp", vo);
}
public EmpVO selectEmp(EmpVO vo) {
return (EmpVO) selectByPk("selectEmp", vo);
//return (EmpVO) selectByPk("selectEmpUsingCacheModelLRU", vo);
}
@SuppressWarnings("unchecked")
public List<EmpVO> selectEmpList(EmpVO searchVO) {
return list("selectEmpList", searchVO);
}
public EmpWithDeptVO selectEmpWithDept(EmpVO vo) {
return (EmpWithDeptVO) selectByPk("selectEmpWithDept", vo);
}
public EmpIncludesDeptVO selectEmpIncludesDept(EmpVO vo) {
return (EmpIncludesDeptVO) selectByPk("selectEmpIncludesDept", vo);
}
}
/**************************************************************************************/
5) mapping xml 작성
. /lab204-dataaccess/src/test/resources/META-INF/sqlmap/mappings/lab-dummy.xml 를 확인한다.
. /lab204-dataaccess/src/test/resources/META-INF/sqlmap/mappings/lab-emp.xml 를 작성한다.
/**************************************************************************************/
<sqlMap namespace="Emp">
<typeAlias alias="empVO" type="egovframework.lab.dataaccess.service.EmpVO" />
<resultMap id="empResult" class="empVO">
<result property="empNo" column="EMP_NO" />
<result property="empName" column="EMP_NAME" />
<result property="job" column="JOB" />
<result property="mgr" column="MGR" />
<result property="hireDate" column="HIRE_DATE" />
<result property="sal" column="SAL" />
<result property="comm" column="COMM" />
<result property="deptNo" column="DEPT_NO" />
</resultMap>
<insert id="insertEmp" parameterClass="empVO">
<![CDATA[
insert into EMP
(EMP_NO,
EMP_NAME,
JOB,
MGR,
HIRE_DATE,
SAL,
COMM,
DEPT_NO)
values (#empNo#,
#empName#,
#job#,
#mgr#,
#hireDate#,
#sal#,
#comm#,
#deptNo#)
]]>
</insert>
<update id="updateEmp" parameterClass="empVO">
<![CDATA[
update EMP
set EMP_NAME = #empName#,
JOB = #job#,
MGR = #mgr#,
HIRE_DATE = #hireDate#,
SAL = #sal#,
COMM = #comm#,
DEPT_NO = #deptNo#
where EMP_NO = #empNo#
]]>
</update>
<delete id="deleteEmp" parameterClass="empVO">
<![CDATA[
delete from EMP
where EMP_NO = #empNo#
]]>
</delete>
<select id="selectEmp" parameterClass="empVO" resultMap="empResult">
<![CDATA[
select EMP_NO,
EMP_NAME,
JOB,
MGR,
HIRE_DATE,
SAL,
COMM,
DEPT_NO
from EMP
where EMP_NO = #empNo#
]]>
</select>
<select id="selectEmpList" parameterClass="empVO" resultMap="empResult">
<![CDATA[
select EMP_NO,
EMP_NAME,
JOB,
MGR,
HIRE_DATE,
SAL,
COMM,
DEPT_NO
from EMP
where 1 = 1
]]>
<isNotNull prepend="and" property="empNo">
EMP_NO = #empNo#
</isNotNull>
<isNotNull prepend="and" property="empName">
EMP_NAME LIKE '%' || #empName# || '%'
</isNotNull>
</select>
</sqlMap>
/**************************************************************************************/
. /lab204-dataaccess/src/test/resources/META-INF/sqlmap/mappings/lab-emp-cachemodel.xml 를 작성한다.
/**************************************************************************************/
<sqlMap namespace="EmpCache">
<typeAlias alias="empVO" type="egovframework.lab.dataaccess.service.EmpVO" />
<!-- Spring 2.5.5 이상에서 mappingLocations 을 통해 sql mapping 파일 로딩하는 경우 CacheModel 사용에 문제가 있는듯. 주의할것! -->
<cacheModel id="cacheEmpLRU" type="LRU" readOnly="true"
serialize="false">
<flushInterval hours="24" />
<flushOnExecute statement="insertEmp" />
<flushOnExecute statement="updateEmp" />
<flushOnExecute statement="deleteEmp" />
<property name="cache-size" value="1000" />
</cacheModel>
<!-- sql reuse test -->
<sql id="selectEmpReuse">
<![CDATA[
select EMP_NO,
EMP_NAME,
JOB,
MGR,
HIRE_DATE,
SAL,
COMM,
DEPT_NO
from EMP
where 1=1
]]>
<include refid="empWhereEmpNo" />
</sql>
<sql id="empWhereEmpNo">
<isNotNull prepend="and" property="empNo">
EMP_NO = #empNo#
</isNotNull>
</sql>
<!-- CacheModel test -->
<select id="selectEmpUsingCacheModelLRU" parameterClass="empVO"
resultMap="Emp.empResult" cacheModel="cacheEmpLRU">
<include refid="selectEmpReuse" />
</select>
</sqlMap>
/**************************************************************************************/
. /lab204-dataaccess/src/test/resources/META-INF/sqlmap/mappings/lab-emp-dept-resultmap.xml 를 작성한다.
/**************************************************************************************/
<sqlMap namespace="EmpResultMap">
<typeAlias alias="empWithDeptVO" type="egovframework.lab.dataaccess.service.EmpWithDeptVO" />
<typeAlias alias="empIncludesDeptVO" type="egovframework.lab.dataaccess.service.EmpIncludesDeptVO" />
<typeAlias alias="deptIncludesEmpListVO" type="egovframework.lab.dataaccess.service.DeptIncludesEmpListVO" />
<resultMap id="empWithDeptResult" class="empWithDeptVO" extends="Emp.empResult">
<!--<result property="deptNo" column="DEPT_NO"/>-->
<result property="deptName" column="DEPT_NAME"/>
<result property="loc" column="LOC"/>
</resultMap>
<resultMap id="empIncludesDeptResult" class="empIncludesDeptVO" extends="Emp.empResult">
<!--
Emp-Dept 1:1 relation
테스트 결과 resultMap 의 참조 시 sql-map-config.xml 의
useStatementNamespaces="false" 와 상관없이 namespace prefix 를 써야 하는듯
-->
<result property="deptVO" resultMap="Dept.deptResult" />
</resultMap>
<!-- 1:N 인 경우 groupBy 속성을 명시 -->
<resultMap id="deptIncludesEmpListResult" class="deptIncludesEmpListVO" extends="Dept.deptResult" groupBy="deptNo">
<!-- Dept-EmpList 1:N relation -->
<result property="empVOList" resultMap="Emp.empResult" />
</resultMap>
<select id="selectEmpWithDept" parameterClass="empVO" resultMap="empWithDeptResult">
<![CDATA[
select EMP_NO,
EMP_NAME,
JOB,
MGR,
HIRE_DATE,
SAL,
COMM,
A.DEPT_NO,
B.DEPT_NAME,
B.LOC
from EMP A, DEPT B
where A.DEPT_NO = B.DEPT_NO
and A.EMP_NO = #empNo#
]]>
</select>
<select id="selectEmpIncludesDept" parameterClass="empVO" resultMap="empIncludesDeptResult">
<![CDATA[
select EMP_NO,
EMP_NAME,
JOB,
MGR,
HIRE_DATE,
SAL,
COMM,
A.DEPT_NO,
B.DEPT_NAME,
B.LOC
from EMP A, DEPT B
where A.DEPT_NO = B.DEPT_NO
and A.EMP_NO = #empNo#
]]>
</select>
<select id="selectDeptIncludesEmpList" parameterClass="deptVO" resultMap="deptIncludesEmpListResult">
<![CDATA[
select A.DEPT_NO as DEPT_NO,
DEPT_NAME,
LOC,
EMP_NO,
EMP_NAME,
JOB,
MGR,
HIRE_DATE,
SAL,
COMM
from DEPT A,
EMP B
where A.DEPT_NO = B.DEPT_NO
and A.DEPT_NO = #deptNo#
order by B.EMP_NO
]]>
</select>
</sqlMap>
/**************************************************************************************/
6) Testcase 작성
. /lab204-dataaccess/src/test/java/egovframework/lab/dataaccess/service/EmpServiceTest.java 를 작성한다.
/**************************************************************************************/
package egovframework.lab.dataaccess.service;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.jdbc.SimpleJdbcTestUtils;
import org.springframework.transaction.annotation.Transactional;
import egovframework.rte.fdl.cmmn.exception.EgovBizException;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:META-INF/spring/context-*" })
@TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)
@Transactional
public class EmpServiceTest {
@Resource(name = "dataSource")
DataSource dataSource;
@Resource(name = "empService")
EmpService empService;
@Before
public void onSetUp() throws Exception {
// 테스트 편의상 매 테스트메서드 수행전 외부의 sql file 로부터 DB 초기화
// (기존 테이블 삭제/생성)
SimpleJdbcTestUtils.executeSqlScript(
new SimpleJdbcTemplate(dataSource), new ClassPathResource(
"META-INF/testdata/sample_schema_hsql.sql"), true);
}
public EmpVO makeVO() throws ParseException {
EmpVO vo = new EmpVO();
// empNo 는 Biz. 서비스 내에서 id generation service 에
// 의해 key 를 따고 설정할 것임.
vo.setEmpName("홍길동");
vo.setJob("개발자");
vo.setMgr(new BigDecimal(7902));
SimpleDateFormat sdf =
new SimpleDateFormat("yyyy-MM-dd", java.util.Locale.getDefault());
vo.setHireDate(sdf.parse("2009-07-09"));
vo.setSal(new BigDecimal(1000));
vo.setComm(new BigDecimal(0));
vo.setDeptNo(new BigDecimal(20));
return vo;
}
public void checkResult(EmpVO vo, EmpVO resultVO) {
assertNotNull(resultVO);
assertEquals(vo.getEmpNo(), resultVO.getEmpNo());
assertEquals(vo.getEmpName(), resultVO.getEmpName());
assertEquals(vo.getJob(), resultVO.getJob());
assertEquals(vo.getMgr(), resultVO.getMgr());
assertEquals(vo.getHireDate(), resultVO.getHireDate());
assertEquals(vo.getSal(), resultVO.getSal());
assertEquals(vo.getComm(), resultVO.getComm());
assertEquals(vo.getDeptNo(), resultVO.getDeptNo());
}
@Test
public void testInsertEmp() throws Exception {
EmpVO vo = makeVO();
// insert
BigDecimal empNo = empService.insertEmp(vo);
vo.setEmpNo(empNo);
// select
EmpVO resultVO = empService.selectEmp(vo);
// check
checkResult(vo, resultVO);
}
@Test
public void testUpdateEmp() throws Exception {
EmpVO vo = makeVO();
// insert
BigDecimal empNo = empService.insertEmp(vo);
vo.setEmpNo(empNo);
// data change
vo.setEmpName("홍길순");
vo.setJob("설계자");
// update
empService.updateEmp(vo);
// select
EmpVO resultVO = empService.selectEmp(vo);
// check
checkResult(vo, resultVO);
}
@Test
public void testDeleteEmp() throws Exception {
EmpVO vo = makeVO();
// insert
BigDecimal empNo = empService.insertEmp(vo);
vo.setEmpNo(empNo);
// delete
empService.deleteEmp(vo);
// select
try {
@SuppressWarnings("unused")
EmpVO resultVO = empService.selectEmp(vo);
fail("EgovBizException 이 발생해야 합니다.");
} catch (Exception e) {
assertNotNull(e);
// 여기서는 비지니스 단에서 명시적으로 exception 처리 하였음.
// AbstractServiceImpl 을 extends 하고
// processException("info.nodata.msg"); 과
// 같이 메서드 콜 형태로 처리
assertTrue(e instanceof EgovBizException);
assertEquals("info.nodata.msg", ((EgovBizException) e)
.getMessageKey());
assertEquals("해당 데이터가 없습니다.", e.getMessage());
}
}
@Test
public void testSelectEmpList() throws Exception {
EmpVO vo = makeVO();
// insert
BigDecimal empNo = empService.insertEmp(vo);
vo.setEmpNo(empNo);
// 검색조건으로 key 설정
EmpVO searchVO = new EmpVO();
searchVO.setEmpNo(vo.getEmpNo());
// selectList
List<EmpVO> resultList = empService.selectEmpList(searchVO);
// key 조건에 대한 결과는 한건일 것임
assertNotNull(resultList);
assertTrue(resultList.size() > 0);
assertEquals(1, resultList.size());
checkResult(vo, resultList.get(0));
// 검색조건으로 name 설정 - '%' || #empName# || '%'
EmpVO searchVO2 = new EmpVO();
searchVO2.setEmpName(""); // '%' || '' || '%'
// --> '%%'
// selectList
List<EmpVO> resultList2 = empService.selectEmpList(searchVO2);
// like 조건에 대한 결과는 한건 이상일 것임
assertNotNull(resultList2);
assertTrue(resultList2.size() > 0);
}
@Test
public void testSelectEmpWithDept() throws Exception {
EmpVO vo = makeVO();
// insert
BigDecimal empNo = empService.insertEmp(vo);
vo.setEmpNo(empNo);
// select EmpWithDept
EmpWithDeptVO resultVO = empService.selectEmpWithDept(vo);
// check - emp data
checkResult(vo, resultVO);
// check - emp's dept data - 위 makeVO() 에서 7902 - RESEARCH 로 설정했음.
assertEquals(vo.getDeptNo(), resultVO.getDeptNo());
assertEquals("RESEARCH", resultVO.getDeptName());
assertEquals("DALLAS", resultVO.getLoc());
}
@Test
public void testSelectEmpIncludesDept() throws Exception {
EmpVO vo = makeVO();
// insert
BigDecimal empNo = empService.insertEmp(vo);
vo.setEmpNo(empNo);
// select EmpIncludesDept
EmpIncludesDeptVO resultVO = empService.selectEmpIncludesDept(vo);
// check - emp data
checkResult(vo, resultVO);
// check - emp's dept data - 위 makeVO() 에서 7902 - RESEARCH 로 설정했음.
assertEquals(vo.getDeptNo(), resultVO.getDeptVO().getDeptNo());
assertEquals("RESEARCH", resultVO.getDeptVO().getDeptName());
assertEquals("DALLAS", resultVO.getDeptVO().getLoc());
}
}
/**************************************************************************************/
3. DeptService 서비스 작성
1) Interface 작성
. /lab204-dataaccess/src/main/java/egovframework/lab/dataaccess/service/DeptService.java 를 작성한다.
/**************************************************************************************/
public interface DeptService {
public void insertDept(DeptVO deptVO) throws Exception;
public void updateDept(DeptVO deptVO) throws Exception;
public void deleteDept(DeptVO deptVO) throws Exception;
public DeptVO selectDept(DeptVO deptVO) throws Exception;
public DeptIncludesEmpListVO selectDeptIncludesEmpList(DeptVO deptVO) throws Exception;
public List<DeptVO> selectDeptList(DeptVO searchVO) throws Exception;
}
/**************************************************************************************/
2) VO 작성
. /lab204-dataaccess/src/main/java/egovframework/lab/dataaccess/service/DeptVO.java 를 작성한다.
/**************************************************************************************/
public class DeptVO extends SearchVO {
private static final long serialVersionUID = -5658611204548724246L;
private BigDecimal deptNo;
private String deptName;
private String loc;
public BigDecimal getDeptNo() {
return deptNo;
}
public void setDeptNo(BigDecimal deptNo) {
this.deptNo = deptNo;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
}
/**************************************************************************************/
. /lab204-dataaccess/src/main/java/egovframework/lab/dataaccess/service/DeptIncludesEmpListVO.java 를 작성한다.
/**************************************************************************************/
public class DeptIncludesEmpListVO extends DeptVO {
/** serialVersionUID */
private static final long serialVersionUID = -6098129034400325088L;
private List<EmpVO> empVOList;
public List<EmpVO> getEmpVOList() {
return empVOList;
}
public void setEmpVOList(List<EmpVO> empVOList) {
this.empVOList = empVOList;
}
}
/**************************************************************************************/
3) Annotation 을 적용한 Impl
. /lab204-dataaccess/src/main/java/egovframework/lab/dataaccess/service/impl/DeptServiceImpl.java 를 작성한다.
/**************************************************************************************/
@Service("deptService")
public class DeptServiceImpl extends AbstractServiceImpl implements DeptService {
@Resource(name = "deptDAO")
private DeptDAO deptDAO;
public void insertDept(DeptVO deptVO) throws Exception {
deptDAO.insertDept(deptVO);
}
public void updateDept(DeptVO deptVO) throws Exception {
deptDAO.updateDept(deptVO);
}
public void deleteDept(DeptVO deptVO) throws Exception {
deptDAO.deleteDept(deptVO);
}
public DeptVO selectDept(DeptVO deptVO) throws Exception {
DeptVO resultVO = deptDAO.selectDept(deptVO);
if (resultVO == null) {
throw processException("info.nodata.msg");
}
return resultVO;
}
public List<DeptVO> selectDeptList(DeptVO searchVO) throws Exception {
return deptDAO.selectDeptList(searchVO);
}
public DeptIncludesEmpListVO selectDeptIncludesEmpList(DeptVO deptVO)
throws Exception {
DeptIncludesEmpListVO resultVO = deptDAO.selectDeptIncludesEmpList(deptVO);
if (resultVO == null) {
throw processException("info.nodata.msg");
}
return resultVO;
}
}
/**************************************************************************************/
4) DAO 작성
. /lab204-dataaccess/src/main/java/egovframework/lab/dataaccess/service/impl/DeptDAO.java 를 작성한다.
/**************************************************************************************/
@Repository("deptDAO")
public class DeptDAO extends EgovAbstractDAO {
public void insertDept(DeptVO vo) {
insert("insertDept", vo);
}
public int updateDept(DeptVO vo) {
return update("updateDept", vo);
}
public int deleteDept(DeptVO vo) {
return delete("deleteDept", vo);
}
public DeptVO selectDept(DeptVO vo) {
return (DeptVO)selectByPk("selectDept", vo);
}
@SuppressWarnings("unchecked")
public List<DeptVO> selectDeptList(DeptVO searchVO) {
return list("selectDeptList", searchVO);
}
public DeptIncludesEmpListVO selectDeptIncludesEmpList(DeptVO vo) {
return (DeptIncludesEmpListVO)selectByPk("selectDeptIncludesEmpList", vo);
}
}
/**************************************************************************************/
5) mapping xml 작성
. /lab204-dataaccess/src/test/resources/META-INF/sqlmap/mappings/lab-dept.xml 를 작성한다.
/**************************************************************************************/
<sqlMap namespace="Dept">
<typeAlias alias="deptVO" type="egovframework.lab.dataaccess.service.DeptVO" />
<resultMap id="deptResult" class="deptVO">
<result property="deptNo" column="DEPT_NO" />
<result property="deptName" column="DEPT_NAME" />
<result property="loc" column="LOC" />
</resultMap>
<insert id="insertDept" parameterClass="deptVO">
insert into DEPT
(DEPT_NO,
DEPT_NAME,
LOC)
values (#deptNo#,
#deptName#,
#loc#)
</insert>
<select id="selectDept" parameterClass="deptVO" resultMap="deptResult">
<![CDATA[
select DEPT_NO,
DEPT_NAME,
LOC
from DEPT
where DEPT_NO = #deptNo#
]]>
</select>
<update id="updateDept" parameterClass="deptVO">
update DEPT
set DEPT_NAME = #deptName#,
LOC = #loc#
where DEPT_NO = #deptNo#
</update>
<delete id="deleteDept" parameterClass="deptVO">
delete from DEPT
where DEPT_NO = #deptNo#
</delete>
<select id="selectDeptList" parameterClass="deptVO" resultMap="deptResult">
<![CDATA[
select DEPT_NO,
DEPT_NAME,
LOC
from DEPT
where 1 = 1
]]>
<isNotNull prepend="and" property="deptNo">
DEPT_NO = #deptNo#
</isNotNull>
<isNotNull prepend="and" property="deptName">
DEPT_NAME LIKE '%' || #deptName# || '%'
</isNotNull>
</select>
</sqlMap>
/**************************************************************************************/
. /lab204-dataaccess/src/test/resources/META-INF/sqlmap/mappings/lab-emp-dept-resultmap.xml 에서 selectDeptIncludesEmpList 쿼리 관련 resultMap 과 sql 설정 확인
/**************************************************************************************/
<sqlMap namespace="EmpResultMap">
..
<typeAlias alias="deptIncludesEmpListVO" type="egovframework.lab.dataaccess.service.DeptIncludesEmpListVO" />
..
<!-- 1:N 인 경우 groupBy 속성을 명시 -->
<resultMap id="deptIncludesEmpListResult" class="deptIncludesEmpListVO" extends="Dept.deptResult" groupBy="deptNo">
<!-- Dept-EmpList 1:N relation -->
<result property="empVOList" resultMap="Emp.empResult" />
</resultMap>
..
<select id="selectDeptIncludesEmpList" parameterClass="deptVO" resultMap="deptIncludesEmpListResult">
<![CDATA[
select A.DEPT_NO as DEPT_NO,
DEPT_NAME,
LOC,
EMP_NO,
EMP_NAME,
JOB,
MGR,
HIRE_DATE,
SAL,
COMM
from DEPT A,
EMP B
where A.DEPT_NO = B.DEPT_NO
and A.DEPT_NO = #deptNo#
order by B.EMP_NO
]]>
</select>
</sqlMap>
/**************************************************************************************/
6) Testcase 작성
. /lab204-dataaccess/src/test/java/egovframework/lab/dataaccess/service/DeptServiceTest.java 를 작성한다.
/**************************************************************************************/
package egovframework.lab.dataaccess.service;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.math.BigDecimal;
import java.util.List;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.jdbc.SimpleJdbcTestUtils;
import org.springframework.transaction.annotation.Transactional;
import egovframework.rte.fdl.cmmn.exception.EgovBizException;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:META-INF/spring/context-*" })
@TransactionConfiguration(transactionManager = "txManager", defaultRollback = false)
@Transactional
public class DeptServiceTest {
@Resource(name = "dataSource")
DataSource dataSource;
@Resource(name = "deptService")
DeptService deptService;
@Before
public void onSetUp() throws Exception {
// 테스트 편의상 매 테스트메서드 수행전 외부의 sql file 로부터 DB 초기화
// (기존 테이블 삭제/생성)
SimpleJdbcTestUtils.executeSqlScript(
new SimpleJdbcTemplate(dataSource), new ClassPathResource(
"META-INF/testdata/sample_schema_hsql.sql"), true);
}
public DeptVO makeVO() {
return makeVO(90);
}
public DeptVO makeVO(int deptNo) {
DeptVO vo = new DeptVO();
vo.setDeptNo(new BigDecimal(deptNo));
vo.setDeptName("test 부서");
vo.setLoc("test 위치");
return vo;
}
public void checkResult(DeptVO vo, DeptVO resultVO) {
assertNotNull(resultVO);
assertEquals(vo.getDeptNo(), resultVO.getDeptNo());
assertEquals(vo.getDeptName(), resultVO.getDeptName());
assertEquals(vo.getLoc(), resultVO.getLoc());
}
@Test
public void testInsertDept() throws Exception {
DeptVO vo = makeVO();
// insert
deptService.insertDept(vo);
// select
DeptVO resultVO = deptService.selectDept(vo);
// check
checkResult(vo, resultVO);
}
@Test
public void testUpdateDept() throws Exception {
DeptVO vo = makeVO();
// insert
deptService.insertDept(vo);
// data change
vo.setDeptName("upd Dept");
vo.setLoc("upd loc");
// update
deptService.updateDept(vo);
// select
DeptVO resultVO = deptService.selectDept(vo);
// check
checkResult(vo, resultVO);
}
@Test
public void testDeleteDept() throws Exception {
DeptVO vo = makeVO();
// insert
deptService.insertDept(vo);
// delete
deptService.deleteDept(vo);
// select
try {
@SuppressWarnings("unused")
DeptVO resultVO = deptService.selectDept(vo);
fail("EgovBizException 이 발생해야 합니다.");
} catch (Exception e) {
assertNotNull(e);
// 여기서는 비지니스 단에서 명시적으로 exception 처리 하였음.
// AbstractServiceImpl 을 extends 하고
// processException("info.nodata.msg"); 과
// 같이 메서드 콜 형태로 처리
assertTrue(e instanceof EgovBizException);
assertEquals("info.nodata.msg", ((EgovBizException) e)
.getMessageKey());
assertEquals("해당 데이터가 없습니다.", e.getMessage());
}
}
@Test
public void testSelectDeptList() throws Exception {
DeptVO vo = makeVO();
// insert
deptService.insertDept(vo);
// 검색조건으로 key 설정
DeptVO searchVO = new DeptVO();
searchVO.setDeptNo(vo.getDeptNo());
// selectList
List<DeptVO> resultList = deptService.selectDeptList(searchVO);
// key 조건에 대한 결과는 한건일 것임
assertNotNull(resultList);
assertTrue(resultList.size() > 0);
assertEquals(1, resultList.size());
checkResult(vo, resultList.get(0));
// 검색조건으로 name 설정 - '%' || #deptName# || '%'
DeptVO searchVO2 = new DeptVO();
searchVO2.setDeptName(""); // '%' || '' || '%' --> '%%'
// selectList
List<DeptVO> resultList2 = deptService.selectDeptList(searchVO2);
// like 조건에 대한 결과는 한건 이상일 것임
assertNotNull(resultList2);
assertTrue(resultList2.size() > 0);
}
@Test
public void testSelectDeptIncludesEmpList() throws Exception {
DeptVO vo = new DeptVO();
vo.setDeptNo(new BigDecimal(20));
// select EmpIncludesDept
DeptIncludesEmpListVO resultVO = deptService.selectDeptIncludesEmpList(vo);
// check - 초기화 데이터에 따라 deptNo 20 인 data
assertEquals(new BigDecimal(20), resultVO.getDeptNo());
assertEquals("RESEARCH", resultVO.getDeptName());
assertEquals("DALLAS", resultVO.getLoc());
// check - dept's empList data
// 초기화 데이터에 따라
// EMP_NO EMP_NAME JOB MGR HIRE_DATE SAL COMM DEPT_NO
// ------ -------- ------- ---- ---------- ---- ------ -------
// 7369 SMITH CLERK 7902 1980-12-17 800 (null) 20
// 7566 JONES MANAGER 7839 1981-04-02 2975 (null) 20
// 7788 SCOTT ANALYST 7566 1987-04-19 3000 (null) 20
// 7876 ADAMS CLERK 7788 1987-05-23 1100 (null) 20
// 7902 FORD ANALYST 7566 1981-12-03 3000 (null) 20
assertEquals(5, resultVO.getEmpVOList().size());
assertEquals(new BigDecimal(7369), resultVO.getEmpVOList().get(0).getEmpNo());
assertEquals(new BigDecimal(7566), resultVO.getEmpVOList().get(1).getEmpNo());
assertEquals(new BigDecimal(7788), resultVO.getEmpVOList().get(2).getEmpNo());
assertEquals(new BigDecimal(7876), resultVO.getEmpVOList().get(3).getEmpNo());
assertEquals(new BigDecimal(7902), resultVO.getEmpVOList().get(4).getEmpNo());
}
}
/**************************************************************************************/
4. Sql 로깅 설정
1) /lab204-dataaccess/src/test/resources/META-INF/spring/jdbc.properties 를 다음과 같이 변경한다.
/**************************************************************************************/
driver=net.sf.log4jdbc.DriverSpy
dburl=jdbc:log4jdbc:hsqldb:mem:testdb
#dburl=jdbc:log4jdbc:hsqldb:hsql://localhost/sampledb
username=sa
password=
/**************************************************************************************/
2) /lab204-dataaccess/src/test/resources/log4j.xml 에 다음 Logger 를 추가한다.
/**************************************************************************************/
<!-- log SQL with timing information, post execution -->
<logger name="jdbc.sqltiming" additivity="false">
<level value="INFO" />
<appender-ref ref="console" />
</logger>
/**************************************************************************************/
[출처] http://blog.naver.com/heehow/140152784894
[출처] [eGovFrame] 전자정부표준프레임워크 교육 2일|작성자 주한길
'eGovFrame' 카테고리의 다른 글
[eGovFrame] 전자정부프레임워크 설치 및 셋팅 (0) | 2015.07.20 |
---|---|
[eGovFrame] 전자정부표준프레임워크 교육 3일 (0) | 2015.07.20 |
[eGovFrame] 전자정부표준프레임워크 교육 1일 (0) | 2015.07.20 |
[eGovFrame] 전자정부 개발프레임워크 개발환경 (Manual) (0) | 2015.07.20 |
[eGovFrame] 전자정부 개발환경 아키텍처구성 (0) | 2015.07.20 |