Query Service - Mapping XML Files

Query 서비스 초기화 시, Query 서비스는 속성 정의 파일에 정의되어 있는 매핑 xml 파일들을 로드한다. 그리고 사용자 요청 시 매핑 정보를 기반으로 실행하고자 하는 쿼리문을 query id를 이용해 찾아 실행한다.
매핑 XML 파일은 <queryservice>와 </queryservice>내에 크게 <table-mapping>와 <queries>로 구성된다. <table-mapping>와 <queries>는 필수 요소이므로 빠뜨리지 않도록 주의해야 한다.

table-mapping 정의 방법

<table-mapping> 내에 <table>를 이용하여, 테이블과 특정 클래스간의 매핑 정보들을 정의할 수 있다. <table-mapping> 내에는 여러 개의 <table>를 정의할 수 있다.
Tag Name
Description
Child Tag
table 테이블과 클래스간의 매핑 정보를 정의한다.
* attribute 설명
name: 해당 테이블명
class : 매핑 클래스명
field-mapping(필수)
primary-key(필수)
field-mapping 테이블의 칼럼과 이에 매핑되는 클래스의 attribute를 정의한다.
dbms-column(필수)
class-attribute(필수)
primary-key 해당 테이블의 Primary Key를 정의한다.
dbms-column(필수)
dbms-column 해당 테이블의 칼럼명을 정의한다.
class-attributes dbms-column에서 정의한 칼럼과 매핑되는 해당 클래스의 attribute명을 정의한다.

다음은 위에서 나열한 설정 정보들을 이용한 <table-mapping>의 설정 예제로, 테이블 TBL_CUSTOMER와 클래스 CustomerVO간의 매핑 정보를 담고 있다.
<queryservice>
    <table-mapping>
       <table name="TBL_CUSTOMER"
       class="integration.anyframe.services.query.vo.CustomerVO">
          <field-mapping>
             <dbms-column>ssno</dbms-column>
             <class-attribute>ssno</class-attribute>
          </field-mapping>
          <field-mapping>
             <dbms-column>name</dbms-column>
             <class-attribute>nm</class-attribute>
          </field-mapping>
          <field-mapping>
             <dbms-column>address</dbms-column>
             <class-attribute>addr</class-attribute>
          </field-mapping>
          <primary-key>
             <dbms-column>ssno</dbms-column>
          </primary-key>
       </table>
    </table-mapping>
    <!-- skipped -->
</queryservice>

queries 정의 방법

<queries> 내에 <query>를 이용하여, Query 서비스들을 통해 실행할 쿼리문들을 정의할 수 있다. <queries> 내에는 여러 개의 <query>를 정의할 수 있다.
Tag Name
Description
Child Tag
query 쿼리문을 정의 한다.
* attribute설명
isDynamic : 동적 쿼리인지 아닌지 식별 (Default=true)
isCamelCase : 조회 결과 매핑시 조회 칼럼명에 대해 CamelCase 적용할 것인지 정의 
(Default=true)
statement(필수)
lobStatement(선택)
param(필수)
result(선택)
statement 실행할 쿼리문을 정의한다. Joined 쿼리에서는 동일한 조회 칼럼명이 있을 경우, Alias를 부여해야 한다.
param Query 서비스는 해당 쿼리문을 미리 컴파일하여 PreparedStatement 형태로 저장하여 처리하고 있다. 따라서 입력값 셋팅을 위해 PreparedStatement에 setXXX를 수행하려면, 입력 Parameter에 해당하는 SQL Type을 java.sql.Types에 정의된 값을 참조하여 param tag의 attribute인 type의 값으로 정의한다. param tag는 입력 Parameter의 개수와 순서에 맞게 추가한다.
입력 Parameter의 데이터를 가져오기 위해서는 다음과 같은 기준에 따라 데이터 타입을 정의해야만 한다
   ---------------------------------------
   Java Type                DBMS Type
   ---------------------------------------
   String                   CHAR
   String                   VARCHAR
   String                   LONGVARCHAR
   java.math.BigDecimal	    NUMERIC
   java.math.BigDecimal	    DECIMAL
   boolean                  BIT
   byte                     TINYINT
   short                    SMALLINT
   int                      INTEGER
   long                     BIGINT
   float                    REAL
   double                   FLOAT
   double                   DOUBLE
   byte[]                   BINARY
   byte[]                   VARBINARY
   byte[]                   LONGVARBINARY
   java.sql.Date            DATE
   java.sql.Time            TIME
   java.sql.Timestamp       TIMESTAMP
   ---------------------------------------
