'쿼리'에 해당되는 글 7

  1. 2011.01.18 java에서 xml을 이용한 쿼리에서 쿼리조건분기
------------------------------------------------------------

아무래도 현재 사용중인 프레임은 AnyFrame 이라는 것을 이용한 것인가보다...
xml에 #if, #end 같은 조건 분기가 들어가는 것을 보니...
검색해도 잘 나오지 않더니 오늘 우연히 찾았다...

XML에서 #if ~ #elseif ~ #end

검색을 어떻게 더 해야 조건 제어에 대한 내용을 더 찾을 수 있을지....

(쥔장 주석 끝)-----------------------------------------------


#startline====================================================

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

[출처] AnyFrame... http://dev.anyframejava.org/anyframe/doc/core/3.2.1/corefw/guide/query-usecase-dynamic.html

#endline=====================================================