Query Service - Dynamic Query
Query 서비스는 text 치환, named parameter 형태 등을 통해 Dynamic 쿼리문 정의를 지원한다.
다음과 같은 syntax를 사용하면, 운영 시 입력된 조건 값에 따라 동적으로 쿼리문을 변환할 수 있다.
- :ParameterName
: 특정 쿼리문에 입력되어야 할 변수를 지정할 때 사용한다.
- {{치환 문자열 키}}
: 치환 문자열 키에 해당하는 문자열로 치환되어야 하는 부분에 정의한다.
- #if ~ (#elseif) ~ #end
: 조건 분기가 필요한 부분에 정의한다.
- # foreach ~ #end
: Loop가 필요한 부분에 정의한다.
- $velocityCount
: foreach 구문내의 Loop index를 체크하고자 하는 부분에 정의한다.
Query 서비스 속성 정의 파일 Sample
다음은 Query 서비스를 정의한
applicationContext-query-common.xml
과
Query 서비스에서 읽어들일 매핑 XML 파일의 위치를 정의한
applicationContext-query-sqlloader.xml
파일의 일부이다.
<bean id="queryService" class="anyframe.core.query.impl.QueryServiceImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
<property name="sqlRepository" ref="sqlLoader"/>
<!-- 중략 -->
</bean>
<bean id="jdbcTemplate" class="anyframe.core.query.impl.jdbc.PagingJdbcTemplate">
<property name="dataSource" ref="common_datasource" />
</bean>
<!-- 중략 -->
<bean name="sqlLoader" class="anyframe.core.query.impl.config.loader.SQLLoader">
<config:configuration>
<filename>classpath:/services/query/mappings/mapping-dynamic-query.xml</filename>
<!-- 중략 -->
</config:configuration>
</bean>
<!-- 중략 -->
매핑 XML 파일 Sample
다음은 앞서 정의한 Query 서비스를 통해 로드된
mapping-dynaminc-query.xml
로,
여러 유형의 dynamic 쿼리문을 포함하고 있다.
<queries>
<query id="getLogonIdByNamedParam" isDynamic="true">
<statement>
SELECT LOGON_ID
FROM TBL_USER
WHERE LOGON_ID BETWEEN :lowId AND :highId
</statement>
<param name="lowId" type="VARCHAR"/>
<param name="highId" type="VARCHAR"/>
</query>
<query id="getEmployeesByTextReplacement" isDynamic="true">
<statement>
SELECT LOGON_ID
FROM {{schema}}
ORDER BY {{sortColumn}}
</statement>
</query>
<query id="getLogonIdByIf" isDynamic="true">
<statement>
SELECT LOGON_ID
FROM TBL_USER
#if ($id && !$id.equals(""))
WHERE LOGON_ID like '%' || :logonId || '%'
#end
ORDER BY {{sortColumn}}
</statement>
<param name="logonId" type="VARCHAR"/>
</query>
<query id="getLogonIdByForeach" isDynamic="true">
<statement>
SELECT LOGON_ID, NAME
FROM TBL_USER
WHERE LOGON_ID IN (
#foreach ($logonId in $logonIdList)
#if ($velocityCount == 1 )
'$logonId'
#else
, '$logonId'
#end
#end
)
ORDER BY NAME
</statement>
</query>
</queries>
Query 서비스 테스트 코드 Sample
다음은 앞서 언급한 매핑 XML 파일에 정의된 parameter 값에 따라 동적으로 변경 가능한 쿼리문들을 실행하는 소스 코드
QueryServiceTestDynamic.java
를
구성하고 있는 메소드들이다. dynamic 쿼리문을 사용하면 소스 코드나 매핑 XML 파일을 고치지 않고도 조건에 따라서 원하는 쿼리문을 실행할 수 있다.
/**
* Named Parameter를 이용해서 query id가 'getLogonIdByNamedParam'인 Dynamic 쿼리문을 실행한다.
* getLogonIdByNamedParam : key가 lowId, highId인 Named Parameter의 값을 key=value 형태로 Query
* 서비스에 전달하면 Query 서비스는 해당 value를 PreparedStatement에 셋팅하고 해당 쿼리문을 실행한다.
* 이 메소드에서는 "lowId = a", "highId = z"라는 paramter를 Object[] 형태로 Query 서비스에 전달하고 있으며,
* 이 때 실행되는 쿼리문은 다음과 같다.
* SELECT LOGON_ID FROM TBL_USER WHERE LOGON_ID BETWEEN 'a' AND 'z'
*/
public void testDynamicQueryUsingNamedParameter() throws Exception {
IQueryService queryService = (IQueryService) context.getBean("queryService");
Object[] iVal = new Object[2];
iVal[0] = "lowId=a";
iVal[1] = "highId=z";
ArrayList rtList
= (ArrayList)(queryService.find("getLogonIdByNamedParam", iVal));
if( rtList.size() != 2){
throw new Exception("Dynamic Query Using NamedParameter failed");
}
}
/**
* {{치환문자열키}}를 이용해서 query id가 'getEmployeesByTextReplacement'인 Dynamic 쿼리문을 실행한다.
* getEmployeesByTextReplacement : key가 schema, sortColumn인 치환문자열의 값을 key=value 형태로 Query
* 서비스에 전달하면 Query 서비스는 해당 value를 문자열로 그대로 치환해서 사용한다.
* 이 메소드에서는 "schema=TBL_USER", "sortColumn=NAME"라는 parameter를 Object[] 형태로 Query 서비스에 전달하고
* 있으며, 이 때 실행되는 쿼리문은 다음과 같다.
* SELECT * FROM TBL_USER ORDER BY NAME
*/
public void testDynamicQueryUsingTextreplace() throws Exception {
IQueryService queryService = (IQueryService) context.getBean("queryService");
Object[] iVal = new Object[2];
iVal[0] = "schema=TBL_USER";
iVal[1] = "sortColumn=NAME";
ArrayList rtList
= (ArrayList)(queryService.find("getEmployeesByTextReplacement", iVal));
if( rtList.size() != 2){
throw new Exception("Dynamic Query Using Text replace failed");
}
}
/**
* 조건분기를 위한 #if를 이용해서 query id가 'getLogonIdByIf'인 Dynamic 쿼리문을 실행한다.
* getLogonIdByIf : key가 id인 parameter의 값이 Null이 아니고, 빈 문자열도 아니라면, #if문 내에
* 포함된 WHERE절이 실행될 쿼리문에 포함된다. (#if문이 끝나는 지점에는 반드시 #end를 정의해주어야 함에
* 유의하자.)
* 또한, WHERE절 내에서는 '%' || :logonId || '%'와 같은 형태의 문장을 사용하고 있는데 이것은 Named
* Parameter의 값에 앞뒤로 %를 붙인 형태의 문자열을 만들어 내기 위함이다.
* (이 때, '%', ||, :logonId 사이에는 빈 칸을 두어야 WHERE절이 정상적으로 동작한다. )
* 이 메소드에서는 "id=test", "sortColumn=NAME" 라는 parameter를 Object[] 형태로 Query 서비스에 전달하고 있으며
* 실행되는 쿼리문은 다음과 같다.
* SELECT LOGON_ID
* FROM TBL_USER
* WHERE LOGON_ID like '%test%'
* ORDER BY NAME
*/
public void testDynamicQueryUsingCondition() throws Exception {
IQueryService queryService = (IQueryService) context.getBean("queryService");
Object[] iVal = new Object[3];
iVal[0] = "id=yes";
iVal[1] = "logonId=test";
iVal[2] = "sortColumn=NAME";
ArrayList rtList
= (ArrayList)(queryService.find("getLogonIdByIf", iVal));
if( rtList.size() != 1){
throw new Exception("Dynamic Query Using Condition failed");
}
}
/**
* Loop를 위한 #foreach를 이용해서 query id가 'getLogonIdByForeach'인 Dynamic 쿼리문을 실행한다.
* getLogonIdByForeach : key가 logonIdList인 parameter의 값은 List 형태이며, List에서 순서대로
* 추출된 값들은 내부적으로 logonId라는 변수에 셋팅된다. logonId는 Query 서비스에 전달되어야 하는 입력
* parameter가 아니다.
* velocityCount가 1인 경우 즉, 첫번째 Loop일 경우 logonIdList의 첫번째 값이 그대로 추가되고 그 이후부터는
* ,를 붙인 값이 추가되게 된다. (#foreach, #if문이 끝나는 지점에는 반드시 #end를 정의해주어야 함에 유의하자.)
* 이 메소드에서는 logonIdList의 값이 "admin", "test"라는 두개의 문자열로 구성된 ArrayList를 Object[]
* 형태로 Query 서비스에 전달하고 있으며 실행되는 쿼리문은 다음과 같다.
* SELECT LOGON_ID, NAME
* FROM TBL_USER
* WHERE LOGON_ID IN ('admin', 'test')
* ORDER BY NAME
*/
public void testDynamicQueryUsingLoop() throws Exception {
IQueryService queryService = (IQueryService) context.getBean("queryService");
List logonIdList = new ArrayList();
logonIdList.add("admin");
logonIdList.add("test");
Object[] iVal = new Object[]{"logonIdList", logonIdList};
ArrayList rtList
= (ArrayList) (queryService.find("getLogonIdByForeach", new Object[]{ iVal }));
if (rtList.size() != 1) {
throw new Exception("Dynamic Query Using Condition failed");
}
}
앞서 소개된 샘플 테스트 코드를 포함하여 Query 서비스 소개 페이지에서 제공하는 모든 샘플 테스트 코드는 HSQL DB를
기반으로 실행된다. ( 단, ※ CallableStatement, LOB의 경우는 Oracle 9i, 10g를 기반으로 함. )
Resources
다운로드
샘플 테스트 코드를 포함하고 있는 anyframe-querytest-src.zip 파일을 다운받은 후, 테스트 환경 설정
을 참조하여
위에서 제시한 예제 코드를 실행해 볼 수 있다.
| Name
|
Download
|
| anyframe-querytest-src.zip |
Download
|