* attribute 설정
type : parameter의 DBMS type
binding : CallableStatement경우 'IN','OUT','INOUT' 중 선택
name : CallableStatement경우 Stored Procedure 내에 정의된 변수 이름 정의
생략 시 VARCHAR로 인식됨
result 해당 쿼리가 조회를 위한 SELECT문일 경우에 사용할 수 있으며, 쿼리 수행 결과를 매핑할 클래스명을 정의한다. <result>가 지정되지 않았을 경우 쿼리 수행 결과에 대해 하나의 Row에 대해 칼럼명, 해당값을 쌍으로 Map에 put하고 각 Row별 Map들을 ArrayList에 담은 형태로 결과값을 리턴하게 된다. isCamelCase가 true인 경우 Map에 저장되는 칼럼명에는 CamelCase가 적용됨에 유의하자. (즉, 칼럼명이 USER_NAME인 경우 Map의 키값은 userName이 된다.)
* attribute 설명
length : 한 페이지에 보여질 데이터의 건수
class : 수행 결과를 저장할 클래스명
result-mapping(선택)
result-mapping 해당 쿼리가 조회를 위한 SELECT문일 경우에 사용할 수 있으며, 수행 결과를 저장할 클래스와 매핑되는 테이블이 존재하지 않을 경우, 조회된 칼럼별로 이에 매핑되는 해당 클래스의 attribute 명을 정의한다.
* attribute 설명
column : 조회된 칼럼명
attribute : 정의한 칼럼에 매핑되는 클래스의 attribute명

