목차 >> Data Access Object(DAO) +- GlueGenericDao +- GlueJdbcDao ----+- Glue Query File(SQL) ----+- Query Manager ----+- Activity에서의 dao 사용 ----+- GlueColumnManager ----+- Audit Data 처리 ----+- Reuse Activity +- GlueLazyJdbcDao ----+- GlueCursorBasedRowSet ----+- GlueLazyTransactionManagerHolder |
GlueJdbcDao는 SpringJDBC를 이용해서 추상화된 CRUD 오퍼레이션을 제공하는 구현체입니다.
GlueJdbcDao의 상속관계 및 메소드는 Java Doc을 참고합니다 (GlueAPI).
GlueJdbcDao의 필수 속성(property)는 다음과 같습니다.
<bean id="biz-dao" class="com.poscoict.glueframework.dao.jdbc.GlueJdbcDao"> <property name="dataSource" ref="dataSource"/> <property name="queryManager" ref="queryManager"/> </bean>
다음은 추가 속성(property)입니다.
GlueJdbcDao의 속성에 나와 있듯이 GlueJdbcDao를 사용하기 위해서는 관련 빈이 필요합니다.
GlueJdbcDao의 관련 bean은 그림과 같고, applicationContext.xml은 다음과 같습니다.
그림 : GlueJdbcDao의 bean들과의 관계 |
<bean id="dao-1" class="com.poscoict.glueframework.dao.jdbc.GlueJdbcDao"> <property name="dataSource" ref="dataSource-1"/> <property name="queryManager" ref="queryManager"/> </bean> <bean id="dataSource-1" class= . . . /> <bean id="transactionManager-1" class="com.poscoict.glueframework.transaction.GlueDataSourceTransactionManager"> <property name="dataSource" ref="dataSource-1"/> </bean> <bean id="dao-2" class="com.poscoict.glueframework.dao.jdbc.GlueJdbcDao"> <property name="dataSource" ref="dataSource-2"/> <property name="queryManager" ref="queryManager"/> </bean> <bean id="dataSource-2" class= . . . /> <bean id="transactionManager-2" class="com.poscoict.glueframework.transaction.GlueDataSourceTransactionManager"> <property name="dataSource" ref="dataSource-2"/> </bean> <bean id="queryManager" class= . . . /> <bean id="cacheManager" class= . . . /> <bean id="queryLoader" class= . . . />
빈(bean)들간의 관계성은 모두 자바 인터페이스를 기반으로 레퍼린싱 되고 있습니다. 그러므로, 실제 구현 된 소스 코드를 변경하지 않고도 빈 설정 파일만 수정 해주면 변경된 로직으로 작동하는 새로운 오브젝트를 각 빈들과 연관을 지을 수 있습니다.
다시 말해서 설정파일(applicationContext.xml) 재정의만을 통해서 새로 제작된 DAO, QueryManager 등을 손쉽게 교체할 수 있다는 것을 의미합니다. 결론적으로 유지보수성 및 확장성이 아주 뛰어납니다.
GlueJdbcDao 에서는 2가지 유형의 SQL을 사용할 수 있습니다.
SELECT * FROM EMP WHERE DEPTNO=? AND SAL>?
SELECT * FROM EMP WHERE DEPTNO=:deptno AND SAL>:sal
2가지 유형의 SQL은 Glue Query File에서 isNamed 속성을 통해 구분됩니다.
<?xml version="1.0" encoding="UTF-8"?> <queryMap desc="sql" xmlns="http://www.poscoict.com/glueframework/query"> <query id="sql.type.1" desc="기본SQL" isNamed="false"> <![CDATA[ SELECT * FROM EMP WHERE DEPTNO=? AND SAL>? ]]> </query> <query id="sql.type.2" desc="NamedSQL" isNamed="true"> <![CDATA[ SELECT * FROM EMP WHERE DEPTNO=:deptno AND SAL>:sal ]]> </query> </queryMap>
GlueSequence 를 통해 DB의 sequence값을 다음과 같이 얻어 올 수 있습니다.
GlueGenericJdbcDao dao = ...; GlueSequence sequence = dao.getSequence() long seq = sequence.getNextLongValue("SQ_BIZ_TABLE1");
GlueJdbcDao(GlueGenericJdbcDao) 는 DAO가 참고하는 DataSource를 이용해서 GlueSequence 를 자동으로 생성합니다.
oracle, db2 등의 일부 DB일 경우 자동으로 생성되며, 다음과 같은 INFO 레벨의 로그를 확인할 수 있습니다.
Sequence Increamenter is constructed successfully... : {}
그외 DB의 경우 GlueJdbcDao에서 자동으로 GlueSequence를 생성하지 않으며, 다음과 같은 INFO 레벨의 로그를 확인할 수 있습니다.\
Not support a sequence incrementer : {}
GlueJdbcDao 에서 지원하지 않을 경우, sequence property를 통해 사용하고자 하는 DB에 맞는 GlueSequece를 생성해서 사용합니다.
QueryManager의 구현체인 GlueQueryManagerImpl은 다음과 같은 속성을 갖습니다 (GlueAPI).
다음은 applicationContext.xml 예제입니다.
QueryManager를 사용하기 위해서는 관련 빈(bean)을 다음과 같이 추가할 수 있습니다.
<bean id="queryManager" class="com.poscoict.glueframework.dao.manager.GlueQueryManagerImpl"> <property name="cacheManager" ref="cacheManager"/> <property name="queryLoader" ref="queryLoader"/> </bean> <bean id="queryLoader" class="com.poscoict.glueframework.dao.manager.GlueQueryLoader"/> <bean id="cacheManager" class= . . . />
GlueQueryManagerImpl 이 참조하는 QueryLoader는 다음과 같은 속성을 갖을 수 있습니다 (GlueAPI).
<bean id="queryLoader" class="com.poscoict.glueframework.dao.manager.GlueQueryLoader"/>
<bean id="queryLoader" class="com.poscoict.glueframework.dao.manager.GlueQueryLoader"> <property name="rootPath" ref="path/biz"/> </bean>
<bean id="queryLoader" class="com.poscoict.glueframework.dao.manager.GlueQueryLoader"> <property name="extraQueryFiles"> <list> <value>path/biz1-query.glue_sql</value> <value>path/biz2-query.glue_sql</value> </list> </property> </bean>
GlueJdbcDao는 2종류의 Query를 사용할 수 있습니다.
SELECT * FROM EMP WHERE DEPTNO=? AND SAL>?
public class BizActivity extends GlueActivity<GlueContext> { public String runActivity( GlueContext ctx ) { Object deptno = . . .; Object sal = . . .; String queryId = . . .; List paramList = new ArrayList(); paramList.add( deptno ); paramList.add( sal ); GlueParameter<List> param = new GlueParameter<List>(); param.setParameter( paramList ); // GlueParameter<List> param = new GlueParameter<List>( paramList ); GlueGenericDao dao = this.getDao( "biz-dao" ); // this.getDao( this.getProperty( "dao" ) ) List<Map> result = dao.find( queryId, param ); . . .
Query File을 사용하지 않은 경우는, 즉 Java Code에 다음과 같이 SQL을 포함할 수 있습니다.
List<Map> result = dao.findByQueryStatement("SELECT * FROM EMP WHERE DEPTNO=? AND SAL>?", param)
SELECT * FROM EMP WHERE DEPTNO=:deptno AND SAL>:sal
public class BizActivity extends GlueActivity<GlueContext> { public String runActivity( GlueContext ctx ) { Object deptno = . . .; Object sal = . . .; String queryId = . . .; Map paramMap = new HashMap(); paramMap.put( "deptno", deptno ); paramMap.put( "sal", sal ); GlueParameter<Map> param = new GlueParameter<Map>(); param.setParameter( paramMap ); // GlueParameter<Map> param = new GlueParameter<Map>( paramMap ); GlueGenericDao dao = this.getDao( "biz-dao" ); // this.getDao( this.getProperty( "dao" ) ) List<Map> result = dao.find( queryId, param ); . . .
Query File을 사용하지 않은 경우는, 즉 Java Code에 다음과 같이 SQL을 포함할 수 있습니다.
result = dao.findByQueryStatement("SELECT * FROM EMP WHERE DEPTNO=:deptno AND SAL>:sal", param)
ColumnManager는 dao의 find() 실행결과를 List<Map> 구조에서 GlueRowSet<GlueRow>의 구조로 변환하는 기능을 갖습니다. 이러한 변환과정을 통해 GlueRowSet은 GlueColumnDef 정보를 포함하게 됩니다.
이러한 기능은 기본SQL(? 사용)일 경우 지원됩니다.
GlueColumnManager는 다음과 같은 속성을 갖습니다 (GlueAPI).
<bean id="dao" class="com.poscoict.glueframework.dao.jdbc.GlueJdbcDao"> . . . <property name="columnManager" ref="columnManager"/> </bean> <bean id="columnManager" class="com.poscoict.glueframework.dao.manager.GlueColumnManager"> <property name="cacheManager" ref="cacheManager"/> </bean> <bean id="cacheManager" class= . . . />
다음은 GlueColumnManager 사용과 관련된 Class입니다.
<bean id="biz-dao" class="com.poscoict.glueframework.dao.jdbc.GlueJdbcDao"> <property name="dataSource" ref="dataSource"/> <property name="queryManager" ref="queryManager"/> <property name="columnManager" ref="columnManager"/> </bean> <bean id="columnManager" class="com.poscoict.glueframework.dao.manager.GlueColumnManager"> <property name="cacheManager" ref="cacheManager"/> </bean>
public class SampleActivity extends GlueActivity<GlueContext> { @Override public String runActivity( GlueContext ctx ) { GlueGenericJdbcDao dao = (GlueGenericJdbcDao)this.getDao("biz-dao"); List paramList = . . . GlueRowSet rowSet = (GlueRowSet)dao.findByQueryStatement("select * from EMP where deptno=?", new GlueParameter<List>(paramList) ); GlueColumnDef[] def = rowSet.getColumnDefs(); while(rowSet.hasNext()) { GlueRow row = rowSet.next(); for(int i=0, iz=def.length; i < iz; i++) { System.out.println("ColumnDef - Name : "+def[i].getName()); System.out.println("ColumnDef - Type : "+def[i].getType()); System.out.println("ColumnDef - Length : "+def[i].getLength()); System.out.println("ColumnDef - Precision : "+def[i].getPrecision()); System.out.println("ColumnDef - Scale : "+def[i].getScale()); System.out.println("Data : "+row.getAttribute(def[i].getName())); } } return GlueBizControlConstants.SUCCESS; } }
관계형 데이타베이스에서 모든 테이블이 not null 제약조건의 audit 컬럼들을 동일하게 포함할 때, insert/update 수행시 해당 컬럼값을 자동으로 binding 해 줄 수 있습니다.
이를 위해 glue.property에 다음 property가 포함되어 있어야 합니다.
여러 개의 Audit항목이 존재 할 경우 컴마(,)를 구분자로 사용합니다. Audit 항목이 시간정보일 경우 timestamp 유형은 #current_timestamp# 를 붙여주고, Date 유형은#current_date# 를 붙여줍니다.
glue.properties 가 다음과 같을 경우 SQL 유형별 예제입니다.
audit.insert.key=cid,cip,cdt#current_timestamp#,uid,uip,udt#current_timestamp# audit.update.key=uid,uip,udt#current_timestamp#
insert into EMP_AUDIT( CREATE_ID, CREATE_IP, CREATE_DT, UPDATE_ID, UPDATE_IP, UPDATE_DT, EMPNO, ENAME, SAL, DEPTNO ) values(?,?,?,?,?,?,?,?,?,?)
기본 SQL이 위와 같을 때 GlueParameter의 setAuditAttributes() 를 사용한 Java Code 예제입니다.
List<Object> paramList = new ArrayList<Object>(); paramList.put( 1118 ); //EMPNO paramList.put( "USER8" ); //ENAME paramList.add( 7000 ); //SAL paramList.put( 10 ); //DEPTNO param = new GlueParameter<ArrayList<Object>>( paramList ); param.setAuditAttributes( ctx.getAuditAttributes(); ); dao.insert( queryid, param );
기본 SQL이 위와 같을 때 reuse activity의 is-audit property를 사용한 예제입니다.
<activity name="Insert" class="com.poscoict.glueframework.biz.activity.GlueJdbcInsert"> <property name="dao" value=". . ." /> <property name="sql-key" value=". . ." /> <property name="param-count" value="4" /> <property name="param0" value="EMPNO" /> <property name="param1" value="ENAME" /> <property name="param2" value="SAL" /> <property name="param3" value="DEPTNO" /> <property name="is-audit" value="true" /> <transition name="success" value=". . ." /> </activity>
insert into EMP_AUDIT (EMPNO, ENAME, SAL, DEPTNO, CREATE_ID, CREATE_IP, CREATE_DT, UPDATE_ID, UPDATE_IP, UPDATE_DT )values(:empno, :ename, :sal, :deptno, :cid, :cip, :cdt, :uid, :uip, :udt )
Named SQL이 위와 같을 때 GlueParameter의 setAuditAttributes() 를 사용한 Java Code 예제입니다.
Map<String, Object> paramMap = new HashMap<String, Object>(); paramMap.put( "sal", 7000 ); paramMap.put( "ename", "USER8" ); paramMap.put( "empno", 1118 ); paramMap.put( "deptno", 10 ); param = new GlueParameter<HashMap<String, Object>>( paramMap ); param.setAuditAttributes( ctx.getAuditAttributes(); ); dao.insert( queryid, param );
기본 SQL이 위와 같을 때 reuse activity의 is-audit property를 사용한 예제입니다.
<activity name="Insert" class="com.poscoict.glueframework.biz.activity.GlueJdbcInsert"> <property name="dao" value=". . ." /> <property name="sql-key" value=". . ." /> <property name="param-bindings" value="empno=EMPNO|ename=ENAME|sal=SAL|deptno=DEPTNO" /> <property name="is-audit" value="true" /> <transition name="success" value=". . ." /> </activity>
다음은 Core >> Glue Service 부분에서 'Glue Service 실행 Layer' 부분에 나타나는 Control Layer에 해당하는 Class입니다. 아래 Class 에서는 Glue Service 실행 전에 glue.properties에 Audit 항목을 위한 property가 있는지 확인합니다. 있다면 GlueAuditAttributes를 생성하여 GlueContext에 설정하는 로직이 수행됩니다.
다음 코드를 참고해서 필요시 GlueAuditAttributes 를 생성할 수 있습니다. 즉, Control Layer에 해당하는 Class를 만들거나, Audit Data를 변경하고자 하는 경우 입니다.
if ( GlueStaticContext.isAudit() ) { ctx.setAuditAttributes( new GlueAuditAttributes( ctx ) ); }
if ( GlueStaticContext.isAudit() ) { Map map = new HashMap() map.put( "cid", id ); map.put( "cip", ip ); map.put( "uid", id ); map.put( "uip", ip ); ctx.setAuditAttributes( new GlueAuditAttributes( map ) ); }
GlueJdbcDao를 사용한 reuse Activity는 다음과 같은 것이 제공되고 있습니다.
GlueLazyJdbcDao 의 상속관계 및 메소드는 Java Doc을 참고합니다 (GlueAPI).
GlueLazyJdbcDao 의 필수 속성(property)는 다음과 같습니다.
다은은 applicationContext.xml 예입니다. $query 부분은 조회용 SQL 로 치환되므로, 이를 고려해서 countQuery 값을 설정합니다.
<bean id="biz-dao" class="com.poscoict.glueframework.dao.jdbc.GlueLazyJdbcDao"> <property name="dataSource" ref="dataSource"/> <property name="queryManager" ref="queryManager"/> <property name="fetchSize" ref="100"/> <property name="countQuery" ref="select count(*) from (${query})"/> </bean>
다음은 추가 속성(property)입니다.
GlueLazyJdbcDao의 속성에 나와 있듯이 GlueLazyJdbcDao를 사용하기 위해서는 관련 빈이 필요합니다.
GlueLazyJdbcDao의 관련 bean은 그림과 같고, applicationContext.xml은 다음과 같습니다.
그림 : GlueLazyJdbcDao의 bean들과의 관계 |
<bean id="dao-1" class="com.poscoict.glueframework.dao.jdbc.GlueLazyJdbcDao"> <property name="dataSource" ref="dataSource-1"/> <property name="queryManager" ref="queryManager"/> <property name="fetchSize" ... /> <property name="countQuery" ... /> </bean> <bean id="transactionManager-1" class="com.poscoict.glueframework.transaction.GlueLazyDataSourceTransactionManager"> <property name="dataSource" ref="dataSource-1"/> </bean> <bean id="dataSource-1" class= . . . /> <bean id="dao-2" class="com.poscoict.glueframework.dao.jdbc.GlueJdbcDao"> <property name="dataSource" ref="dataSource-2"/> <property name="queryManager" ref="queryManager"/> <property name="fetchSize" ... /> <property name="countQuery" ... /> </bean> <bean id="transactionManager-2" class="com.poscoict.glueframework.transaction.GlueLazyDataSourceTransactionManager"> <property name="dataSource" ref="dataSource-2"/> </bean> <bean id="dataSource-2" class= . . . /> <bean id="queryManager" class= . . . /> <bean id="cacheManager" class= . . . /> <bean id="queryLoader" class= . . . />
GlueLazyJdbcDao의 경우 GlueLazyDataSourceTransactionManager과 같이 사용하도록 합니다.
Controller Layer에서 GlueLazyTransactionManagerHolder 를 통해 Transaction 처리가 되도록 해야합니다.
GlueCursorBasedRowSet 은 내부적으로 java.sql.Connection, java.sql.Statement, java.sql.ResultSet 을 갖는 특별한 클래스입니다.
GlueLazyJdbcDao 를 통해 조회용 SQL이 실행되는 경우 그 결과값은 List<Map> 형태의 Collection객체와는 다릅니다.
GlueCursorBasedRowSet 은 java.util.List 인터페이스를 확장하였으나, 사용가능한 method는 다음과 같습니다.
GlueLazyJdbcDao 에 GlueColumnManager 가 설정된 경우, 조회용 SQL의 실행결과에 GlueColumnDef 를 포함합니다.
GlueQueryDefinition의 resultType 에 해당하는 객체는 반환합니다. default는 java.util.Map 으로 반환합니다.
다음은 GlueQueryEditor와 JavaCode 예입니다.
<query id="emp.select.map" desc="Map"> <![CDATA[ SELECT * FROM EMP ]]> </query>
GlueGenericJdbcDao dao = ... GlueCursorBasedRowSet rowSet = (GlueCursorBasedRowSet)dao.find("emp.select.map"); while(rowSet.hasNext()){ Map row = rowSet.next(); empno = row.get("empno"); }
다음은 GlueQueryEditor와 JavaCode 예입니다.
<query id="emp.select.vo" desc="VO" resultType="sample.vo.EmpVO"> <![CDATA[ SELECT * FROM EMP ]]> </query>
GlueGenericJdbcDao dao = ... GlueCursorBasedRowSet rowSet = (GlueCursorBasedRowSet)dao.find("emp.select.map"); while(rowSet.hasNext()){ EmpVO row = rowSet.next(); empno = row.getEmpno(); }
clear()가 실행된 경우, hasNext(), next(), reset() 메소드를 사용할 수 없습니다.
clear()가 실행되는 경우, ResultSet, Statement, Connection을 반납합니다.
GlueService에서 GlueLazyJdbcDao가 사용된 경우, Service Layer에서 Transcation 종료처리를 할 수 없습니다.
Service Layer에서는 GlueService에 설정된 Transaction Manager를 따로 GlueLazyTransactionManagerHolder 에 담아두어 Control Layer 에서 처리할 수 있도록 하고 있습니다.
다음은 Control Layer에 해당하는 클래스입니다.
다은 유형의 Control layer가 필요한 경우 아래 코드를 참고해서 작성하도록 합니다.
boolan isError = . . . List<GlueLazyTransactionManager> txList = GlueLazyTransactionManagerHolder.get(); if ( txList != null ) { for ( int i = txList.size() - 1; i > -1; i-- ) { GlueLazyTransactionManager transactionManager = txList.get( i ); try { if ( isError ) { transactionManager.lazyRollback(); } else { transactionManager.lazyCommit(); } } catch ( GlueException e ) { . . . } } } GlueLazyTransactionManagerHolder.clear();