다음은 위에서 나열한 설정 정보들을 이용한 다양한 <queries>의 설정 예제이다.
  • 일반적인 사용
  • ECT문의 경우 특정 테이블과 매핑되는 클래스를 정의하지 않으면, 쿼리문 수행 결과는 각 Row에 대한 Map을 ArrayList에 담아 리턴된다. 각 Map으로부터 get("칼럼명")을 통해 해당 칼럼의 값을 얻을 수 있다.
    <queryservice>
        <!-- skipped -->
        <queries>
           <query id="selectGeneral" isDynamic="false">
              <statement>
                 SELECT * FROM TBL_CUSTOMER WHERE SSNO like ?
              </statement>
              <param type="VARCHAR" />
           </query>
        </queries>
    </queryservice>
  • <result-mapping>이 없고 <table-mapping>을 이용할 경우
  • 특정 클래스와 테이블 사이의 매핑 정보를 정의할 때 사용하며, 테이블과 특정 클래스 간의 매핑 정보를 정의해 두면 특정 조회문의 조회 결과를 매핑할 때 <result-mapping>없이 해당 클래스명만 <result>에 정의해 두면 되므로 XML 정의가 보다 간단해진다. 또한 별도 쿼리문 정의없이 객체만으로도 단건 데이터 생성/수정/삭제/조회가 가능해진다. <result class=""integration.anyframe.services.query.vo.CustomerVO"/>와 같이 table mapping시 사용한 클래스를 정의하면 쿼리문 수행 결과는 해당 클래스의 setter 호출을 통해 저장되고, getter를 호출함으로써 결과값을 얻을 수 있게 된다.
    <queryservice>
    	<table-mapping>
    		<table name="TBL_CUSTOMER"
    			class="integration.anyframe.services.query.vo.CustomerVO">
    			<field-mapping>
    				<dbms-column>ssno</dbms-column>
    				<class-attribute>ssno</class-attribute>
    			</field-mapping>
    			<field-mapping>
    				<dbms-column>name</dbms-column>
    				<class-attribute>nm</class-attribute>
    			</field-mapping>
    			<field-mapping>
    				<dbms-column>address</dbms-column>
    				<class-attribute>addr</class-attribute>
    			</field-mapping>
    			<primary-key>
    				<dbms-column>ssno</dbms-column>
    			</primary-key>
    		</table>
    	</table-mapping>    
        <queries>
           <query id="select" isDynamic="false">
              <statement>
                 SELECT * FROM TBL_CUSTOMER WHERE SSNO like ?
              </statement>
              <param type="VARCHAR" />
              <result class="integration.anyframe.services.query.vo.CustomerVO"/>
           </query>
        </queries>
    </queryservice>
  • <table-mapping>, <result-mapping>없이 <result>만을 이용할 경우
  • 매핑 대상 클래스의 속성명이 조회 칼럼명과 동일하거나 CamelCase된 형태이어서, <table-mapping>나 <result-mapping>를 통해 별도 매핑을 수행하지 않아도 되는 경우에 사용할 수 있다. 즉, <result>의 클래스 속성 정보에 정의된 클래스의 속성과 조회된 칼럼명을 대상으로 CamelCase를 적용하여 다음과 같이 동작한다.
    • 조회된 칼럼명을 CamelCase화하여 정의된 클래스의 속성중에서 찾아 매핑하고, 속성값 셋팅 (* 예를 들어 조회된 칼럼명이 USER_NAME이면, 매핑되는 속성명은 userName이 된다.)
    <queryservice>
        <!-- skipped -->
        <queries>
    		<query id="getUser" isDynamic="false">
    			<statement>
    				SELECT USER_ID, USER_NAME, PASSWORD, SSN, SL_YN, BIRTH_DAY, AGE, 
    				       CELL_PHONE, ADDR, EMAIL, EMAIL_YN, IMAGE_FILE, REG_DATE
    				FROM USERS
    				WHERE USER_ID = ?
    			</statement>
    			<param type="VARCHAR" />
    			<result class="com.sds.emp.user.services.UserVO" />			
    		</query>
        </queries>
    </queryservice>
    * Query 서비스는 내부적으로 쿼리문 수행으로 얻어진 조회 결과를 매핑할 때 다음과 같은 순서로 매핑 기준을 찾는다.
    1. 정의된 <result-mapping> 정보가 있으면 이를 기반으로 매핑
    2. 정의된 <result> 클래스에 대한 <table-mapping> 정보가 있으면 이를 기반으로 매핑
    3. 정의된 <result> 클래스에 대한 정보가 있으면 이를 기반으로 매핑
    4. HashMap에 isCamelCase 속성값을 기반으로 매핑

  • <result-mapping>을 이용할 경우
  • <result-mapping>은 <table-mapping>에 정의되지 않은 클래스이면서, 조회된 칼럼명과 매핑 클래스의 속성명이 연관성이 없어 별도 매핑이 필요한 경우 사용한다. 주로 joined 쿼리문에서 사용할 수 있으며, <result-mapping>에 column과 attribute값을 일대일로 매핑한다. <result class>에 정의된 클래스의 setter 호출을 통해 저장되고, getter 호출을 통해 결과값을 얻을 수 있다.
    <queryservice>
        <!-- skipped -->
        <queries>
           <query id="selectUsingResultMapping" isDynamic="false">
              <statement>SELECT * FROM TBL_CUSTOMER WHERE SSNO like ?</statement>
              <param type="VARCHAR"/>
              <result class="integration.anyframe.services.query.vo.CompositionCustomerVO">
                 <result-mapping column="NAME" attribute="nm"/>
                 <result-mapping column="ADDRESS" attribute="addr"/>
              </result>
           </query>
        </queries>
    </queryservice>
  • dynamic query인 경우
  • dynamic query를 사용하면 정의된 조건에 따라 쿼리문을 수정하지 않고도 원하는 쿼리문을 실행할 수 있다. #if($key.equals("true"))에서 'key'라는 키의 값이 true이면 OR NAME like '%test%'이 쿼리문에 추가되어 실행된다.
    <queryservice>
        <!-- skipped -->
        <queries>
           <query id="dynamicsample" isDynamic="true">
              <statement>select NAME, ADDRESS from TBL_CUSTOMER 
                 where NAME like :name
                      #if($key.equals("true"))
                        OR NAME like '%test%'
                      #end>
              </statement>
              <param name="name" type="VARCHAR"/>
           </query> 
        </queries>
    </queryservice>
  • LOB(Large Object)를 이용할 경우
  • LOB 유형의 데이터도 기본적으로 다른 데이터 유형들과 다르지 않으므로 매핑 xml도 일반적인 쿼리문 정의 방식과 같다.
    <queryservice>
        <!-- skipped -->
        <queries>
           <query id="lobsample" isDynamci="false">
              <statement>insert into binary values(?,?,?)</statement>
              <param type="INTEGER"/>
              <param type="BLOB"/>
              <param type="CLOB"/>
           </query>
        </queries>
    </queryservice>
  • CallableStatement일 경우
  • CallableStatement는 프로시져나 함수를 미리 DB에 설정해 놓고 그것을 호출해 오는 방식으로 일반 쿼리문보다 빠른 속도로 실행될 수 있다. 매핑 xml에서는 쿼리문을 직접 입력하지 않고 프로시져나 함수를 call하기만 하면 된다.
    <queryservice>
        <!-- skipped -->
        <queries>
           <query id="callablesample">
              <statement>
                 {? = call java_refcursor.job_listing (?)}
              </statement>
              <param type="CURSOR" binding="OUT" name="out_Val" />
              <param type="VARCHAR" binding="IN" name="in_Val" />
           </query>
        </queries>
    </queryservice>