제4장 데이터 오브젝트/데이터 오브젝트 팩토리 개발

내용 목차

4.1. 개요
4.2. 환경설정
4.2.1. 시스템 설정
4.2.2. 애플리케이션 설정
4.2.3. 서비스 그룹 설정
4.2.4. 서비스 설정
4.3. 데이터 오브젝트
4.3.1. 개발 방법
4.3.2. 데이터 오브젝트 API
4.3.3. 맵 데이터 오브젝트
4.4. DB 데이터 오브젝트 팩토리
4.4.1. 구조
4.4.2. 개발 방법
4.4.3. DB 데이터 오브젝트 팩토리 API
4.4.4. BeforeImage 기능
4.5. DataDefinitionExecutor
4.6. 쿼리 오브젝트
4.6.1. 개발 방법
4.6.2. 쿼리 오브젝트 API
4.7. 데이터 오브젝트 매퍼
4.7.1. 개발 방법
4.7.2. 데이터 오브젝트 매퍼 API
4.8. 파일 데이터 오브젝트 팩토리
4.8.1. 구조
4.8.2. 개발 방법
4.8.3. 파일 데이터 오브젝트 팩토리 API
4.9. 비동기 데이터 오브젝트
4.9.1. 비동기 데이터 오브젝트 API
4.9.2. AsyncDBOperationResult
4.9.3. 개발 과정
4.10. 데이터 오브젝트 캐시
4.10.1. 캐시 사용이 가능한 DO 생성
4.10.2. 세션 캐시
4.10.3. 2차 캐시

본 장에서는 데이터 오브젝트와 데이터 오브젝트 팩토리의 개념과 특징, 개발 방법에 대해 설명한다.

4.1. 개요

데이터 오브젝트(DataObject, DO)는 ProObject 모듈간 데이터 전달의 기본 단위이며, 서비스 오브젝트의 입출력의 정해진 형식이다.

데이터 오브젝트 팩토리(DataObjectFactory, DOF)는 ProObject에서 DB와 FILE IO 작업을 담당하는 모듈이다. DBIO는 DB 데이터 오브젝트 팩토리를 통해 동작하고, FILEIO는 파일 데이터 오브젝트 팩토리를 통해 개발할 수 있다. 각각의 팩토리는 기반이 되는 데이터 오브젝트가 존재하며, 팩토리는 데이터 오브젝트에 1대1로 매핑하여 개발한다. 데이터 오브젝트 팩토리는 DB 데이터 오브젝트 팩토리와 파일 데이터 오브젝트 팩토리로 나뉜다.

4.2. 환경설정

데이터 오브젝트 및 데이터 오브젝트 팩토리와 관련된 옵션은 ProManager를 통해 아래 파일에 설정된다.

4.2.1. 시스템 설정

ProObject 엔진의 가장 기본적인 설정은 다음의 파일에 Key-Value 방식을 이용해 설정한다.

${PROOBJECT_HOME}/config/system.properties

다음은 system.properties의 설정항목에 대한 설명이다.

SYSTEM_DATAOBJECT_QUERY_LOGGING = [SEVERE|WARNING|INFO|CONFIG|FINE|FINER|FINEST|OFF]
SYSTEM_DATAOBJECT_BANNED_PARAMETER = parmeter1,parameter2....
SYSTEM_DATAOBJECT_DEFAULT_PAGE_SIZE = page_size
SYSTEM_DATAOBJECT_MAX_PAGE_SIZE = max_page_size
SYSTEM_DATAOBJECT_PAGE_QUERY = page_query
SYSTEM_DATAOBJECT_PAGE_HINT = page_hint
SYSTEM_DATAOBJECT_DEFAULT_CACHE_SIZE = cache_size
SYSTEM_DATAOBJECT_MAX_ROW_SIZE = max_row_size
SYSTEM_DATAOBJECT_LIMIT_ROW_SIZE = [true|false]
SYSTEM_DATAOBJECT_FETCH_SIZE = fatch_size
SYSTEM_DATAOBJECT_BATCH_MAX_SIZE = batch_max_size
SYSTEM_DATAOBJECT_SECOND_CACHE = second_cache_class
SYSTEM_DATAOBJECT_CACHE_ENABLE = [true|false]

SYSTEM_MESSAGE_FLD_MARSHAL_NUMBER_FILL_TYPE = [zero|space]
SYSTEM_MESSAGE_FLD_UNMARSHAL_NUMBER_FILL_TYPE = [zero|space]

SYSTEM_FILE_DATAOBJECT_CHARSET = [UTF-8|EUC-KR]

항목설명
SYSTEM_DATAOBJECT_QUERY_LOGGING

데이터 오브젝트 팩토리가 수행한 쿼리를 로깅할 로그 레벨을 지정한다. 쿼리는 서비스 로그에 남는다.

ProObject에 설정된 엔진 로그 설정과 비교하여 쿼리 로깅 레벨이 엔진 로거의 로그 레벨보다 같거나 더 높은 경우 로깅한다.

다음 중에 설정한다.

  • SEVERE

  • WARNING

  • INFO

  • CONFIG

  • FINE

  • FINER

  • FINEST

  • OFF(기본값)

SYSTEM_DATAOBJECT_BANNED_PARAMETER데이터 오브젝트 팩토리가 쿼리를 수행할 때 파라미터 값으로 들어오면 안되는 금칙어를 설정한다. 금칙어 목록은 콤마(,)를 구분자로 구분하여 연속해서 입력한다.
SYSTEM_DATAOBJECT_DEFAULT_PAGE_SIZEDB 데이터 오브젝트 팩토리가 제공하는 페이징 기능을 사용할 때 한 페이지에 담을 데이터 수를 지정한다. (기본값: 10)
SYSTEM_DATAOBJECT_MAX_PAGE_SIZE

SYSTEM_DATAOBJECT_DEFAULT_PAGE_SIZE 옵션값으로 지정할 수 있는 최대 페이지 수를 제한한다.

해당 옵션 값이 0인 경우 이 옵션은 동작하지 않는다. (기본값: 0)

SYSTEM_DATAOBJECT_PAGE_QUERY

페이징 처리를 하는 경우 사용할 페이징 쿼리문을 입력한다.

ProObject은 기본으로 Oracle과 Tibero에서 사용할 수 있는 페이징 쿼리를 제공한다. 사용하는 DBMS가 Tibero나 Oracle이 아닌 경우 본 옵션에 페이징 처리를 위한 쿼리를 입력한다.

(기본값: Tibero, Oracle 기준 페이징 처리법)

SYSTEM_DATAOBJECT_PAGE_HINTDB 데이터 오브젝트 팩토리에서 페이징 처리를 하는 경우 쿼리 옵티마이저 힌트를 설정한다. (기본값: EMPTY String)
SYSTEM_DATAOBJECT_DEFAULT_CACHE_SIZEProObject에서 제공하는 세션 캐시를 사용하는 경우 캐시 크기를 지정한다. (기본값: 5000)
SYSTEM_DATAOBJECT_MAX_ROW_SIZEDB 데이터 오브젝트 팩토리를 이용하여 다건 조회하는 경우 조회 가능한 최대 건수를 설정한다. (기본값: 0)
SYSTEM_DATAOBJECT_LIMIT_ROW_SIZE

SYSTEM_DATAOBJECT_MAX_ROW_SIZE 옵션의 제한값을 적용할지 여부를 설정한다.

  • true : SYSTEM_DATAOBJECT_MAX_ROW_SIZE 옵션의 갯수를 기준으로 조회를 제한한다.

  • false : SYSTEM_DATAOBJECT_MAX_ROW_SIZE 옵션의 갯수를 기준으로 조회를 제한하지 않는다. (기본값)

SYSTEM_DATAOBJECT_FETCH_SIZE

DB 데이터 오브젝트 팩토리에서 조회 쿼리를 수행하는 경우 조회할 페치(FETCH) 크기를 지정한다.

옵션값은 java.sql.PreparedStatement의 setFecthSize()를 통해 설정한다. (기본값: 300)

SYSTEM_DATAOBJECT_BATCH_MAX_SIZEDB 데이터 오브젝트 팩토리의 addBatch 기능을 사용할 경우 addBatch가 가능한 최대 건수를 설정한다. 해당 갯수만큼 addBatch가 쌓이면 자동으로 executeBatch를 수행한다. (기본값: 0)
SYSTEM_DATAOBJECT_SECOND_CACHE

2차 캐시를 핸들링할 com.tmax.proobject.dataobject.cache.IProObjectCacheManager 인터페이스를 구현한 클래스를 입력한다.

Tmax의 InfiniCache를 2차 캐시로 사용할 경우 com.tmax.proobject.dataobject.cache.InfiniCacheManager를 값으로 설정한다.

SYSTEM_DATAOBJECT_CACHE_ENABLE

캐시 기능을 ON/OFF하기 위한 설정이다. 이 옵션값이 true에서 false로 변경되면 캐시 영역을 invalidate한다.

  • true : ProObject의 DO 캐시 기능을 활성화한다.

  • false : ProObject의 DO 캐시 기능을 비활성화한다. (기본값)

해당 옵션은 반드시 SYSTEM_DATAOBJECT_SECOND_CACHE와 APPLICATION_DATAOBJECT_CACHE 설정과 함께 사용해야 한다.

SYSTEM_MESSAGE_FLD_MARSHAL_NUMBER_FILL_TYPE

데이터 오브젝트를 메시지화할 경우 숫자 타입의 필드의 여백을 무엇으로 채울지에 대한 옵션이다.

  • zero : 여백을 '0'으로 채운다. (기본값)

  • space: 여백을 공백으로 채운다.

SYSTEM_MESSAGE_FLD_UNMARSHAL_NUMBER_FILL_TYPE

ProObject가 받은 메시지를 데이터 오브젝트화할 경우 숫자 타입문자의 여백을 인식하는 방법을 설정한다.

  • zero : '0'을 여백으로 인식한다. (기본값)

  • space : 공백을 여백으로 인식한다.

SYSTEM_FILE_DATAOBJECT_CHARSET

파일 데이터 오브젝트 팩토리를 이용하여 파일을 쓰는 경우 사용할 CharSet을 지정한다.

다음 중에 하나를 설정한다.

  • UTF-8 (기본값)

  • EUC-KR

4.2.2. 애플리케이션 설정

ProObject 엔진의 가장 기본적인 설정은 다음의 파일에 Key-Value 방식을 이용해 설정한다.

${APP_HOME}/config/application.properties

다음은 application.properties의 설정항목에 대한 설명이다.

APPLICATION_DATAOBJECT_CACHE = dataobject_cache
APPLICATION_DATAOBJECTFACTORY_QUERY_LOGGING_LEVEL = [SEVERE|WARNING|INFO|CONFIG|FINE|FINER|FINEST|OFF]
APPLICATION_DATAOBJECT_CONFIG_LOG_LEVEL = [SEVERE|WARNING|INFO|CONFIG|FINE|FINER|FINEST|OFF]
APPLICATION_DATAOBJECTFACTORY_DEFAULT_PAGE_SIZE = page_size
APPLICATION_DATAOBJECTFACTORY_PAGE_HINT = paging_query_hint
APPLICATION_DATAOBJECTFACTORY_MAX_ROW_SIZE = maximum_row_number
APPLICATION_DATAOBJECTFACTORY_LIMIT_ROW_SIZE = [true|false]
APPLICATION_DATAOBJECTFACTORY_SINGLE_RESULT_CHECK = [true|false]
APPLICATION_DATAOBJECTFACTORY_FETCH_SIZE = fetch_size
APPLICATION_DATAOBJECTFACTORY_QUERY_TIMEOUT = query_timeout
APPLICATION_DATAOBJECTFACTORY_EMPTY_RESULT_CHECK = [true|false]
APPLICATION_DATAOBJECTFACTORY_ENCRYPTMODULE_ENABLE = [true|false]
APPLICATION_DATAOBJECTFACTORY_ENCRYPTMODULE_PATH = encryptmodule_class
항목설명
APPLICATION_DATAOBJECT_CACHE

캐시 기능을 사용할 데이터 오브젝트 팩토리 리스트를 지정한다.

데이터 오브젝트 팩토리 캐시 기능은 애플리케이션의 데이터 오브젝트 팩토리별로 ON/OFF가 가능하며, 리스트는 콤마(,)을 통해 구분한다. (기본값: EMPTY String)

APPLICATION_DATAOBJECTFACTORY_QUERY_LOGGING_LEVEL

데이터 오브젝트 팩토리가 실행한 쿼리문을 출력하는 로그 레벨을 지정한다.

ProObject에 설정된 엔진 로그 설정과 비교하여 쿼리 로깅 레벨이 엔진 로거의 로그 레벨보다 같거나 더 높은 경우 로깅한다.

다음 중에 설정한다.

  • SEVERE

  • WARNING

  • INFO

  • CONFIG

  • FINE

  • FINER

  • FINEST

  • OFF(기본값)

APPLICATION_DATAOBJECT_CONFIG_LOG_LEVEL

데이터 오브젝트 팩토리가 사용 중인 옵션 값들을 출력하는 로그 레벨을 지정한다.

다음 중에 설정한다.

  • SEVERE

  • WARNING

  • INFO

  • CONFIG (기본값)

  • FINE

  • FINER

  • FINEST

  • OFF

APPLICATION_DATAOBJECTFACTORY_DEFAULT_PAGE_SIZE다건 조회하는 경우 DB 데이터 오브젝트 팩토리가 조회할 페이지의 기본 사이즈를 지정한다. (기본값: 10)
APPLICATION_DATAOBJECTFACTORY_PAGE_HINT다건 조회하는 경우 페이징 기능을 사용할 경우 페이징 쿼리에 포함할 쿼리 힌트를 지정한다.
APPLICATION_DATAOBJECTFACTORY_SINGLE_RESULT_CHECKDB 데이터 오브젝트 팩토리의 get() API를 이용하여 단건 조회를 수행하는 경우 조회 결과가 2건 이상인 경우 com.tmax.proobject.dataobject.exception.MaxRowException을 발생시킨다.
APPLICATION_DATAOBJECTFACTORY_FETCH_SIZE

DB 데이터 오브젝트 팩토리에서 조회 쿼리를 수행하는 경우 조회할 페치 크기를 지정한다.

옵션값은 java.sql.PreparedStatement의 setFecthSize()를 통해 설정한다. (기본값: 300)

APPLICATION_DATAOBJECTFACTORY_QUERY_TIMEOUT

DB 데이터 오브젝트 팩토리가 쿼리를 수행할 때의 타임아웃을 지정한다. 옵션값은 java.sql.PreparedStatement의 setQueryTimeout()을 통해 설정한다.

설정하지 않는 경우 데이터소스를 제공하는 WAS의 쿼리 타임아웃 설정을 따른다. (기본값: 0, 단위: second)

APPLICATION_DATAOBJECTFACTORY_EMPTY_RESULT_CHECKDB 데이터 오브젝트 팩토리의 get, getForwardList, getScrollableList, getDataObjectList를 통해 데이터를 조회할 때 결과가 없는 경우 com.tmax.proobject.dataobject.exception.EmptyResultException을 발생시킨다.
APPLICATION_DATAOBJECTFACTORY_ENCRYPTMODULE_ENABLEDB 데이터 오브젝트 팩토리의 데이터 오브젝트 멤버변수 암호화 기능을 ON/OFF하기 위한 설정값이다.
APPLICATION_DATAOBJECTFACTORY_ENCRYPTMODULE_PATH데이터 오브젝트 멤버변수를 암호화하는 경우 사용할 클래스 패스를 지정한다(com.tmax.proobject.dataobject.security.IEncryptUtil을 상속받은 클래스로 지정한다).
APPLICATION_DATAOBJECTFACTORY_MAX_ROW_SIZE

DB 데이터 오브젝트 팩토리를 이용하여 다건 조회하는 경우 조회 가능한 최대 건수를 설정한다.

최대 ROW 수를 초과할 경우 ProObjectRuntimeException의 DOJ-2006으로 예외를 발생시킨다. (기본값: 0)

APPLICATION_DATAOBJECTFACTORY_LIMIT_ROW_SIZE

APPLICATION_DATAOBJECTFACTORY_MAX_ROW_SIZE 옵션의 제한값을 적용할지 여부를 설정한다.

  • true : APPLICATION_DATAOBJECTFACTORY_MAX_ROW_SIZE 옵션의 갯수를 기준으로 조회를 제한한다.

  • false : APPLICATION_DATAOBJECTFACTORY_MAX_ROW_SIZE 옵션의 갯수를 기준으로 조회를 제한하지 않는다. (기본값)

4.2.3. 서비스 그룹 설정

ProObject 엔진의 가장 기본적인 설정은 다음의 파일에 Key-Value 방식을 이용해 설정한다.

${SG_HOME}/config/servicegroup.properties

다음은 servicegroup.properties의 설정항목에 대한 설명이다.

SERVICEGROUP_DATAOBJECTFACTORY_QUERY_LOGGING_LEVEL = [SEVERE|WARNING|INFO|CONFIG|FINE|FINER|FINEST|OFF]
SERVICEGROUP_DATAOBJECTFACTORY_SINGLE_RESULT_CHECK = [true|false]
SERVICEGROUP_DATAOBJECTFACTORY_QUERY_TIMEOUT = query_timeout
SERVICEGROUP_DATAOBJECTFACTORY_EMPTY_RESULT_CHECK = [true|false]
SERVICEGROUP_DATAOBJECTFACTORY_ENCRYPTMODULE_ENABLE = [true|false]
SERVICEGROUP_DATAOBJECTFACTORY_ENCRYPTMODULE_PATH = encryptmodule_class 
항목설명
SERVICEGROUP_DATAOBJECTFACTORY_QUERY_LOGGING_LEVEL

데이터 오브젝트 팩토리가 실행한 쿼리문을 출력하는 로그 레벨을 지정한다.

ProObject에 설정된 엔진 로그 설정과 비교하여 쿼리 로깅 레벨이 엔진 로거의 로그 레벨보다 같거나 더 높은 경우 로깅한다.

다음 중에 설정한다.

  • SEVERE

  • WARNING

  • INFO

  • CONFIG

  • FINE

  • FINER

  • FINEST

  • OFF(기본값)

SERVICEGROUP_DATAOBJECTFACTORY_SINGLE_RESULT_CHECKDB 데이터 오브젝트 팩토리의 get() API를 이용하여 단건 조회를 수행하는 경우 조회 결과가 2건 이상인 경우 com.tmax.proobject.dataobject.exception.MaxRowException을 발생시킨다.
SERVICEGROUP_DATAOBJECTFACTORY_QUERY_TIMEOUT

DB 데이터 오브젝트 팩토리가 쿼리를 수행할 때 타임아웃을 지정한다. 옵션값은 java.sql.PreparedStatement의 setQueryTimeout()을 통해 설정한다.

설정하지 않는 경우 데이터소스를 제공하는 WAS의 쿼리 타임아웃 설정을 따른다. (기본값: 0, 단위: second)

SERVICEGROUP_DATAOBJECTFACTORY_EMPTY_RESULT_CHECKDB 데이터 오브젝트 팩토리의 get, getForwardList, getScrollableList, getDataObjectList를 통해 데이터를 조회하는 조회할 때 결과가 없는 경우 com.tmax.proobject.dataobject.exception.EmptyResultException을 발생시킨다.
SERVICEGROUP_DATAOBJECTFACTORY_ENCRYPTMODULE_ENABLEDB 데이터 오브젝트 팩토리의 데이터 오브젝트 멤버변수 암호화 기능을 ON/OFF하기 위한 설정값이다.
SERVICEGROUP_DATAOBJECTFACTORY_ENCRYPTMODULE_PATH데이터 오브젝트 멤버변수를 암호화하는 경우 사용할 클래스 패스를 지정한다(com.tmax.proobject.dataobject.security.IEncryptUtil을 상속받은 클래스로 지정한다).

4.2.4. 서비스 설정

ProObject 엔진의 서비스 설정은 서비스 그룹과 동일하게 servicegroup.properties에 Key-Value 방식으로 설정한다.

${SG_HOME}/config/servicegroup.properties

다음은 servicegroup.properties의 설정항목에 대한 설명이다.

SERVICE_{0}_DATAOBJECTFACTORY_SINGLE_RESULT_CHECK = [true|false]
SERVICE_{0}_DATAOBJECTFACTORY_QUERY_TIMEOUT = query_timeout
SERVICE_{0}_DATAOBJECTFACTORY_EMPTY_RESULT_CHECK = [true|false]
항목설명
SERVICE_{0}_DATAOBJECTFACTORY_SINGLE_RESULT_CHECKDB 데이터 오브젝트 팩토리의 get() API를 이용하여 단건 조회를 수행하는 경우 조회 결과가 2건 이상인 경우 com.tmax.proobject.dataobject.exception.MaxRowException을 발생시킨다.
SERVICE_{0}_DATAOBJECTFACTORY_QUERY_TIMEOUT

DB 데이터 오브젝트 팩토리가 쿼리를 수행할 때 타임아웃을 지정한다. 옵션값은 java.sql.PreparedStatement의 setQueryTimeout()을 통해 설정한다. 설정하지 않는 경우 데이터소스를 제공하는 WAS의 쿼리 타임아웃 설정을 따른다.

(기본값: 0, 단위: second)

SERVICE_{0}_DATAOBJECTFACTORY_EMPTY_RESULT_CHECKDB 데이터 오브젝트 팩토리의 get, getForwardList, getScrollableList, getDataObjectList를 통해 데이터를 조회하는 조회할 때 결과가 없는 경우 com.tmax.proobject.dataobject.exception.EmptyResultException을 발생시킨다.

4.3. 데이터 오브젝트

데이터 오브젝트(DataObject)는 ProObject 모듈간 데이터 전달의 기본 단위이며, 서비스 오브젝트의 입출력의 정해진 형식이다. 데이터 오브젝트는 ProManager를 통해 등록한 메타 데이터를 기반으로 ProStudio에서 개발할 수 있으며, 자세한 내용은 "ProObject Studio 개발자 안내서"를 참고한다.

4.3.1. 개발 방법

데이터 오브젝트를 개발하기 위해서는 메타 딕셔너리에 개발자가 사용할 메타 데이터가 등록되어 있어야 한다.

ProStudio의 데이터 오브젝트 에디터를 통해 메타 딕셔너리에서 사용할 메타 데이터를 검색하여 데이터 오브젝트의 멤버변수를 구성한다. 데이터 오브젝트의 멤버변수로 선언할 수 있는 메타의 Java Type은 int, long, float, double, short, byte, boolean, char 8가지 primitive type과 각 primitive type에 대한 wrapper class을 포함하는 BigInteger, BigDecimal, Date, Timestamp, Blob, Clob과 같은 java.sql type도 지원한다.

데이터 오브젝트의 개발 과정은 다음과 같다.

  1. ProManager를 통해서 메타 딕셔너리를 구성한다.

  2. ProStudio를 통해서 데이터 오브젝트를 생성한다.

  3. ServiceObject, BizObject 등에 데이터 오브젝트를 사용하여 개발한다.

데이터 오브젝트는 기본적으로 멤버변수와 자신의 변수 타입에 대한 Getter/ Setter를 메소드뿐 아니라 부수적인 기능을 갖고 있다.

다음은 데이터 오브젝트의 개발에 사용되는 변수와 주요 기능에 대한 설명이다.

  • 부모 데이터 오브젝트

    데이터 오브젝트는 다른 데이터 오브젝트를 자신의 부모 클래스로 지정할 수 있다. 개발된 데이터 오브젝트는 개발자가 지정한 부모 클래스를 extends한 형태로 생성된다. 이때 데이터 오브젝트의 부모 클래스로는 데이터 오브젝트 밖에 지정할 수 없으므로 다른 클래스를 부모 클래스로 지정하지 않도록 주의한다.

  • Include 데이터 오브젝트

    데이터 오브젝트는 위에서 설명한 8가지 primitive type과 그에 대한 wrapper type, java.sql type 외에도 다른 데이터 오브젝트를 멤버변수로 갖을 수 있다. 개발 방법은 메타 딕셔너리에 메타 데이터를 검색하는 것과 동일하게, ProStudio에서 데이터 오브젝트를 검색하여 메타처럼 활용하여 개발한다. 이때 Super 데이터 오브젝트와 마찬가지로 데이터 오브젝트외에 다른 클래스를 멤버변수로 선언하지 않도록 주의해야 한다.

  • 마스킹

    String 타입의 멤버변수인 경우 마스크(mask)를 지정할 수 있다. 마스킹은 데이터 오브젝트를 메시지로 변환하거나, toString 등으로 멤버변수의 값을 출력할 경우 변수의 값을 마스크 값으로 치환하는 기능이다. 예를 들어 'name' 변수에 마스킹 옵션이 지정되어 있으면 데이터 오브젝트의 toString API를 호출하는 경우 'name'의 값이 '****'와 같이 표시된다.

    마스크 옵션 지정은 메타를 통해서 지정되며, 마스크 옵션이 설정된 메타를 이용하여 데이터 오브젝트의 멤버변수를 선언한 경우 해당 멤버변수는 마스킹이 지정된다.

  • 메시지

    데이터 오브젝트는 ProStudio를 통해서 JSON, FixedLength, Delimiter, XML 형식의 전문을 지원하는 메시지 클래스를 생성하여 사용할 수 있다.

    구분설명
    JSON메시지 전문 형태를 JSON으로 주고받는다.
    FixedLength메시지 전문 형태를 메타에 지정된 length로 주고받는다. 남는 길이의 경우 임의의 문자로 채워지며 이를 원하지 않을 경우 ProStudio에서 'fill' 문자를 채워넣거나 trim 옵션을 지정할 수 있다. 또한 align, decimal, decimalAlign 옵션을 지정하여 잘리는 방향 및 기준을 설정할 수 있다.
    Delimiter개발자가 지정한 특정 문자로 값들을 구분하여 전문을 주고받는다.
    XMLxml 형태로 전문을 주고받는다.

    데이터 오브젝트는 기본적인 Java의 primitive 타입 뿐만 아니라, Parent Class 지정 및 다른 DO를 멤버변수로 선언하여 사용할 수 있다.

  • 데이터 오브젝트 멤버변수

    데이터 오브젝트의 멤버변수는 ProManager를 통해 등록한 메타 데이터를 기반으로 생성된다. 멤버변수는 private으로 선언되며, 멤버변수에 해당하는 Getter와 Setter가 같이 생성된다. 추가적으로 멤버변수마다 '~_nullable', '~_modified' 변수와 해당 필드의 Getter가 생성된다.

  • DTO_LOGICAL_NAME

    데이터 오브젝트를 생성하는 경우 개발자가 지정한 논리명을 갖고 있는 String 변수이다.

  • ~_nullable 변수

    필드의 null 값 허용 여부를 나타내는 변수이다. 이 필드는 private으로 선언되며, Getter만 제공된다.

    예를 들어 'number'라는 변수의 Java 타입이 Integer이고 null 값을 허용한다면, number_nullable 변수의 값은 true가 되며 개발자가 isNullableNumber() api를 호출하면 리턴 값은 true가 된다. 데이터 오브젝트를 사용하는 경우 사용할 멤버변수가 null 값을 허용하는지 여부를 확인할 때 사용한다.

  • ~_isModified 변수

    멤버변수의 Setter 호출 여부를 나타내는 변수이다. 개발자는 isModified~() API를 통해 데이터 오브젝트의 멤버변수의 값이 변경되었는지 확인할 수 있다.

    데이터 오브젝트 개발 중 멤버변수의 Setter를 호출하여 변수 값을 변경할 경우 ~_isModified 변수의 값이 true로 변경된다. ~_isModified 변수에 대한 기능은 각 멤버변수의 isModified~() 외에도 isModified()와 getIsModifiedField()를 통해 확인할 수 있다.

  • fieldPropertyMap

    데이터 오브젝트의 멤버변수에 해당하는 메타 정보를 저장하고 있는 맵(Map)이다. 맵은 멤버변수명을 key로 하고, 메타 정보를 담고 있는 FieldProperty를 value로 구성된다.

    FieldProperty에는 다음과 같은 정보가 담겨 있다.

    구분설명
    logicalName변수의 논리명이다.
    Java Type변수의 java 타입이다.
    length변수의 길이이다.
    decimalfloat, double, BigDecimal type인 경우 decimal 값이다.
    array변수가 array 타입인 경우 배열의 크기이다.
    reference변수가 include 타입인 경우 멤버변수로 선언된 데이터 오브젝트의 풀 패키지명이다.
    columnNamepersistence 메타를 사용한 경우 메타에 해당하는 DB 컬럼 이름이다.
    tableNamepersistence 메타를 사용한 경우 메타에 해당하는 DB 테이블 이름이다.
    schemapersistence 메타를 사용한 경우 메타에 해당하는 DB 스키마 이름이다.
    keypersistence 메타를 사용한 경우 DB의 key에 해당하는 필드인지 여부이다.
    transient데이터 오브젝트 팩토리를 통해 DB 결과를 DO로 리턴받을 때 DB 작업에서 제외할 필드 여부를 지정한다.
    encrypt데이터 오브젝트 팩토리를 통해 DB 작업을 수행할 경우 해당 멤버변수값을 암호화할지 여부를 지정한다.

4.3.2. 데이터 오브젝트 API

데이터 오브젝트는 자신의 멤버변수에 해당하는 Getter/Setter 이외에도, 개발의 편의성을 위해 clone, equals, hashCode toString를 제공한다.

API설명
멤버변수 Getter

데이터 오브젝트를 생성하는 경우 개발자가 지정했던 멤버변수의 값을 반환한다. 멤버변수의 nullable 속성이 false인 경우 null 대신 각 JAVA 타입에 해당하는 기본값이 반환된다.

  • BLOB 타입 멤버변수 : byte[]와 InputStream 타입으로 값을 반환하는 Getter를 지원한다.

  • CLOB 타입 멤버변수 : String과 Reader 타입으로 값을 반환하는 Getter를 추가적으로 지원한다.

단, Java 메모리 문제상 String과 byte[]의 경우 최대 64kb 사이즈까지의 값만 지원하고, 반환할 값이 64kb를 넘는 경우 BufferOverflowException을 발생시킨다.

멤버변수 Setter

데이터 오브젝트의 멤버변수에 값을 지정할 때 사용되는 메소드이다.

기본적으로 자기 자신의 타입을 입력받는 Setter외에도, String으로 입력받는 Setter가 추가 지원된다. 그 외에도 BigDecimal이나 BigInteger와 같이 특수한 타입에 대해서는 int, float, double, long, String 등 자기 자신이 아닌 타입의 값을 입력받아서 데이터 오브젝트 내부에서 적절한 값으로 변경하여 저장하는 Setter를 지원한다.

clone

현재 데이터 오브젝트가 갖고 있는 멤버변수의 값을 그대로 복사한 데이터 오브젝트를 반환하는 메소드이다.

데이터 오브젝트의 레퍼런스 복사가 아니라 추가적인 객체를 생성하여 값을 복사한 다음 반환한다.

equals

데이터 오브젝트의 객체 비교를 위해 제공되는 메소드이다.

데이터 오브젝트의 멤버변수값을 비교하여 동일성을 비교하여 동일 여부를 boolean 값으로 반환한다.

hashCode

데이터 오브젝트의 객체 비교를 위해 제공되는 메소드이다.

데이터 오브젝트의 멤버변수값들을 이용하여 hashCode를 생성한 후 반환한다.

get데이터 오브젝트의 멤버변수명을 입력받아 해당하는 멤버변수의 값을 반환하는 메소드이다.
set데이터 오브젝트의 멤버변수명과 값을 입력받아 해당하는 멤버변수에 입력받은 값을 설정하는 메소드이다.
clear데이터 오브젝트의 멤버변수 값들을 초기값으로 설정한다.
toString

현재 멤버변수들의 값을 String 값으로 출력한다.

다음의 형태로 출력된다. 출력할 멤버변수에 마스킹 속성이 설정된 경우 마스킹된 값으로 출력된다.

'변수명 : + 값' 

4.3.3. 맵 데이터 오브젝트

맵 데이터 오브젝트(MapDataObject)는 메타를 통한 멤버변수 선언 없이 사용하기 위한 데이터 오브젝트이다. 해당 클래스는 com.tmax.proobject.dataobject에 존재하며, com.tmax.proobject.dataobject.context.ContextDataObject를 상속받아 구현된다.

다음은 오브젝트 사용에 주의할 사항이다.

  1. 필드명은 NULL이 될수 없다.

  2. 맵 데이터 오브젝트의 변수에는 기본적인 Java 타입 값과 데이터 오브젝트만 설정할 수 있다.

  3. 메시지 타입은 com.tmax.proobject.dataobject.MapDataObjectMsgJson 클래스를 통한 JSON 형태만 지원 가능하다.

  4. JSON 형태의 메시지를 맵 데이터 오브젝트로 Unmarshal하는 경우 Object type은 맵 데이터 오브젝트, 숫자는 Number, 문자열은 String, true, false는 Boolean, NULL로 변환하여 제공된다.

맵 데이터 오브젝트는 ContextDataObject를 상속받고 있으므로 본 절에서는 맵 데이터 오브젝트에서만 제공하는 API와 오버라이드한 API에 대해서 설명한다.

사용방법은 ContextDataObject와 동일하며 사용 가능한 API는 다음과 같다.

API설명
clone

현재 데이터 오브젝트가 갖고 있는 멤버변수의 값을 그대로 복사한 데이터 오브젝트를 반환하는 메소드이다.

데이터 오브젝트의 레퍼런스 복사가 아니라 추가적인 객체를 생성하여 값을 복사한 다음 반환한다.

get데이터 오브젝트의 멤버변수명을 입력받아 해당하는 멤버변수의 값을 반환하는 메소드이다.
set데이터 오브젝트의 멤버변수명과 값을 입력받아 멤버변수에 값을 설정하는 메소드이다. 필드명은 NULL이 될 수 없다.
setIfAbsent

데이터 오브젝트에 입력받은 필드명에 해당하는 값이 없는 경우에만 데이터를 추가한다. 반환값은 입력받은 필드명에 해당하는 값이 있는 경우 해당 값을 반환하며, 없는 경우 NULL을 반환한다.

ConcurrentHashMap의 putIfAbsent와는 다르게 동시성을 보장하지 않는다.

getFieldPropertyMap맵 데이터 오브젝트에 설정된 필드와 정보를 FieldProperty 객체로 변환하여 반환한다. FieldProperty에는 logicalName과 physicalName, Class 정보가 담겨 있으며, logicalName과 physicalName은 맵 데이터 오브젝트의 get API를 통해 입력한 fieldName이다.

4.4. DB 데이터 오브젝트 팩토리

본 절에서는 DBIO 작업을 위한 DB 데이터 오브젝트 팩토리에 대한 개념과 개발 방법에 대해 설명한다.

4.4.1. 구조

DB 데이터 오브젝트 팩토리(DBDataObjectFactory)는 DBIO 작업을 수행하기 위한 모듈이다. 하나의 DB 데이터 오브젝트 팩토리에는 개발자가 지정한 C/R/U/D 쿼리가 매핑되며, 개발자는 DB 데이터 오브젝트 팩토리를 사용하는 경우 수행하려는 쿼리를 선택해야 한다. 그외에도 DB 데이터 오브젝트 팩토리는 원할한 DBIO 작업을 위해 페이징 기능과 beforeImageLog 기능을 제공한다.

DB 데이터 오브젝트 팩토리는 4가지 종류의 쿼리와 쿼리 조건 파라미터를 멤버변수로 구성한다.

[그림 4.1] DB 데이터 오브젝트 팩토리 구조

DB 데이터 오브젝트 팩토리 구조


DB 데이터 오브젝트 팩토리 개발 방법은 쿼리를 선택한 후 파라미터 입력하는 과정으로 진행된다.

[그림 4.2] DB 데이터 오브젝트 팩토리 개발 유형

DB 데이터 오브젝트 팩토리 개발 유형

  • CASE1

    팩토리에 자신이 사용할 쿼리를 지정하고 쿼리 조건을 바꿔가면서 사용하는 유형이다 .

  • CASE2

    조건이 일정한 쿼리를 지정하여 사용하는 유형이다.

  • CASE3

    로직에서 사용자로부터 직접 쿼리를 입력받아 수행하는 유형이다.

4.4.1.1. Condition

Condition은 개발자가 사용할 Separated Query의 조건문이다.

DB 데이터 오브젝트 팩토리에 CONDITION enum으로 구성되며, DB 데이터 오브젝트 팩토리 개발 당시 개발자가 지정한 SQL ALIAS로 구분된다. 조건 선택은 DB 데이터 오브젝트 팩토리의 setCondition API을 이용하며, 반드시 Separated Query가 선택되어 있어야 한다. Separated Query와 Condition은 서로 종속관계는 아니며, 한 쿼리에 대해서 다양한 조건을 연결하기 위해 사용된다.

  • 예제 1)

    SeparatedQueryFactory factory = new SeparatedQueryFactory("tibero");
    
    //사용할 쿼리 선택
    factory.setSeparatedQuery(SEPARATEDQUERY.SELECT1);
    
    //쿼리에 사용할 조건절 선택
    factory.setCondition(CONDITION.C1); //CONDITION.C1 : ENAME = :ename AND SAL > :sal
    factory.setEname("John");
    factory.setSal(100);
    
    //실제 수행되는 쿼리
    SELECT ~ ...
    WHERE
    ENAME = 'John' AND SAL > 100 

4.4.1.2. Separated Query

Separated Query는 개발자가 사용할 SQL 중 쿼리 조건문을 제외한 나머지 부분이다.

예를 들어 "SELECT * FROM EMP WHERE EMPNO = ?"라는 SQL을 사용하려면, Separated Query는 "SELECT * FROM EMP"까지만 구성한다.

Separated Query는 단독으로 사용될 수 없으며 반드시 Condtion과 조합하여 사용해야 한다. SeparatedQuery는 DB 데이터 오브젝트 팩토리에 'SEPARATEDQUERY'라는 enum으로 구성되어 있으며, DB 데이터 오브젝트 팩토리 개발 당시 개발자가 지정한 SQL ALIAS로 구분된다. 쿼리 선택은 DB 데이터 오브젝트 팩토리의 setSeparatedQuery API를 이용하며, 반드시 setCondition API를 통해 쿼리 조건문을 선택해줘야 정상적인 동작을 한다.

다음은 Separated Query와 Condition 조합을 이용하는 예제이다. 기본적인 사용법은 Full Query와 같다.

SeparatedQueryFactory factory = new SeparatedQueryFactory("tibero");

//사용할 쿼리 선택
factory.setSeparatedQuery(SEPARATEDQUERY.SELECT1);

//쿼리에 사용할 조건절 선택
factory.setCondition(CONDITION.EMPNO);
factory.setEmpno(1);
SampleDataObject output = factory.get();

System.out.println(output.toString());

4.4.1.3. Full Query

Full Query는 개발자가 사용할 SQL 문을 뜻한다. DBIO를 수행하기 위한 쿼리 문자열이다.

'FULLQUERY'라는 enum으로 구성되어 있으며, DB 데이터 오브젝트 팩토리를 개발할 때 개발자가 지정한 SQL ALIAS로 구분된다. 쿼리 선택은 DB 데이터 오브젝트 팩토리의 setFullQuery API를 이용하며, 쿼리 선택 방식은 FULLQUERY.ALIAS로 선택할 수 있다.

다음은 Full Query의 사용법에 대한 설명이다.

  • 단건조회 예제

    //* 사용한 쿼리 : SELECT EMPNA.EMPNO, ENAME, JOB, SAL FROM EMP WHERE EMPNO = :empno
    //사용하려는 데이터소스명을 입력하여 DBDOF 객체 생성
    FullQueryFactory factory = new FullQueryFactory("tibero");
    
    //수행하려는 쿼리 선택
    factory.setFullQuery(FULLQUERY.SELECT1);
    
    //파라미터 값 입력. 본로직에서는 쿼리의 조건을 입력하므로, 
    //DBDOF의 파라미터에 해당하는 setter를 호출했다.
    factory.setEmpno(1);
    
    //단건조회용 API인 get 메소드를 호출하여 조회 결과를 Target DO로 반환받는다.
    SampleDataObject output = factory.get();
    System.out.println(output.toString());
  • 다건조회 예제

    //* 사용한 쿼리 : 
    SELECT EMPNA.EMPNO, ENAME, JOB, SAL FROM EMP WHERE ENAME = :ename
    • getForwardList

      FullQueryFactory factory = new FullQueryFactory("tibero");
      factory.setFullQuery(FULLQUERY.SELECT1);
      factory.setEname("Michael");
      
      //getForwardList API를 이용하여 다건조회 수행.
      //ForwardList는 iterator 조회와 get API만을 지원한다.
      //index 조정을 불가하며, iterator나 get을 통해 지나간 커서 위치는 재조회할 수 없다.
      for(SampleDataObject dataobject : factory.getForwardList()) {
          System.out.println(dataobject.toString());
      }
    • getScrollableList

      FullQueryFactory factory = new FullQueryFactory("tibero");
      factory.setFullQuery(FULLQUERY.SELECT1);
      factory.setEname("Michael");
      
      //getScrollableList 메소드를 호출하여, 다건 조회 결과를 list로 반환받는다.
      ScrollableList<SampleDataObject> list = factory.getScrollableList();
      
      //반환받은 list에 index 값을 입력하여 해당 커서 위치에 존재하는 조회 결과를 반환받을 수 있다.
      System.out.println(list.get(1));
      System.out.println(list.get(2));
      System.out.println(list.get(3));
    • getDataObjectList

      FullQueryFactory factory = new FullQueryFactory("tibero");
      factory.setFullQuery(FULLQUERY.SELECT1);
      factory.setEname("Michael");
      
      //java.util.ArrayList에 조회결과를 담아서 반환한다.
      ArrayList<SampleDataObject> list = factory.getDataObjectList();
      System.out.println(list.get(0));
      System.out.println(list.get(1));
      System.out.println(list.get(2));
  • 데이터 삽입 예제

    //* 사용한 쿼리 : INSERT INTO EMPNA(EMPNO, ENAME, JOB, SAL) VALUES(:empno,:ename,:job,:sal)
    FullQueryFactory factory = new FullQueryFactory("tibero");
    factory.setFullQuery(FULLQUERY.INSERT1);
    SampleDataObject input = new SampleDataObject();
    
    //Target DO를 생성하고 삽입할 데이터들을 입력한다.
    input.setEmpno(5);
    input.setEname("jung");
    input.setJob("was3");
    input.setSal((long)100);
    
    //add API를 호출하여 insert 쿼리를 수행한다.
    //add 메소드의 파라미터는 Target DO이며 boolean 값을 통해 addBatch 수행 여부를 지정한다. 
    //true일 경우 addBatch를 수행하지 않는다.
    //add API의 리턴값은 변경된 데이터 수이다.
    System.out.println(factory.add(input, true));
  • 데이터 수정 예제

    //* 사용한 쿼리 : UPDATE EMPNA SET ENAME = :ename, JOB = :job, SAL = :sal WHERE EMPNO = :empno
    FullQueryFactory factory = new FullQueryFactory("tibero");
    factory.setFullQuery(FULLQUERY.UPDATE1);
    
    //UPDATE 쿼리의 조건 값을 입력한다.
    //모든 쿼리의 조건 값은 DBDOF에 설정한다.
    factory.setEmpno(1);
    
    //수정할 데이터값을 입력한다.
    //수정할 데이터값은 Target DO로 입력한다.
    SampleDataObject input = new SampleDataObject();
    input.setEname("Michael");
    input.setJob("was3");
    input.setSal((long)100);
    
    System.out.println(factory.update(input, true));
  • 데이터 삭제 예제

    데이터 삭제의 경우 DELETE 쿼리에 테이블명과 조건절만 존재한다. 원칙상 쿼리의 조건 값은 DB 데이터 오브젝트 팩토리를 통해 입력받도록 되어 있지만, DELETE 쿼리에 한하여 Target DO에 조건 값을 입력할 수 있는 경우 Target DO로도 DELETE 쿼리 조건을 입력한다.

    • 데이터 오브젝트를 통한 쿼리 조건 입력 예제

      //* 사용한 쿼리 : DELETE FROM EMPNA WHERE EMPNO = :empno
      FullQueryFactory factory = new FullQueryFactory("tibero");
      factory.setFullQuery(FULLQUERY.DELETE1);
      
      //Target DO를 생성하고 삭제할 조건값을 입력한다.
      //이는 DBDOF를 통해 SELECT하거나 INPUT으로 들어온 
      //DO에서 추가적으로 값을 추출하는 과정을 거치지 않게 하기위한 방법이다.
      SampleDataObject input = new SampleDataObject();
      input.setEmpno(1);
      
      System.out.println(factory.remove(input, true));
    • DBDOF에 직접 쿼리 조건 입력 예제

      FullQueryFactory factory = new FullQueryFactory("tibero");
      factory.setFullQuery(FULLQUERY.DELETE1);
      //쿼리 조건에 해당하는 파라미터 값 입력
      factory.setEmpno(2);
              
      System.out.println(factory.remove(true)); 

4.4.1.4. User Query

User Query는 DB 데이터 오브젝트 팩토리 개발 당시 Full Query나 Separated Query로 지정하지 않은 쿼리를 사용하기 위한 방법이다.

SO나 BO의 로직에서 개발자로부터 직접 쿼리와 쿼리 파라미터를 입력받는다. setUserQuery API를 이용해서 로직에서 사용할 쿼리 문자열을 입력하고, setBindingParameter API를 이용해서 쿼리의 파라미터값을 입력한다.

User Query는 DB 데이터 오브젝트 팩토리를 통해 반복적으로 사용할 쿼리가 아닌 특정 로직에서만 일회성으로 사용되는 쿼리를 입력받기 위한 방법이므로 범용적으로 사용되는 쿼리의 경우 Full Query 또는 Separated Query를 이용해야 한다.

다음은 User Query를 사용하는 예제이다.

FullQueryFactory factory = new FullQueryFactory("tibero");
StringBuilder query = new StringBuilder();
query.append("SELECT *   \n");
query.append("FROM EMPNA   \n");
query.append("WHERE    \n");
query.append("EMPNO = :empno");
factory.setUserQuery(query.toString());
factory.setBindParameter("empno", new Integer(1)); 

SampleDataObject output = factory.get();
System.out.println(output.toString());

4.4.1.5. Dynamic Query

Dynamic Query는 런타임에 동적으로 SQL 문을 조립하는 것이 가능하다. 컴파일 단계에서 SQL 문의 전체 텍스트를 알 수 없을 때 Dynamic Query를 활용하여 범용적이고 유연한 응용 프로그램을 작성할 수 있다.

DB 데이터 오브젝트 팩토리는 Mybatis의 Dynamic SQL 문법의 if, choose, when, otherwise, trim, where, set, foreach 태그를 지원한다.

  • if

    if 태그는 테스트 결과에 따라 선택적으로 구문을 쿼리에 추가하며, where절의 일부로 포함될 수 있다.

    SELECT * FROM EMP
      WHERE DEPTNO = 10
      <if test="ename != null">
        AND ENAME like #{ename}
      </if>

    ename의 값이 없다면 DEPTNO = 10인 모든 employee가 리턴된다. 하지만 ename 값이 있다면 그 값과 비슷한 데이터를 찾는다.

  • choose, when, otherwise

    choose, when, otherwise 태그는 Java의 switch 구문과 유사하다.

    SELECT * FROM EMP WHERE DEPTNO = 10
      <choose>
        <when test="ename != null">
          AND ENAME like #{ename}
        </when>
        <when test="job != null">
          AND JOB like #{job}
        </when>
        <otherwise>
          AND sal = 100
        </otherwise>
      </choose>

    ename에 값이 있다면 ename 구문이 선택되고, job에 값이 있다면 job 구문이 선택된다. 두 값 모두 없다면 sal 구문이 선택된다.

  • where, trim, set

    다음과 같이 if 태그를 사용하면 문제가 발생한다.

    SELECT * FROM EMP
      WHERE 
      <if test="deptno != null">
        DEPTNO = #{deptno}
      </if>
      <if test="ename != null">
        AND ENAME like #{ename}
      </if>

    deptno에 값이 없고 ename에도 값이 없다면 다음과 같이 SQL 문법에 맞지않는 쿼리가 만들어진다.

    SELECT * FROM EMP WHERE

    ename에 값만 있다면 다음과 같은 쿼리가 만들어지고 역시 문법에 맞지 않는다.

    SELECT * FROM EMP WHERE AND ENAME like 'kim'

    where 태그는 이런 문제를 해결한다.

    SELECT * FROM EMP
      <where>
        <if test="deptno != null">
             DEPTNO = #{deptno}
        </if>
        <if test="ename != null">
            AND ENAME like #{ename}
        </if>
      </where>

    where 태그는 쿼리에 동적으로 "WHERE" 키워드를 추가하며, 태그가 반환하는 구문이 "AND"나 "OR" 키워드로 시작하면 그 키워드를 지운다.

    where 태그는 trim 태그를 사용하여 다음과 같이 대체할 수 있다.

    <trim prefix="WHERE" prefixOverrides="AND |OR ">
      ...
    </trim>

    set 태그는 다음과 같이 사용하며, 동적으로 "SET" 키워드를 추가하고, 필요없는 콤마를 제거한다.

    UPDATE EMP
        <set>
          <if test="ename != null">ENAME=#{ename},</if>
          <if test="deptno != null">EMPNO=#{deptno},</if>
          <if test="job != null">JOB=#{job}</if>
        </set>
      where EMPNO=1234

    set 태그 역시 trim 태그를 사용하여 대체할 수 있다.

    <trim prefix="SET" suffixOverrides=",">
      ...
    </trim>

    이와 같은 방식으로 trim 태그는 사용자 정의에 따라 유연하게 활용할 수 있다.

  • foreach

    foreach 태그는 collection에 대해 반복 처리를 해서 쿼리를 작성할 수 있게 한다.

    SELECT * FROM EMP
      WHERE ID in
      <foreach item="item" index="index" collection="list"
          open="(" separator="," close=")">
            #{item}
      </foreach>

    foreach 태그는 태그 내부에서 사용할 수 있는 item, index 변수를 선언한다. 또한 열고 닫는 문자열을 명시할 수 있고, 반복간에 둘 수 있는 구분자를 추가할 수 있다.

DB 데이터 오브젝트 팩토리에서 Dynamic Query는 'DynamicQueryImple' enum으로 구성되어 있으며, DB 데이터 오브젝트 팩토리를 개발할 때 개발자가 지정한 SQL ALIAS로 구분된다. 쿼리 선택은 DB 데이터 오브젝트 팩토리의 setDynamicQuery API를 이용하며, 쿼리 선택 방식은 DynamicQueryImple.ALIAS로 선택할 수 있다.

다음은 Dynamic Query를 사용하는 예시이다.

  • 다건 조회 예제

    //* 사용한 쿼리 : SELECT * FROM EMP
      <where>
        <if test="deptno != null">
             DEPTNO = #{deptno}
        </if>
        <if test="ename != null">
            AND ENAME LIKE #{ename}
        </if>
      </where>
    
    //사용하려는 데이터소스명을 입력하여 DBDOF 객체 생성
    DynamicQueryFactory factory = new DynamicQueryFactory("tibero");
    
    //수행하려는 쿼리 선택
    factory.setDynamicQuery(DynamicQueryFactory.DynamicQueryImple.SELECT1);
    
    //파라미터 값 입력
    factory.setEname("kim");
    
    //단건조회용 API인 get 메소드를 호출하여 조회 결과를 Target DO로 반환받는다.
    EmpDO output = factory.get();

4.4.1.6. Memeber Variables

DB 데이터 오브젝트 팩토리를 구성하는 멤버변수는 쿼리에서 사용되는 쿼리 조건에 해당하는 파라미터들로 구성된다. SQL의 where에 해당하는 파라미터들만 DB 데이터 오브젝트 팩토리의 멤버변수가 되며, INSERT 문의 VALUES나 UPDATE 문의 SET에 해당하는 파라미터들은 멤버변수가 될 수 없다.

4.4.2. 개발 방법

본 절에서는 DB 데이터 오브젝트 팩토리를 개발하는 과정과 개발된 DB 데이터 오브젝트 팩토리를 이용하여 DBIO를 수행로직을 개발하는 방법에 대해서 설명한다.

4.4.2.1. 선행 개발 과정

DB 데이터 오브젝트 팩토리를 개발하기 위해서는 반드시 다음의 과정이 선행되어야 한다.

  1. 데이터소스 목록 정의

    dbio_config.xml에 DB 데이터 오브젝트 팩토리에서 사용할 데이터소스 목록을 정의해야 한다.

  2. 데이터 오브젝트 생성

    데이터 오브젝트 팩토리를 개발하기 위해서는 반드시 기반이 될 데이터 오브젝트가 존재해야 한다.

    데이터 오브젝트 팩토리를 개발하기 위해서는 데이터 오브젝트가 선행 개발되어야 한다. 데이터 오브젝트는 흔히 쓰이는 개념인 DataTransferObject의 개념에서 확장되어 ORM의 Entity 개념을 포함하고 있다. ProObject에서는 데이터 오브젝트가 서비스 간의 input, output을 담당하며 DBIO와 FILE IO의 단위이다. DBIO로 사용하려는 데이터 오브젝트를 개발할 때는 SQL을 수행하려는 대상 테이블들의 view로 생각한다. 기본적인 데이터 오브젝트에 대한 개발 방법은 “4.3. 데이터 오브젝트”를 참고한다.

    다음은 DB 데이터 오브젝트 팩토리를 이용해서 개발하는 경우 유의해야 할 사항에 대한 설명이다.

    • Non Persistence DataObject

      데이터 오브젝트와 DB 스키마의 연관성이 없는 형태를 뜻한다. DB 스키마 정보가 포함되지 않은 메타를 사용하여 개발된 데이터 오브젝트이다. 이런 형태의 데이터 오브젝트를 DB 데이터 오브젝트 팩토리에서 사용하기 위해서는 데이터 오브젝트의 멤버변수명(META의 Physical Name)과 쿼리에서 사용된 column 이름 또는 alias 이름이 일치해야 한다.

    • Persistence DataObject

      데이터 오브젝트와 DB 스키마가 coupling된 형태를 뜻한다. DB 스키마 정보가 포함된 메타를 사용하여 개발된 데이터 오브젝트이다. 메타의 physical name과 관계없이, 메타에 지정된 schema, table, column이 일치하는 값만 가져온다.

      Column과 데이터 오브젝트의 매핑 우선순위는 다음과 같다.

      1. 데이터 오브젝트의 멤버변수명(메타의 Physical Name)과 alias 이름이 일치하는 경우

      2. 데이터 오브젝트의 멤버변수에 해당하는 컬럼명(메타의 Column Name)과 DB 컬럼 이름이 일치하는 경우

      3. 데이터 오브젝트의 멤버변수명(메타의 Physical Name)과 쿼리에서 사용된 컬럼 이름이 일치하는 경우

    Target DO는 DB 데이터 오브젝트 팩토리의 select 결과물을 받아오기 위한 혹은 insert, update할 값을 입력하기 위한 단위이다. ProObject에서는 Target DO를 DBIO를 수행할 TABLE VIEW로 간주하며, Target DO를 생성할 때에도 Table 단위 혹은 View 단위로 구성하여 사용하길 권장한다.

    Target DO는 DB 데이터 오브젝트 팩토리의 단위가 되기도 하지만 데이터 오브젝트의 특성을 그대로 갖고 있으므로, 서비스의 INPUT, OUTPUT으로 사용 가능하며 메시지 클래스를 생성할 수도 있다. Target DO를 선택할 때에 데이터 오브젝트의 멤버변수로 다른 데이터 오브젝트를 포함하는 경우는 허용하지 않으므로 주의한다.

  3. 데이터 오브젝트 선택

    데이터 오브젝트 팩토리의 기반이 될 데이터 오브젝트를 선택한다. DB 데이터 오브젝트 팩토리는 선택된 데이터 오브젝트 형태로 쿼리 조회결과를 리턴하고, insert/update/delete 값을 입력받는다.

  4. SQL 입력

    DB 데이터 오브젝트 팩토리에서 사용할 쿼리를 입력한다. 쿼리는 여러 개를 입력가능하며, SO/BO에서 데이터 오브젝트 팩토리를 사용하는 경우 동작할 쿼리를 선택할 수 있다.

    DB 데이터 오브젝트 팩토리는 사용할 SQL을 미리 입력하고, SO나 BO에서 해당 SQL을 호출한다. DB 데이터 오브젝트 팩토리에 사용할 SQL의 개수는 제한이 없으나, 동일한 내용의 SQL을 중복해서 입력할 수 없다. 개발자는 C/R/U/D 타입별로 자신이 사용할 SQL을 DB 데이터 오브젝트 팩토리에 입력할 수 있으며, 반드시 SQL에 해당하는 SQL ALIAS를 입력해야 한다. SQL ALIAS는 DB 데이터 오브젝트 팩토리를 통해 개발자가 사용할 SQL을 선택할 때 SQL을 구분하는 단위가 되므로 반드시 사용자가 이해할 수 있는 의미를 담아 지정하도록 한다.

DB 데이터 오브젝트 팩토리를 개발하기 위해서는 데이터 오브젝트 팩토리의 기반이 되는 데이터 오브젝트를 선행 개발해야 한다. DB 데이터 오브젝트 팩토리는 SELECT SQL의 결과를 데이터 오브젝트로 제공하며, insert/update SQL의 VALUE를 데이터 오브젝트로 입력 받는다.

4.4.2.2. 수행로직 개발

DB 데이터 오브젝트 팩토리를 이용한 개발은 다음의 과정으로 진행된다.

  1. 데이터소스 설정 및 객체 생성

    DB 데이터 오브젝트 팩토리는 ProObject에서 제공하는 기본 DI(Dependency Injection)의 대상이 아니다. 그러므로 개발자가 직접 DI 기능을 추가해주지 않는한, DB 데이터 오브젝트 팩토리의 객체를 생성해야 한다. DB 데이터 오브젝트 팩토리는 Constructor에 데이터소스명을 직접 입력받도록 되어 있으며, 데이터소스명이 입력되지 않는 경우 ProObject.xml에 설정된 기본 데이터소스를 사용하게 된다. 사용하려는 데이터소스는 반드시 dbio_config.xml에 설정된 PairDataSource의 alias명과 일치해야 한다.

  2. 쿼리 선택

    사용할 데이터소스명을 지정하여 DB 데이터 오브젝트 팩토리의 객체를 생성한 후 개발에 필요한 쿼리를 선택해야 한다.

    사용할 쿼리 종류에 따라서 Full Query의 경우 setFullQuery API를 호출하고 Separated Query의 경우 setSeparatedQuery와 setCondition을 API를 호출하여 사용할 쿼리를 선택한다. 반복해서 사용할 쿼리가 아닌 일회성 쿼리의 경우 setUserQuery API를 통해 쿼리를 입력할 수 있으며, 파라미터는 setBindingParameter API를 호출해서 설정할 수 있다.

    선택한 쿼리는 쿼리 수행 API들(get, getForwardList, getScrollableList, getDataObjectList, add, update, delete)이 호출된 후에는 초기화되므로 쿼리를 반복 수행할 때는 반드시 쿼리 수행 API 호출 전에 쿼리 선택 API를 호출한다.

  3. 파라미터 값 입력

    기존 Java JDBC에서는 쿼리 안에서 파라미터 값들을 '?'로 표시했으며, 파라미터 값은 setObject(int index)와 같이 개발자가 파라미터 위치를 지정해줘야 한다.

    이러한 불편함을 해소하기 위해 DB 데이터 오브젝트 팩토리에 쿼리를 작성할 때 파라미터 부분을 ':파라미터명'으로 지정한다. ':파라미터명'으로 설정된 쿼리의 경우 DB 데이터 오브젝트 팩토리에서 실제 쿼리를 수행할 때 '?'로 치환하고 인덱스 번호를 맞춰서 JDBC Operation을 수행한다.

    쿼리에 설정된 파라미터의 위치에 따라 DB 데이터 오브젝트 팩토리에 파라미터 값을 입력하는 방식은 두 가지로 나뉜다.

    • 쿼리 조건(쿼리 내 WHERE 절에 해당하는 파라미터)에 위치한 파라미터 값을 입력하는 방법

      쿼리 조건에 해당하는 파라미터들은 DB 데이터 오브젝트 팩토리에 멤버변수로 선언되며, 파라미터 값도 DB 데이터 오브젝트 팩토리의 파라미터에 해당하는 Setter 메소드를 호출하여 입력한다.

    • 데이터 삽입/수정에 대한 파라미터 값을 입력하는 방법

      데이터 삽입/수정에 대한 파라미터란 INSERT 쿼리의 VALUES() 절에 포함되는 파라미터들이나 UPDATE 쿼리의 SET() 절에 포함되는 파라미터들을 뜻한다. 이러한 파라미터들은 Target DO에 값을 입력해야 한다. 즉, 파라미터 명과 Target DO의 멤버변수명이 일치해야 한다.

    쿼리 조건에 해당하는 파라미터와 데이터 삽입/수정에 해당하는 파라미터 값 입력방식이 다른 이유에 대해서 설명한다. 우선 쿼리 조건에 해당하는 파라미터의 경우 조건에 따라 다양한 이름으로 설정할 수 있다. 예를 들어 나이의 범위를 지정하는 조건의 경우 AGE라는 컬럼에 대해서 다음과 같이 쿼리를 구성할 수 있다.

    AGE > :mimAge AND AGE < :maxAge

    maxAge나 minAge와 같은 변수까지 모두 데이터 오브젝트에 선언하는 것은 불필요한 멤버변수를 늘리는 행위이므로 데이터 오브젝트가 아닌 DB 데이터 오브젝트 팩토리를 통해 파라미터 값을 입력받을 수 있다. 반면 데이터 삽입/수정 쿼리의 경우 입력받는 데이터 파라미터가 테이블의 컬럼과 정확하게 1:1로 일치한다. 예를 들어 "INSERT INTO EMP(EMPNO, ENAME, AGE) VALUES(~) "와 같은 쿼리에서 VALUES에 해당하는 파라미터들은 삽입하려는 테이블 컬럼에 1:1로 매핑하도록 제한된다.

  4. 쿼리 수행 API 호출

    실행할 쿼리를 선택하고 파라미터까지 입력한 다음에는 DBIO 작업을 수행할 API를 호출한다. DBIO용 API는 C/R/U/D별로 나누어져 있으며, SELECT는 다시 단건 조회용 다건 조회용으로 나뉜다. API에 대한 설명은 “4.4.3. DB 데이터 오브젝트 팩토리 API”를 참고한다.

    참고

    DBIO API를 호출하면 DB 데이터 오브젝트 팩토리에 설정했던 쿼리와 파라미터 정보가 초기화되므로 똑같은 쿼리를 반복 수행한다고 해도 위에서 설명한 '2.쿼리 선택'과 '3.파라미터 입력'까지의 과정을 반복해야 한다.

  5. DB 데이터 오브젝트 팩토리 사용종료 명령

    ProObject에서 DB 데이터 오브젝트 팩토리를 사용하는 경우 DB 리소스에 대한 관리를 자동으로 처리한다. 그러나 반복문을 통한 DB 데이터 오브젝트 팩토리 생성 등 단시간에 DB 리소스를 지나치게 사용하는 경우 또는 사용자가 임의로 리소스를 해제하는 경우를 위해 데이터 오브젝트 팩토리는 cleanUp() API를 제공하고 있다. 데이터 오브젝트 팩토리의 cleanUp() API가 호출되면, 데이터 오브젝트 팩토리는 모든 행위를 중단하고 자신이 할당받은 리소스들을 해제한다.

4.4.3. DB 데이터 오브젝트 팩토리 API

다음은 DB 데이터 오브젝트 팩토리가 제공하는 API 목록이다.

다음은 API에서 사용하는 타입에 대한 설명이다.

구분설명
ForwardList

ForwardList는 iterator와 get 메소드만 사용가능하며, 한 번 조회한 값은 재조회가 불가능하다. ForwardList는 DB 데이터 오브젝트 팩토리의 다건조회 결과물로써, DBIO 작업의 결과를 데이터 오브젝트 형태로 가지고 있는 리스트이다.

java.util.ArrayList와 달리 모든 결과물을 가지고 있는 것이 아니라, ForwardList의 get API가 호출되는 시점에서 DBIO 결과물을 데이터 오브젝트로 변환해서 하나씩 반환한다. 또한 한번 조회된 인덱스의 결과물은 두 번 조회할 수 없는 forward-only이다.

ForwardList는 개발자가 임의로 생성하지 않아야 하는 객체이며, DB 데이터 오브젝트 팩토리의 다건조회 결과물로써 받아서 사용할 수 있다. ForwardList를 Iterator 혹은 for-each로 사용한 것이 아닌 객체를 생성하여 사용했다면 반드시 데이터 오브젝트 팩토리의 cleanUp(ForwardList list) API를 호출하여 ForwardList가 사용한 DB 리소스를 해제한다.

ScrollableList

ScrollableList는 iterator를 통한 순차 조회와 get(int index)를 통한 cursor 조회가 가능하다. ScrollableList는 DB 데이터 오브젝트 팩토리의 다건조회 결과물로써, DBIO 작업의 결과를 데이터 오브젝트 형태로 가지고 있는 리스트이다.

사용법은 ForwardList와 동일하지만, 사용자가 조회하고 싶은 인덱스 위치를 지정할 수 있다는 점이 다르다. ForwardList와 다르게 get API에 파라미터로 인덱스를 지정할 수 있으며, 해당 위치의 결과 값을 데이터 오브젝트로 반환받을 수 있다.

ScrollableList의 사용이 종료되었다면 반드시 데이터 오브젝트 팩토리의 cleanUp(ScrollableList list) API를 호출하여 ScrollableList가 사용한 DB 리소스를 해제한다.

DataObjectListDataObjectList는 DB 데이터 오브젝트 팩토리의 다건조회 결과물을 ArrayList에 담아서 준다. 즉, 모든 조회 결과가 ArrayList에 담겨서 제공되므로 조회 결과가 많은 경우 사용에 주의한다.

4.4.3.1. add

데이터 삽입을 수행할 때 호출하는 API이다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법 1)

    테이블에 삽입하려는 데이터를 데이터 오브젝트로 입력받아, INSERT 쿼리를 수행한다. 파라미터 값에 따라 Java JDBC의 addBatch를 수행한다.

    • add(DataObject insert, boolean immediate) 메소드의 immediate 변수 값이 false인 경우와 동일한 기능을 수행한다.

      int add(DataObject insert, boolean immediate)
      void add(DataObject insert)
    • 입력받은 리스트의 데이터 오브젝트들에 대해서 add(DataObject insert) 동작을 수행한 후 DB에 반영한다.

      int add(List <DataObject> insertList)
    • auto increased 속성을 지닌 column의 값을 구하기 위한 API이다. 입력값으로 넘겨준 데이터 오브젝트의 필드에 auto increased column의 데이터를 담아준다.

      int add(DataObject insert, String... autoIncreasedFields)
  • 사용법 2)

    userQuery를 사용하는 경우 데이터를 삽입하기 위한 API로 Java JDBC의 addBatch를 수행하지 않고, 쿼리 결과를 DB에 반영한다.

    int add()

4.4.3.2. clearBatch

add, update, remove API들을 통해 addBatch된 내역들에 대한 DB 반영을 취소한다. clearBatch는 사용자가 명시적으로 호출할 수도 있지만, 서비스가 실패한 경우 엔진 내부에서 호출한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    void clearBatch()

4.4.3.3. cleanUp

데이터 오브젝트 팩토리의 사용을 종료하고 데이터 오브젝트 팩토리가 사용한 모든 리소스를 반환한다. 일반적으로 서비스가 종료될 때 엔진에서 호출하는 API이다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법 1)

    DB 데이터 오브젝트 팩토리를 통해 생성한 ForwardList를 메모리에서 해제한다. ForwardList를 통해 리스트의 마지막까지 조회한 경우 엔진에서 호출한다. 인자로 받은 ForwardList를 생성하는데 사용했던 DB 자원을 해제한다.

    void cleanUp()
    void cleanUp(ForwardList<T> forwardlist)
  • 사용법 2)

    DB 데이터 오브젝트 팩토리를 통해 생성한 ScrollableList를 메모리에서 해제한다. ScrollableList의 사용을 종료하고 난 후에는 반드시 호출해야 한다. 인자로 받은 ScrollableList를 생성하는데 사용했던 DB 자원을 해제한다.

    void cleanUp(ScrollableList<T> scrollablelist)

4.4.3.4. executeAll

add, update, remove API들을 통해 addBatch된 내역들을 DB에 반영하는 기능을 수행한다.

executeAll은 사용자가 명시적으로 호출할 수도 있지만 다음의 경우 엔진 내부에서 호출하기도 한다.

  1. 서비스가 성공적으로 종료된 경우 트랜잭션 커밋을 수행하기 전에 엔진에서 호출한다.

  2. get, getForwardList, getScrollableList, getDataObjectList를 통해서 데이터를 조회하기 전에 호출한다.

  3. 이전에 수행한 쿼리와 현재 수행하려는 쿼리가 다른 경우 호출한다. 메소드의 반환값으로 addBatch되었던 내역의 갯수를 사용자에게 반환한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    int executeAll()

4.4.3.5. get

다건 조회를 수행할 때 호출하는 API이다. 쿼리 수행 결과를 ArrayList에 담아서 결과를 데이터 오브젝트 하나에 매핑하여 반환한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    DataObject get(void)

4.4.3.6. getDataObjectList

다건 조회를 수행할 때 호출하는 API이다. 쿼리 수행 결과를 ArrayList에 담아서 반환한다. 모든 조회 결과값이 ArrayList에 담겨있기 때문에 결과 개수에 따라 memory overflow를 고려해야 한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    ArrayList getDataObjectList(void)

4.4.3.7. getForwardList

다건 조회를 수행할 때 호출하는 API이다. 쿼리 수행 결과를 ForwardList에 매핑하여 반환한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    ForwardList getForwardList(void)

4.4.3.8. getResultCount

데이터 조회 결과수를 확인하는 API이다.

get(), getForwardList(), getScrollableList(), getDataObjectList()처럼 조회 결과를 받는 것이 아니라 조회 결과의 개수를 long 값으로 반환받기 위한 API다. API 호출 전에 데이터 조회를 위해 쿼리 선택 및 조건값을 설정하고, getResultCount()를 호출한다. 사용자가 선택한 쿼리에 "SELECT COUNT(*)" 쿼리를 붙여서 사용자가 선택한 쿼리의 결과 개수를 구한다. 실제로 조회용 쿼리가 실행되는 것을 참고한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    long getResultCount()

4.4.3.9. getScrollableList

다건 조회를 수행할때 호출하는 API이다. 쿼리 수행 결과를 ScrollableList에 매핑하여 반환한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    ScrollableList getScrollableList(void)

4.4.3.10. init

데이터 오브젝트 팩토리의 모든 상태를 초기화한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    void init()

4.4.3.11. setCondition

DB 데이터 오브젝트 팩토리의 C/R/U/D Operataion을 수행하기 전 선택한 SeparatedQuery에 대한 쿼리 조건을 설정한다. DB 데이터 오브젝트 팩토리를 개발하는 경우 지정한 FullQuery의 enum을 입력한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    setCondition(FULLQUERY Enum)

4.4.3.12. setFetchSize

데이터를 조회하는 경우 조회할 데이터의 페치 크기를 설정한다. API의 파라미터값을 이용하여 Java JDBC의 PreparedStatement의 setFetchSize를 호출한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    void setFetchSize(int fetchSize)

4.4.3.13. setFullQuery

DB 데이터 오브젝트 팩토리의 C/R/U/D Operataion을 수행하기 전 쿼리를 선택하기 위한 메소드이다.

DB 데이터 오브젝트 팩토리를 개발하는 경우 지정한 FullQuery의 enum을 입력한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    setFullQuery(FULLQUERY Enum)

4.4.3.14. setDynamicQuery

DB 데이터 오브젝트 팩토리의 C/R/U/D Operataion을 수행하기 전 쿼리를 선택하기 위한 메소드이다.

DB 데이터 오브젝트 팩토리를 개발하는 경우 지정한 DynamicQueryImple의 enum을 입력한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    setDynamicQuery(DynamicDOF.DynamicQueryImple Enum)

4.4.3.15. setPageNumber

페이징 기능을 이용하여 조회할 페이지 번호를 선택한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    void setPageNumber(int pageNumber)
    void setPageNumber(int pageNumber, String sortField, String sortType)

4.4.3.16. setPageSize

페이징 기능을 사용하여 데이터를 조회했을 때 페이지의 크기를 설정한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    void setPageSize(int size)

4.4.3.17. setPageQuery

ProObject의 페이징 기능은 Oracle, Tibero 기준으로 동작한다. Oracle, Tibero 이외의 DB에서 페이징을 원하는 경우 이 API를 이용한다. 예를 들어 DB2의 페이징 기능을 이용하려면 API의 파라미터로 "LIMIT"을 설정한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    void setPageQuery(String pageQuery)

4.4.3.18. setSeparatedQuery

DB 데이터 오브젝트 팩토리의 C/R/U/D Operataion을 수행하기 전 쿼리를 선택하기 위한 메소드이다.

DB 데이터 오브젝트 팩토리를 개발하는 경우 지정한 SeparatedQuery의 enum을 입력한다. SeparatedQuery를 사용할 때 반드시 setCondition API를 통해 쿼리에 사용할 조건도 입력해야 한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    setSeparatedQuery(SEPARATEDQUERY Enum)

4.4.3.19. setTimeout

쿼리 수행시간에 제한을 설정할 수 있는 API이다.

API의 파라미터 값을 이용하여 쿼리 수행 전 Java JDBC의 PreparedStatement에 setQueryTimeout을 호출한다. 기본값은 데이터 오브젝트 팩토리의 쿼리가 수행되기 전 남아있는 서비스 타임아웃 시간이다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    void setTimeout(int timeout)

4.4.3.20. reloadDataObjectConfig

ProManager를 통해 설정했던 데이터 오브젝트 팩토리의 설정들을 재설정한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    void reloadDataObjectConfig()

4.4.3.21. remove

데이터 삭제를 수행할 때 호출하는 API이다.

데이터 삭제는 삽입과 수정 API와 동일하게 삭제하려는 조건 값을 데이터 오브젝트로 입력받을 수도 있고, 조건 값을 DB 데이터 오브젝트 팩토리에 바로 설정할 수도 있다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법 1)

    파라미터 값에 따라 Java JDBC의 addBatch를 수행한다.

    int remove(boolean immediate)
  • 사용법 2)

    userQuery를 사용하는 경우 데이터를 삽입하기 위한 API로 Java JDBC의 addBatch를 수행하지 않고, 쿼리 결과를 DB에 반영한다.

    int remove()

4.4.3.22. update

데이터 수정을 수행할 때 호출하는 API이다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법 1)

    테이블에 수정하려는 데이터를 데이터 오브젝트로 입력받아 UPDATE 쿼리를 수행한다. 파라미터 값에 따라 JDBC의 addBatch를 수행한다.

    • update(DataObject update, boolean immediate) 메소드의 immediate 변수 값이 false인 경우와 동일한 기능을 수행한다.

      int update(DataObject update, boolean immediate)
      void update(DataObject update)
    • 입력값으로 넘겨준 데이터 오브젝트의 필드에 auto increased column의 데이터를 담아준다.

      int update(DataObject update, String... autoIncreasedFields)
  • 사용법 2)

    userQuery를 사용하는 경우 데이터를 삽입하기 위한 API로 Java JDBC의 addBatch를 수행하지 않고, 쿼리 결과를 DB에 반영한다.

    int update()

4.4.4. BeforeImage 기능

데이터 오브젝트 팩토리의 BeforeImage 기능은 데이터 오브젝트 팩토리에 매핑한 C/U/D 쿼리를 수행하여 테이블에 변경이 일어나면 변경되기 전의 로우 정보(BeforeImage)와 변경된 로우 정보(AfterImage)를 별도의 테이블에 기록한다. Insert문 수행시 추가된 로우 정보를 기록하고, Delete문 수행시 삭제된 로우 정보를, Update문의 수행시 변경 전과 변경 후의 로우 정보를 모두 기록한다.

이렇게 기록된 정보는 추후 사용자가 특정 시점으로 테이블을 되돌리려고 할 경우 활용될 수 있다.

4.4.4.1. 개발 방법

  1. BeforeImage 테이블 생성

    BeforeImage 테이블은 데이터 오브젝트 팩토리에 매핑된 C/U/D 쿼리가 수행되는 테이블명에 "_BF_IMG"가 뒤에 붙은 이름을 가져야 한다. 예를 들어 EMP 테이블이 원장 테이블인 경우 BeforeImage 테이블명은 EMP_BF_IMG이다.

    테이블의 컬럼들은 원장 테이블의 모든 컬럼을 필수적이며 다음과 같은 컬럼을 추가로 가진다.

    컬럼명 & 데이터타입데이터타입설명
    PO_OP_TYPEVARCHAR(2)
    • SQL의 종류

    • 입력값

      • 'D' (Delete)

      • 'UB' (Update Before)

      • 'UA' (Update After)

      • 'I' (Insert)

    PO_OP_SEQNUMBER
    • 실행 순번

    • 입력값 : SEQUENCE.nextval

    PO_OP_TIMETIMESTAMP
    • DB I/O가 일어난 시간

    • 기본값 : SYSTIMESTAMP

    PO_OP_FLAGDEFAULTVARCHAR(1)
    • 해당 row의 복원작업이 수행됐는지 여부

    • 입력값 : 'N', 'Y' (기본값: N)

    BeforeImage 테이블을 생성하는 DDL의 예시는 다음과 같다.

    CREATE TABLE EMP_BF_IMG (
      EMPNO INT,
      ENAME VARCHAR(255),
      ...
      PO_OP_TYPE VARCHAR(2),
      PO_OP_SEQ NUMBER,
      PO_OP_TIME TIMESTAMP DEFAULT SYSTIMESTAMP,
      PO_OP_FLAG VARCHAR(1) DEFAULT 'N'
    );

    Sequence를 별도로 기록하기 때문에 Tibero 기준으로 다음과 같은 DDL의 수행도 필요하다.

    CREATE SEQUENCE EMP_BF_IMG_SEQ INCREMENT BY 1;

    SEQUENCE명은 BeforeImage 테이블명에 "_SEQ"가 뒤에 붙은 이름을 가져야 한다.

  2. 데이터 오브젝트 팩토리 개발

    BeforeImage 기능을 이용하려면 데이터 오브젝트 팩토리를 개발하는 경우 BeforeImage Option을 활성화시켜야 한다.

  3. 서비스 설정

    BeforeImage 기능을 활성화한 데이터 오브젝트 팩토리를 서비스에서 사용하려면 서비스의 런타임 설정에서 SERVICE_{SERVICE_NAME}_BEFORE_IMAGE_ENABLE 값을 true로 설정해야 한다. 이 설정을 생략하면 BeforeImage 기능을 활성화한 데이터 오브젝트 팩토리를 사용해도 BeforeImage 기능을 이용할 수 없다.

4.5. DataDefinitionExecutor

DataDefinitionExecutor는 DDL(Data Definition Language) 문을 실행하기 위한 객체이다. SO나 BO의 로직에서 execute API를 이용하여 개발자로부터 직접 DDL 문자열을 입력받는다.

다음은 DataDefinitionExecutor를 사용하는 예시이다.

String create = "CREATE TABLE DUMP01 (ID NUMBER(4), NAME VARCHAR(50))";
DataDefinitionExecutor executor  = DataDefinitionExecutor.getInstance("test"); // dataSource 명 전달
boolean isSuccess = executor.execute(create);

4.6. 쿼리 오브젝트

쿼리 오브젝트(QueryObject)는 사용자가 데이터 오브젝트 매퍼에서 사용할 SQL 문을 들고 있는 객체이다. 쿼리 오브젝트의 개발은 스튜디오를 통해 개발하며, 확장자는 .qo이다. 사용자는 사용할 SQL 문과 해당 SQL 문과 매핑할 데이터 오브젝트를 지정한다.

4.6.1. 개발 방법

본 절에서는 쿼리 오브젝트를 개발하는 과정에 대해서 설명한다.

4.6.1.1. 선행 개발 과정

쿼리 오브젝트를 개발하기 위해서는 반드시 데이터 오브젝트 생성 과정이 선행되어야 한다.

쿼리 오브젝트를 개발하기 위해서는 데이터 오브젝트가 선행 개발되어야 한다. 데이터 오브젝트는 흔히 쓰이는 개념인 DataTransferObject의 개념에서 확장되어 ORM의 Entity 개념을 포함하고 있다. ProObject에서는 데이터 오브젝트가 서비스 간의 input, output을 담당하며 DBIO와 FILE IO의 단위이다. DBIO로 사용하려는 데이터 오브젝트를 개발할 때는 SQL을 수행하려는 대상 테이블들의 view로 생각한다. 기본적인 데이터 오브젝트에 대한 개발 방법은 “4.3. 데이터 오브젝트”를 참고한다.

데이터 오브젝트의 멤버변수로 다른 데이터 오브젝트를 포함하는 경우는 허용하지 않으므로 주의한다.

4.6.1.2. 수행로직 개발

쿼리 오브젝트 자체는 개발자가 입력한 쿼리의 정보만을 갖고 있는 객체이다. 그러므로 별도의 수행로직은 없으며 개발자가 쿼리에 대한 정보를 필요한 경우 쿼리 오브젝트 API를 이용하여 원하는 정보를 얻을수 있다.

  1. SQL 입력

    쿼리 오브젝트 자체는 개발자가 입력한 쿼리의 정보만을 갖고 있는 객체이다. 그러므로 별도의 수행로직은 없으며 개발자가 쿼리에 대한 정보를 필요한 경우 쿼리 오브젝트 API를 이용하여 원하는 정보를 얻을수 있다. 쿼리 오브젝트에 데이터 오브젝트 매퍼에서 사용할 쿼리를 입력한다. 쿼리는 여러 개를 입력가능하며, SO/BO에서 데이터 오브젝트 매퍼를 사용하는 경우 동작할 쿼리를 선택할 수 있다.

    쿼리 오브젝트는 사용할 SQL을 미리 입력하고, SO나 BO에서 데이터 오브젝트 매퍼를 통해 사용할 SQL을 호출한다. 쿼리 오브젝트에 사용할 수 있는 SQL의 개수는 제한이 없으나, 동일한 내용의 SQL을 중복해서 입력할 수 없다. 개발자는 C/R/U/D 타입별로 자신이 사용할 SQL을 쿼리 오브젝트에 입력할 수 있으며, 반드시 SQL에 해당하는 SQL ALIAS를 입력해야 한다. SQL ALIAS는 데이터 오브젝트 매퍼를 통해 개발자가 사용할 SQL을 선택할 때 SQL을 구분하는 단위가 되므로 반드시 사용자가 이해할 수 있는 의미를 담아 지정하도록 한다.

  2. 데이터 오브젝트 선택

    데이터 오브젝트 팩토리의 경우 사용자가 지정한 모든 쿼리는 Target DO 한가지 타입에 맞춰서 매핑된다. 하지만 쿼리 오브젝트는 쿼리당 매핑할 데이터 오브젝트를 지정할 수 있다. 지정된 데이터 오브젝트 타입은 데이터 오브젝트 매퍼가 쿼리와 매핑하는 기본 데이터 오브젝트가 된다.

4.6.2. 쿼리 오브젝트 API

다음은 쿼리 오브젝트가 제공하는 API 목록이다.

4.6.2.1. getParameterInfo

현재 선택된 쿼리 오브젝트의 쿼리에서 사용되는 파라미터들의 정보를 반환하는 API이다.

쿼리에서 사용된 파라미터 정보를 java.util.Map으로 반환한다. Map의 Key는 파라미터명이며, Value는 파라미터 정보를 담고있는 com.tmax.proobject.dataobject.model.Parameter 객체이다. Parameter 객체는 파라미터의 이름, Class type, 파라미터 인덱스(index), 파라미터의 속성(조건, 값, 다이나믹 파라미터 여부)을 담고 있다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public Map<String, Parameter> getParameterInfo()

4.6.2.2. getResultType

현재 선택된 쿼리 오브젝트의 쿼리 종류를 판단하는 API이다. 데이터 오브젝트 매퍼에서 해당 API를 호출하여 쿼리와 매핑된 데이터 오브젝트 타입을 사용한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public Class<? extends DataObject> getResultType()

4.6.2.3. getTableList

현재 선택된 쿼리 오브젝트의 쿼리가 사용하는 테이블 목록을 반환하는 API이다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public List<String> getTableList()

4.6.2.4. getType

현재 선택된 쿼리 오브젝트의 쿼리 종류를 판단하는 API이다.

API를 호출하는 경우 com.tmax.proobject.dataobject.factory.DBType이 반환되며, 해당 클래스는 enum 클래스로 SELECT, INSERT, UPDATE, DELETE 값을 반환한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    DBType getType()

4.6.2.5. isDynamicQuery

현재 선택된 쿼리 오브젝트의 쿼리에서 다이나믹 키워드가 사용되었는지 확인하기 위한 API이다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public boolean isDynamicQuery()

4.7. 데이터 오브젝트 매퍼

데이터 오브젝트 매퍼(DataObjectMapper)는 사용자가 개발한 쿼리 오브젝트의 쿼리를 실행하는 엔진 모듈이다. 데이터 오브젝트 팩토리가 개발할 때 지정된 쿼리만을 사용해야 한다면, 데이터 오브젝트 매퍼는 개발된 모든 쿼리 오브젝트의 쿼리들을 실행시킬 수 있다.

데이터 오브젝트 팩토리와 마찬가지로 DB 조회 결과는 데이터 오브젝트로 반환되며, 기본은 쿼리 오브젝트 개발 당시 지정된 쿼리의 반환 타입을 사용한다.

4.7.1. 개발 방법

본 절에서는 데이터 오브젝트를 개발하는 과정에 대해서 설명한다.

4.7.1.1. 선행 개발 과정

데이터 오브젝트 매퍼를 사용하여 개발하기 위해서는 반드시 다음의 과정이 선행되어야 한다.

  1. 데이터 오브젝트 생성

    데이터 오브젝트 매퍼로 개발하기 위해서는 반드시 데이터 오브젝트가 선행 개발되어야 한다. 데이터 오브젝트 매퍼는 DB 조회 결과를 데이터 오브젝트 형태로 사용자에게 반환하고, 데이터 오브젝트의 데이터들을 DB에 추가, 수정하기 때문이다. 기본적인 데이터 오브젝트에 대한 개발 방법은 “4.3. 데이터 오브젝트”를 참고한다.

    데이터 오브젝트의 멤버변수로 다른 데이터 오브젝트를 포함하는 경우는 허용하지 않으므로 주의한다.

  2. 쿼리 오브젝트 생성

    데이터 오브젝트 매퍼로 개발을 하기 위해서는 반드시 쿼리 오브젝트가 선행 개발되어야 한다. 데이터 오브젝트 매퍼는 쿼리 오브젝트에 존재하는 쿼리들을 실행시키는 것이므로 쿼리 오브젝트가 선행 개발되어 있지 않다면 쿼리를 수행할 수가 없기 때문이다. 기본적인 쿼리 오브젝트에 대한 개발 방법은 “4.6. 쿼리 오브젝트”를 참고한다.

4.7.1.2. 수행로직 개발

데이터 오브젝트 매퍼를 이용한 개발은 다음의 과정으로 진행된다.

  1. 데이터소스 설정 및 객체 생성

    데이터 오브젝트 매퍼는 ProObject에서 제공하는 기본 DI(Dependency Injection)의 대상이 아니다. 그러므로 개발자가 직접 DI 기능을 추가하고 데이터 오브젝트 매퍼의 객체를 생성해야 한다.

    데이터 오브젝트 매퍼는 Constructor에 데이터소스명을 직접 입력받도록 되어 있으며, 데이터소스명이 입력되지 않는 경우 ProObject.xml에 설정된 기본 데이터소스를 사용한다. 사용하려는 데이터소스는 반드시 dbio_config.xml에 설정된 PairDataSource의 alias명과 일치해야 한다.

    또한 데이터 오브젝트 매퍼의 객체를 생성할 때에 매퍼 객체가 사용할 데이터 오브젝트 타입을 Generic 타입으로 명시해줘야 한다. 데이터 오브젝트 매퍼는 객체를 생성할 때 입력받은 Generic 타입으로 DB 조회, 삽입, 수정 작업을 수행한다.

    // ds1이라는 데이터소스를 사용하고, Emp를 반환 타입으로 갖는 데이터 오브젝트 매퍼 객체 생성
    DataObjectMapper<Emp> mapper = new DataObjectMapper("ds1");
  2. 쿼리 선택

    사용할 데이터소스명을 지정하여 DB 데이터 오브젝트 팩토리의 객체를 생성했다면 개발에 필요한 쿼리를 선택해야 한다.

    데이터 오브젝트 매퍼는 쿼리 오브젝트의 enum 객체를 쿼리로 입력받는다. 데이터 오브젝트 매퍼의 setQuery API를 이용하여 데이터 오브젝트 매퍼가 사용할 쿼리를 설정해야 한다 . 선택한 쿼리는 쿼리 수행 API들(get, getForwardList, getScrollableList, getDataObjectList, add, update, delete)가 호출된 후에는 초기화되므로 쿼리를 반복 수행할 때는 반드시 쿼리 수행 API 호출 전에 쿼리 선택 API를 호출한다.

    // 쿼리 오브젝트 BizAQuery의 SELECT1 쿼리 사용
    mapper.setQuery(BizAQuery.SELECT1);
  3. 파라미터 입력

    기존 Java JDBC에서는 쿼리 안에서 파라미터 값들을 '?'로 표시했으며, 파라미터 값은 setObject(int index)와 같이 개발자가 파라미터 위치를 지정해야 한다. 이러한 불편함을 해소하기 위해 데이터 오브젝트 매퍼는 쿼리 오브젝트에 ':파라미터명' 형태로 파라미터를 입력한다.

    쿼리에 설정된 파라미터의 위치에 따라 DB 데이터 오브젝트 팩토리에 파라미터 값을 입력하는 방식은 두 가지로 나뉜다.

    • 쿼리 조건(쿼리 내 WHERE 절에 해당하는 파라미터)에 위치한 파라미터 값을 입력하는 방법

      데이터 오브젝트 매퍼에서는 setParameter(String name, Object value) API를 이용하여 파라미터를 입력한다.

      mapper.setParameter("empno", 1);
    • 데이터 삽입/수정에 대한 파라미터 값을 입력하는 방법

      데이터 삽입/수정에 대한 파라미터란 INSERT 쿼리의 VALUES() 절에 포함되는 파라미터들이나 UPDATE 쿼리의 SET() 절에 포함되는 파라미터들을 뜻한다. 이러한 파라미터들은 Target DO에 값을 입력해야 한다. 즉, 파라미터명과 Target DO의 멤버변수명이 일치해야 한다.

    쿼리 조건에 해당하는 파라미터와 데이터 삽입/수정에 해당하는 파라미터 값 입력방식이 다른 이유에 대해서 설명한다. 우선 쿼리 조건에 해당하는 파라미터의 경우 조건에 따라 다양한 이름으로 설정할 수 있다. 예를 들어 나이의 범위를 지정하는 조건의 경우 AGE라는 컬럼에 대해서 다음과 같이 쿼리를 구성할 수 있다.

    AGE > :mimAge AND AGE < :maxAge

    maxAge나 minAge와 같은 변수까지 모두 데이터 오브젝트에 선언하는 것은 불필요한 멤버변수를 늘리는 행위이므로 데이터 오브젝트가 아닌 DB 데이터 오브젝트 팩토리를 통해 파라미터 값을 입력받을 수 있다. 반면 데이터 삽입/수정 쿼리의 경우 입력받는 데이터 파라미터가 테이블의 컬럼과 정확하게 1:1로 일치한다. 예를 들어 "INSERT INTO EMP(EMPNO, ENAME, AGE) VALUES(~) "와 같은 쿼리에서 VALUES에 해당하는 파라미터들은 삽입하려는 테이블 컬럼에 1:1로 매핑하도록 제한된다.

  4. 쿼리 수행 API 호출

    실행할 쿼리를 선택하고 파라미터까지 입력한 다음에는 DBIO 작업을 수행할 API를 호출한다. DBIO용 API는 C/R/U/D별로 나누어져 있으며, SELECT는 다시 단건 조회용 다건 조회용으로 나뉜다.

    참고

    DBIO API를 호출하면 DB 데이터 오브젝트 매퍼에 설정했던 쿼리와 파라미터 정보가 초기화되므로 똑같은 쿼리를 반복수행한다고 해도 위에서 설명한 '2.쿼리 선택'과 '3.파라미터 입력'까지의 과정을 반복해야 한다.

4.7.2. 데이터 오브젝트 매퍼 API

다음은 데이터 오브젝트 매퍼가 제공하는 API 목록이다.

4.7.2.1. add

데이터 삽입을 수행할 때 호출하는 API이다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법 1)

    테이블에 삽입하려는 데이터를 데이터 오브젝트로 입력받아, INSERT 쿼리를 수행한다. 파라미터 값에 따라 Java JDBC의 addBatch를 수행한다.

    public int add(T insert, boolean immediate)
  • 사용법 2)

    addBatch를 기본으로 수행하는 API이다. 사용법1)에서 immediate 값을 false로 준것과 동일한 기능을 수행한다.

    public void add(T insert)
  • 사용법 3)

    auto increased 속성을 지닌 column의 값을 구하기 위한 API이다. 입력값으로 넘겨준 데이터 오브젝 트의 필드에 auto increased column의 데이터를 담아준다.

    public int add(T insert, String... autoIncreasedFields)

4.7.2.2. cleanUp

데이터 오브젝트 팩토리의 사용을 종료하고 데이터 오브젝트 팩토리가 사용한 모든 리소스를 반환한다. 일반적으로 서비스가 종료될 때 엔진에서 호출하는 API이다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법 1)

    addBatch된 내용을 모두 execute시키고 데이터 오브젝트 매퍼가 사용한 Java 리소스와 DB 리소스를 반납한다.

    public void cleanUp()
  • 사용법 2)

    ForwardList와 ScrollableList는 사용자에게 데이터 조회값을 전달하기 위해서 Java ResultSet 객체와 Statement 객체를 들고 있다. 해당 DB 리소스를 명시적으로 반환하기 위해 사용하는 API이다.

    public void cleanUp(ForwardList<T> forwardlist)
    public void cleanUp(ScrollableList<T> scrollablelist)

4.7.2.3. clearBatch

add, update, remove API들을 통해 addBatch된 내역들에 대한 DB 반영을 취소한다. clearBatch는 사용자 가 명시적으로 호출할 수도 있지만, 서비스가 실패한 경우 엔진 내부에서 호출한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public void clearBatch()

4.7.2.4. executeAll

add, update, remove API들을 통해 addBatch된 내역들을 DB에 반영하는 기능을 수행한다.

executeAll은 사용자가 명시적으로 호출할 수도 있지만 다음의 경우 엔진 내부에서 호출하기도 한다.

  1. 서비스가 성공적으로 종료된 경우 트랜잭션 커밋을 수행하기 전에 엔진에서 호출한다.

  2. get, getForwardList, getScrollableList, getDataObjectList를 통해서 데이터를 조회하기 전에 호출한다.

  3. 이전에 수행한 쿼리와 현재 수행하려는 쿼리가 다른 경우 호출한다. 메소드의 반환값으로 addBatch되었던 내역의 갯수를 사용자에게 반환한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public int executeAll()

4.7.2.5. get

데이터 오브젝트 매퍼를 이용하여 단건 조회를 수행할때 호출하는 API이다.

호출하는 경우 데이터 조회 결과를 데이터 오브젝트 매퍼 객체 생성당시 Generic으로 지정한 타입으로 반환한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public T get()

4.7.2.6. getDataObjectList

데이터 오브젝트 매퍼를 이용하여 다건 조회를 수행할때 호출하는 API이다.

쿼리 수행 결과를 ArrayList에 담아서 반환한다. 모든 조회 결과 값이 ArrayList에 담겨있기 때문에 결과 개수에 따라 memory overflow를 고려해야 한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public ArrayList<T> getDataObjectList()

4.7.2.7. getForwardList

데이터 오브젝트 매퍼를 이용하여 다건 조회를 수행할 때 호출하는 API이다. 조회 결과를 ForwardList에 매핑하여 반환한다. forward-only 조회를 수행한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public List<T> getForwardList()

4.7.2.8. getResultCount

데이터 조회 결과수를 확인하는 API이다.

get(), getForwardList(), getScrollableList(), getDataObjectList()처럼 조회 결과를 받는 것이 아니라 조회 결과의 개수를 long 값으로 반환받기 위한 API다. API 호출 전에 데이터 조회를 위해 쿼리 선택 및 조건값 을 설정하고, getResultCount()를 호출한다. 사용자가 선택한 쿼리에 "SELECT COUNT(*)" 쿼리를 붙여서 사용자가 선택한 쿼리의 결과 개수를 구한다. 실제로 조회용 쿼리가 실행되는 것을 참고한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public long getResultCount()

4.7.2.9. getScrollableList

데이터 오브젝트 매퍼를 이용하여 다건 조회를 수행할때 호출하는 API이다. 조회 결과를 ScrollableList에 매핑하여 반환한다. scroll-insensitive 조회를 수행한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public ScrollableList<T> getScrollableList()

4.7.2.10. init

데이터 오브젝트 팩토리의 모든 상태를 초기화한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public void init()

4.7.2.11. reloadDataObjectConfig

ProManager를 통해 설정했던 데이터 오브젝트 팩토리의 설정들을 재설정한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public void reloadDataObjectConfig()

4.7.2.12. remove

데이터 삭제를 수행할 때 호출하는 API이다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법 1)

    파라미터 값에 따라 Java JDBC의 addBatch를 수행한다.

    public int remove(boolean immediate)
  • 사용법 2)

    addBatch를 기본으로 수행하는 API이다. 사용법1)에서 immediate 값을 false로 준것과 동일한 기능을 수행한다.

    public void remove()

4.7.2.13. setFetchSize

데이터를 조회하는 경우 조회할 데이터의 페치 크기를 설정한다. API의 파라미터값을 이용하여 JavaJDBC 의 PreparedStatement의 setFetchSize를 호출한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public void setFetchSize(int fetchSize)

4.7.2.14. setPageNumber

페이징 기능을 이용하여 조회할 페이지 번호를 선택한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법 1)

    public void setPageNumber(int pageNumber)
  • 사용법 2)

    페이징을 수행할 때 특정 필드로 정렬이 필요한 경우 파라미터를 통해서 정렬방법(ASC, DESC)과 정렬의 기준이될 필드를 선택할 수 있다. 기본 정렬 타입은 'ASC'이다.

    public void setPageNumber(int pageNumber, String sortField, String sortType)

4.7.2.15. setPageQuery

ProObject의 페이징 기능은 Oracle, Tibero 기준으로 동작한다. Oracle, Tibero 이외의 DB에서 페이징을 원하는 경우 이 API를 이용한다. 예를 들어 DB2의 페이징 기능을 이용하려면 API의 파라미터로 "LIMIT"을 설정한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public void setPageQuery(String pageQuery)

4.7.2.16. setPageSize

페이징 기능을 사용하여 데이터를 조회했을 때 페이지의 크기를 설정한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public void setPageSize(int size)

4.7.2.17. setQuery

데이터 오브젝트 매퍼가 사용할 쿼리를 선택하는 API이다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법 1)

    API의 파라미터로 쿼리 오브젝트의 쿼리를 넘겨준다.

    public DataObjectMapper<T> setQuery(QueryInfo query)
  • 사용법 2)

    쿼리 오브젝트에 지정된 반환 타입과 데이터 오브젝트 매퍼의 Generic 타입이 다른 경우 setQuery API에 데이터 오브젝트 매퍼에 지정한 Generic 타입을 넘겨줘야 한다.

    public DataObjectMapper<T> setQuery(QueryInfo query, Class<T> returnType)

4.7.2.18. setTimeout

쿼리 수행시간에 제한을 설정할 수 있는 API이다.

API의 파라미터 값을 이용하여 쿼리 수행 전 Java JDBC의 PreparedStatement에 setQueryTimeout을 호출한다. 기본값은 데이터 오브젝트 팩토리의 쿼리가 수행되기전 남아 있는 서비스 타임아웃 시간이다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public void setTimeout(int timeout)

4.7.2.19. update

데이터 수정을 수행할 때 호출하는 API이다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법 1)

    테이블에 수정하려는 데이터를 데이터 오브젝트로 입력받아 UPDATE 쿼리를 수행한다. 파라미터 값에 따라 JDBC의 addBatch를 수행한다.

    public int update(T update, boolean immediate)
  • 사용법 2)

    addBatch를 기본으로 수행하는 API이다. 사용법1)에서 immediate 값을 false로 준것과 동일한 기능을 수행한다.

    public void update(T update)
  • 사용법3)

    auto increased 속성을 지닌 column의 값을 구하기 위한 API이다. 입력값으로 넘겨준 데이터 오브젝 트의 필드에 auto increased column의 데이터를 담아준다.

    public int update(T update, String... autoIncreasedFields)

4.8. 파일 데이터 오브젝트 팩토리

본 절에서는 File IO를 수행하는 파일 데이터 오브젝트 팩토리에 대한 개념과 개발방법에 대해 설명한다.

4.8.1. 구조

파일 데이터 오브젝트 팩토리는 Fixed Length, Delimiter, XML 3가지 방식으로 파일 읽기, 쓰기 작업을 수행하는 객체다.

파일 데이터 오브젝트 팩토리는 사용자가 지정한 파일에 대해서 라인 단위로 데이터를 읽고 쓸 수 있며, 읽어들인 데이터는 데이터 오브젝트로 객체 매핑이 되고, 데이터 오브젝트 객체를 입력받아 해당 객체의 데이터를 파일에 쓸 수 있다.

파일 데이터 오브젝트 팩토리는 헤더와 테일 기능을 이용하여, 파일의 처음과 마지막에 사용자가 원하는 서식으로 내용을 추가할 수 있는 기능을 제공한다. ProStudio를 통해 바디(body)가 될 파일 데이터 오브젝트 팩토리에 헤더와 테일이 될 파일 데이터 오브젝트 팩토리를 지정한다. 바디 파일 데이터 오브젝트 팩토리에 getHeader()와 getTail() API를 이용하여 헤더와 테일 객체를 얻어올 수 있다.

4.8.2. 개발 방법

본 절에서는 파일 데이터 오브젝트 팩토리를 개발하는 과정에 대해서 설명한다.

4.8.2.1. 선행 개발 과정

파일 데이터 오브젝트 팩토리를 개발하기 위해서는 반드시 선행되어야 하는 절차가 필요하다.

  1. 데이터 오브젝트 생성

    데이터 오브젝트 팩토리를 개발하기 위해서는 반드시 기반이 될 데이터 오브젝트가 존재해야 한다.

  2. 데이터 오브젝트 선택

    데이터 오브젝트 팩토리의 기반이 될 데이터 오브젝트를 선택한다. 파일 데이터 오브젝트 팩토리는 선택된 데이터 오브젝트 형태로 파일 내용을 읽고, 데이터 오브젝트의 내용을 파일의 라인 하나로 기록한다.

  3. 파일 데이터 오브젝트 객체 생성

    파일 데이터 오브젝트 객체의 Constructor에 파일 경로를 입력하여 데이터 오브젝트 팩토리 객체를 생성한다.

  4. 기능 수행

    get, add API를 이용하여 파일의 내용을 읽고 쓰는 동작을 수행한다. DB 데이터 오브젝트 팩토리에서 제공하는 update, delete는 파일 데이터 오브젝트 팩토리에서 제공되지 않는다.

4.8.2.2. 개발 과정

본 절에서는 실제 파일 데이터 오브젝트의 활용 샘플 코드를 통해서 파일 데이터 오브젝트 팩토리를 사용하는 방법에 대해서 설명한다.

파일 데이터 오브젝트를 이용한 개발은 다음의 과정으로 진행된다.

  1. 파일 데이터 오브젝트 팩토리 객체 생성 및 파일 설정

    //* SampleData 파일을 이용하여 파일 입출력을 하기위한 파일 데이터 오브젝트 팩토리 객체 생성
    SampleFileDataObjectFactory factory = new SampleFileDataObjectFactory();
    factory.setFile(new File("C:\Users\Tmax\Desktop\SampleData"));
    //* 기존 데이터 내용을 지우고 새로 파일을 쓰고 싶은 경우 Constructor에 false 값을 준다.
    //* factory.setFile(new File("C:\Users\Tmax\Desktop\SampleData"),false);
  2. 파일 데이터 오브젝트 팩토리 압축 파일 설정

    //* 압축 파일 설정
    factory.applyZipFile(true);
  3. 파일 데이터 오브젝트의 I/O 대상이 바이너리 파일인 경우 설정

    //* 바이너리 파일 설정
    factory.applyBinaryFile(true);
  4. 파일 데이터 오브젝트 팩토리 인코딩 캐릭터 셋 설정

    factory.setCharSet("UTF-8");
    //* 혹은 factory.setCharSet("EUC-KR");
  5. 파일 데이터 오브젝트 팩토리 계행문자 설정

    //* \n
    factory.setNewLine(true); // or factory.setNewLineLength(1);
    //* \r\n
    factory.setNewLine(false); // or factory.setNewLineLength(2);
  6. 파일 데이터 오브젝트 팩토리 파일 읽기

    SampleDataObject output = factory.get();
    System.out.println(output);
    
  7. 파일 데이터 오브젝트 팩토리 여러 행 읽기

    //* 파일의 처음부터 끝까지 루프를 돌면서 한 라인씩 읽는다.
    ForwardList>SampleDataObject< output = factory.getForwardList();
    for(SampleDataObject item : output) {
        System.out.println(item);
    }
  8. 파일 데이터 오브젝트 팩토리 객체 파일 쓰기

    SampleDataObject item = new SampleDataObject();
    item.setEmpno(1);
    item.setEname("Jhon");
    item.setSal(100);
    item.setDeptno(10);
    factory.add(item);
    //* 명시적으로 버퍼를 비우고 싶다면 factory.flush();
  9. 파일 데이터 오브젝트 팩토리 헤더 파일 읽기

    SampleHeaderItem headerItem = factory.getHeader().get();
    System.out.println(headerItem);
  10. 파일 데이터 오브젝트 팩토리 헤더 파일 쓰기

    SampleHeaderItem headerItem = new SampleHeaderItem();
    headerItem.setMgr(10);
    headerItem.setHireDate("2018-03-31");
    factory.getHeader().add(headerItem);
  11. 파일 데이터 오브젝트 팩토리 테일 파일 읽기

    SampleTailItem tailItem = factory.getTail().get();
    System.out.println(tailItem);
  12. 파일 데이터 오브젝트 팩토리 테일 파일 쓰기

    SampleTailItem tailItem = new SampleTailItem();
    tailItem.setDname("lab1");
    tailItem.setPhoneNumber("010-0000-0000");
    factory.getTail().add(tailItem);

4.8.3. 파일 데이터 오브젝트 팩토리 API

파일 데이터 오브젝트 팩토리를 개발하기 위해서 다음의 API를 제공한다.

4.8.3.1. add

입력받은 데이터 오브젝트의 데이터들을 파일 데이터 오브젝트의 파일 형식에 따라 한 라인으로 파일에 기록한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public void add(DataObject)

4.8.3.2. applyBinaryFile

FILE I/O의 대상이 바이너리 파일인지 텍스트 파일인지 선택하기 위한 API이다.

기본적으로 FILE DOF는 setFile로 입력받은 파일은 텍스트 파일로 취급한다. FILE I/O 대상이 바이너리 파일이면 파라미터로 'true'를 텍스트 파일이면 파라미터로 'false'를 전달한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    void applyBinaryFile(boolean isBinary)

4.8.3.3. applyZipFile

FILE I/O의 대상이 압축파일인 경우를 설정하기 위한 API이다. 기본적으로 FILE DOF는 setFile로 입력받은 파일은 일반 파일로 취급한다. FILE I/O 대상이 압축파일인 경우 파라미터로 'true'를 전달한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    void applyZipFile(boolean isZipFile)

4.8.3.4. cleanUp

파일 데이터 오브젝트 팩토리가 사용한 파일 리소스와 팩토리의 리소스들을 모두 해제한다. 헤더와 테일이 존재할 경우 헤더 테일의 리소스도 해제한다. 일반적으로 서비스를 종료하는 경우 자동으로 호출되는 메소드이다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public void cleanUp(void)

4.8.3.5. flush

파일 입력 작업 중 버퍼에 남은 내용을 파일에 기록한다. ProObject에서는 서비스를 종료하는 경우 자동으로 호출되는 함수이다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public void flush(void)

4.8.3.6. get

파일의 내용 하나를 읽어서 데이터 오브젝트로 반환받는다. 파일의 내용은 데이터 오브젝트의 각 멤버변수에 저장된다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public DataObject get(void)

4.8.3.7. getCharSet

현재 파일 데이터 오브젝트 팩토리가 사용하는 인코딩 캐릭터 셋을 반환한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public String getCharSet(void)

4.8.3.8. getFile

현재 파일 데이터 오브젝트 팩토리의 입출력 대상이되는 파일 객체를 반환한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public File getFile(void)

4.8.3.9. getForwardList

DB 데이터 오브젝트의 getForwardList와 동일한 기능을 수행한다. Iterator, for-each를 수행하면서 파일의 내용을 순차적으로 읽어서 데이터 오브젝트로 반환한다. 또는 ForwardList의 get() API를 호출하는 경우 파일의 한 라인을 데이터 오브젝트로 반환한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public ForwardList<DataObject> getForwardList(void)

4.8.3.10. getHeader

현재 파일 데이터 오브젝트 팩토리의 헤더로 지정된 파일 데이터 오브젝트 팩토리를 반환한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public FileDataObjectFactory getHeader(void)

4.8.3.11. getTail

현재 파일 데이터 오브젝트 팩토리의 테일로 지정된 파일 데이터 오브젝트 팩토리를 반환한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public FileDataObjectFactory getTail(void)

4.8.3.12. isBinaryFile

현재 FILE DOF의 FILE I/O 대상이 바이너리 파일인지 텍스트 파일인지를 반환한다. 바이너리 파일인 경우 'true'를 반환하고, 텍스트 파일인 경우 'false'를 반환한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    boolean isBinaryFile()

4.8.3.13. isZipFile

현재 FILE DOF의 FILE I/O 대상이 압축 파일인지 여부를 반환한다. 압축 파일인 경우 'true'를 반환하고, 일반 파일인경우 'false'를 반환한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    boolean isZipFile()

4.8.3.14. setCharSet

파일 데이터 오브젝트 팩토리에서 사용할 인코딩 캐릭터 셋을 설정한다.

입력값은 String으로 전달하며 'UTF-8'과 'EUC-KR' 둘 중 하나로 지정할 수 있다. 파일 데이터 오브젝트 팩토리에 헤더와 테일이 존재하면, 인코딩 캐릭터 셋을 동일하게 맞춘다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public void setCharSet(String)

4.8.3.15. setFile

파일 데이터 오브젝트가 파일 입출력을 수행해야 하는 대상을 파일 객체로 전달한다.

setFile API 호출을 하면 이전에 호출하였던 applyBinaryFile, applyZipFile로 지정했던 파일 형태가 초기화되므로 참고하도록한다. setFile 호출 이후 바이너리 파일이나 압축 파일에 대한 설정을 추가해야 한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법 1)

    파일 데이터 오브젝트 팩토리를 생성하는 경우 Constructor에 파일 경로를 입력한 경우 팩토리 내부에서 자동으로 호출되는 메소드이다. 이 메소드는 기존 파일 내용에 이어서 입출력 작업을 하는 동작을 위해 호출된다.

    public void setFile(File)

  • 사용법 2)

    기본 기능은 setFile(File)과 동일하지만 boolean 값에 따라 파일 입출력을 이어서 하거나 새로 할 수 있다. boolean 값이 true인 경우 파일 입출력을 이어서 하고, false인 경우 파일을 입력할 때 새로운 파일을 만들어서 사용한다.

    public void setFile(File, boolean)

4.8.3.16. setNewLine

파일 라인 변경 문자를 지정하기 위한 메소드이다. 파라미터가 true인 경우 '\n'을, false인 경우 '\r\n'을 계행문자로 지정한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public void setNewLine(boolean)

4.8.3.17. setNewLineLength

setNewLine(boolean)과 동일한 기능을 수행하는 메소드이다. 파라미터에 1을 입력한 경우 '\n'을, 2를 입력한 경우 '\r\n'을 계행문자로 지정한다.

다음은 API의 사용법에 대한 설명이다.

  • 사용법

    public void setNewLineLength(int)

4.9. 비동기 데이터 오브젝트

Complex Service에서의 데이터 입출력은 데이터 오브젝트 팩토리 객체를 생성한 후 팩토리의 메소드를 호출하기만 하면 된다.

데이터 오브젝트 팩토리의 메소드는 입출력 관련 API를 호출하면 Blocking 방식으로 I/O를 수행하게 된다. 다만 이벤트 서비스에서는 싱글 스레드로 작업이 수행되며, 싱글 스레드는 서비스 스케줄링 및 여러 이벤트 처리를 함께 담당하고 있기 때문에 Blocking 방식으로 I/O를 처리할 경우 심각한 성능의 저하를 가져올 확률이 높다. 하지만 데이터 입출력이 없는 서비스는 드물며, 흔히 말하는 간단한 서비스들은 데이터가 적고 간단한 I/O를 처리하는 서비스인 경우가 많다.

따라서 ProObject에서는 이러한 I/O를 처리하기 위해 비동기 데이터 오브젝트(AsyncDataObject, 이하 AsyncDO)라는 유틸 클래스를 두어 Non-Blocking 방식으로 I/O를 처리한다.

AsyncDO를 사용하기 위해서는 이미 데이터 오브젝트와 데이터 오브젝트 팩토리가 모두 작성되어 있어야 한다. AsyncDO는 기능을 수행할 때 마다 ServiceWorkThread가 할당되며 EventServiceManager를 통한 acall과 rcall을 호출한것과 동일한 기능을 수행한다.

4.9.1. 비동기 데이터 오브젝트 API

비동기 데이터 오브젝트를 위해 execute API를 제공한다.

4.9.1.1. execute

데이터 조회를 비동기 수행하기 위한 메소드이다.

  • 사용법

    public static void execute(DBDataObjectFactory<T> dbDo)
    public static void execute(DBDataObjectFactory<T> dbDo, DataObject dataobject, DBType type)
    public static void execute(DBDataObjectFactory<T> dbDo, String serviceMethodNameOnResponse)
    public static void execute(DBDataObjectFactory<T> dbDo, DataObject dataobject, DBType type, String serviceMethodNameOnResponse)
    public static void execute(DBDataObjectFactory<T> dbDo, String serviceMethodNameOnResponse, String serviceMethodNameOnError)
    public static void execute(DBDataObjectFactory<T> dbDo, DataObject dataobject, DBType type, String serviceMethodNameOnResponse, String serviceMethodNameOnError)
    파라미터설명
    dbDo수행할 쿼리가 선택된 데이터 오브젝트 팩토리이다.
    dataobjectINSERT, UPDATE 쿼리를 수행하는 경우 파라미터로 전달할 데이터 오브젝트 객체이다.
    type

    DOF가 수행할 타입을 선택한다.

    • SELECT

    • INSERT

    • UPDATE

    • DELETE

    serviceMethodNameOnResponseDB 작업을 수행한 후 응답을 처리할 메소드명이다.
    serviceMethodNameOnError비동기 작업 중 예외가 발생한 경우 처리할 메소드명이다.

4.9.2. AsyncDBOperationResult

AsyncDO의 execute api 호출하는 경우 serviceMethodNameOnResponse 값을 입력했다면, DB I/O 작업이 끝난 후 사용자가 지정한 메소드로 서비스에 재진입하게 된다. 이때 사용자는 반드시 메소드의 파라미터로 com.tmax.proobject.engine.system.dto.async.AsyncDBOperationResult를 지정해야 한다.

AsyncDBOperationResult에는 사용자의 쿼리 조회 결과에 대한 DataObject 목록이나 쿼리수행 결과 반영된 row count 수가 들어 있다.

  • 멤버변수

    멤버변수설명
    resultList데이터 오브젝트를 담고 있는 List 객체이다. SELECT를 수행한 경우 결과 값이 List에 담겨 있다.
    resultCnt

    쿼리 수행 결과가 담겨 있다.

    • SELECT의 경우 : 조회한 do 갯수

    • INSERT, UPDATE, DELETE인 경우 : 수행된 ROW 수

4.9.3. 개발 과정

다음은 AsyncDO를 사용하는 과정에 대한 설명이다.

  1. 이벤트 서비스 생성

    데이터 오브젝트를 비동기식으로 사용하기 위해서는 이벤트 서비스를 생성해야 한다.

    public class AsyncJDBCTest implements EventServiceObject {
    
       @Override
       public Object service(Object input, UserContext userContext) throws Throwable {
            return null;
       }
    
       @PostEventResponse
       public Object response(Object input, UserContext userContext) {
           return input;
       }
    } 
  2. 데이터 조회

    다음은 비동기로 SELECT 쿼리를 수행 후 조회 정보를 응답 메소드에서 처리하는 예제이다. 데이터 조회가 완료되면 사용자가 지정한 메소드에 진입하게 되고, AsyncDBOperationResult로부터 결과값 조회와 조회 건수를 확인할 수 있다.

    public class AsyncJDBCTest implements EventServiceObject {
    
      @Override
      public Object service(Object input, UserContext userContext) throws Throwable {
          ServiceLogger logger = (ServiceLogger) ServiceLogger.getLogger();
          logger.info("****************************************************************");
          logger.info("                        ASYNC DOF SELECT                        ");
          logger.info("****************************************************************");
          StringDataObject output = new StringDataObject();
    
          //-- (1) 쿼리를 수행시킬 데이터소스를 입력한다. --//
          EmpDO_Factory factory = new EmpDO_Factory("tibero6");
    
          //-- (2) 수행할 쿼리를 선택한다. --//
          factory.setFullQuery(FULLQUERY.SELECT1);
         
          //-- (3) 쿼리에 조건을 입력해야 한다면 조건값을 입력한다. --//
          factory.setEmpno(1);
            
          //-- (4) AsyncDataObject 클래스의 execute API를 호출하여 
               DOF와 리턴받을 메소드명을 입력한다. --//
          AsyncDataObject.execute(factory, "response");
            
            return output;
       }
    
      @PostEventResponse
      public Object response(AsyncDBOperationResult input, UserContext userContext) {
          ServiceLogger logger = (ServiceLogger) ServiceLogger.getLogger();
          logger.info("****************************************************************");
          logger.info("                ASYNC DOF SELECT RETURN                         ");
          logger.info("****************************************************************");
          StringDataObject output = new StringDataObject();
          output.setValue("TEST > ASYNCDOFSELECTRETURN");
            
          //-- (5) AsyncDBOperationResult의 resultList 멤버변수에 조회결과가 담겨 있다. --//
          EmpDO result = (EmpDO) input.getResultList().get(0);
            
          logger.info("[ASYNCDOFSELECTRETURN] result -> \n" + result.toString());
            
          return output;
      }
    } 
  3. 데이터 추가

    다음은 비동기로 INSERT 쿼리를 수행하는 예제이다.

    public class AsyncJDBCTest implements EventServiceObject {
    
      @Override
      public Object service(Object input, UserContext userContext) throws Throwable {
          ServiceLogger logger = (ServiceLogger) ServiceLogger.getLogger();
          logger.info("****************************************************************");
          logger.info("                    ASYNC DOF INSERT                            ");
          logger.info("****************************************************************");
          StringDataObject output = new StringDataObject();
    
          //-- (1) 쿼리를 수행시킬 데이터소스를 입력한다. --//
          EmpDO_Factory factory = new EmpDO_Factory("tibero6");
    
          //-- (2) 수행할 쿼리를 선택한다. --//
          factory.setFullQuery(FULLQUERY.INSERT1);
    
          //-- (3) 삽입할 데이터를 입력한다. --//
          EmpDO insert = new EmpDO();
          insert.setEmpno(1);
          insert.setEname("HONG GIL DONG);
          ...
            
          //-- (4) AsyncDataObject클래스의 execute API를 호출하여 
               사용할 DOF, 삽입할 데이터, 수행할 쿼리 타입을 입력한다. --//
          AsyncDataObject.execute(factory, insert, DBType.INSERT);
           
          return output;
      }
    } 

  4. 데이터 수정

    다음은 비동기로 UPDATE 쿼리를 수행하는 예제이다.

    public class AsyncJDBCTest implements EventServiceObject {
    
      @Override
      public Object service(Object input, UserContext userContext) throws Throwable {
          ServiceLogger logger = (ServiceLogger) ServiceLogger.getLogger();
          logger.info("****************************************************************");
          logger.info("                     ASYNC DOF UPDATE                           ");
          logger.info("****************************************************************");
          StringDataObject output = new StringDataObject();
    
          //-- (1) 쿼리를 수행시킬 데이터소스를 입력한다. --//
          EmpDO_Factory factory = new EmpDO_Factory("tibero6");
    
          //-- (2) 수행할 쿼리를 선택한다. --//
          factory.setFullQuery(FULLQUERY.UPDATE1);
    
          //-- (3) 수정할 데이터를 입력한다. --//
          EmpDO update = new EmpDO();
          update.setEname("HONG GIL DONG");
          update.setJob("Manager");
          factory.setEmpno(1);
          ...
            
          //-- (4) AsyncDataObject클래스의 execute API를 호출하여 
               사용할 DOF, 수정할 데이터, 수행할 쿼리 타입을 입력한다. --//
          AsyncDataObject.execute(factory, update, DBType.UPDATE);
            
          return output;
      }
    }  
  5. 데이터 삭제

    다음은 비동기로 DELETE 쿼리를 수행하는 예제이다.

    public class AsyncJDBCTest implements EventServiceObject {
    
      @Override
      public Object service(Object input, UserContext userContext) throws Throwable {
          ServiceLogger logger = (ServiceLogger) ServiceLogger.getLogger();
          logger.info("****************************************************************");
          logger.info("                     ASYNC DOF DELETE                           ");
          logger.info("****************************************************************");
          StringDataObject output = new StringDataObject();
    
          //-- (1) 쿼리를 수행시킬 데이터소스를 입력한다. --//
          EmpDO_Factory factory = new EmpDO_Factory("tibero6");
    
          //-- (2) 수행할 쿼리를 선택한다. --//
          factory.setFullQuery(FULLQUERY.DELETE1);
    
          //-- (3) 삭제할 쿼리 조건을 입력한다. --//
          factory.setEmpno(1);
            
          //-- (4) AsyncDataObject클래스의 execute API를 호출하여 
            사용할 DOF, 수행할 쿼리 타입을 입력한다. --//
          AsyncDataObject.execute(factory, null, DBType.DELETE);
           
          return output;
      }
    }  

4.10. 데이터 오브젝트 캐시

ProObject는 데이터 오브젝트 팩토리의 DB 작업을 수행하는데 필요한 시간을 줄임으로써, 좀더 나은 성능 향상을 사용자에게 제공하기 위해 세션 레이어의 캐시 기능을 제공한다. 캐시는 데이터 오브젝트의 키(key) 필드 값을 기준으로 데이터 오브젝트 단위로 캐싱을 한다. 그러므로 키 필드가 없는 경우 캐시 기능은 동작하지 않으므로 주의한다.

또한 system.properties에 SYSTEM_DATAOBJECT_CACHE_ENABLE 항목의 값이 true로 설정되어 있어야 캐시 기능이 동작한다. 해당 옵션이 true에서 false로 변경되면 cache invalidate를 수행한다.

본 절에서는 ProObject에서 캐시를 사용하기 위한 환경 설정과 동작 범위, 방식에 대해서 설명한다.

4.10.1. 캐시 사용이 가능한 DO 생성

ProObject에서 캐시 기능을 사용하기 위해서는 데이터 오브젝트에 키(key) 속성이 있는 필드가 존재해야 한다. key 속성은 ProManager를 통해 메타에 추가할 수 있다. 이때 지정하는 키는 테이블의 키가 아닌 ProObject가 캐싱을 데이터 오브젝트의 기준으로 ProManager의 메타에서 키 속성을 지정한 필드가 기준이 된다.

ProStudio를 통해서 DO를 생성하는 경우 반드시 키 속성을 지닌 메타를 사용하여 DO를 생성해야 한다. 키는 DO가 대응하는 테이블의 실제 키 컬럼에 해당하는 메타를 사용해도 되고, 사용자 임의의 키 필드를 선택해도 가능하다. 두 개 이상의 키 필드를 지정할 수도 있다.

4.10.2. 세션 캐시

세션 캐시는 트랜잭션 단위의 캐시로 한 서비스 내에서의 캐시를 보장한다. 이 기능을 사용하기 위해서는 데이터 오브젝트에 키(key) 필드가 명시되어 있어야 하며 애플리케이션 프로퍼티(APPLICATION_DATAOBJECT_CACHE)에 캐시 기능을 사용할 데이터 오브젝트 팩토리 목록을 기술해야 한다.

  • 세션 캐시의 동작범위

    세션 캐시는 한 트랜잭션 안에서 제공된다. 그러므로 일반적으로 서비스 단위로 제공되는 캐시로 생각하면 이해하기 쉽다. 다만 XA 트랜잭션이나 서비스 연동 호출의 경우 세션 캐시가 보장되지 않는 점을 주의한다.

  • 세션 캐시의 동작방식

    세션 캐시가 활성화된 경우 데이터 오브젝트 팩토리는 DO의 키 필드 값을 기준으로 세션 캐시에서 키에 해당하는 DO가 있는지 조회한다.

    세션 캐시 저장소에 키에 해당하는 DO가 존재하면 데이터 오브젝트 팩토리는 사용자에게 키에 해당하는 DO를 반환한다. 키에 해당하는 DO가 없는 경우 쿼리를 실행하여 DB 데이터를 조회하여 DO를 생성하고, 사용자에게 반환하는 동시에 세션 캐시 저장소에 생성된 DO를 저장한다.

    세션 캐시영역 시용하는 테이블들이 데이터 오브젝트 팩토리를 통해서 UPDATE/DELETE가 수행되면, 해당 세션 캐시들은 DIRTY 상태가 된다. 세션 캐시가 DIRTY가 되면, 데이터 오브젝트 팩토리는 세션 캐시에서 데이터를 조회하지 않고 DB 작업을 수행한다.

    세션이 commit, rollback되는 경우 세션 캐시의 DIRTY 상태는 해제된다. 또한 commit을 수행하는 경우 세션 캐시가 DIRTY 상태라는 것은 세션 캐시가 바라보는 테이블들에 UPDATE, DELETE가 발생했다는 의미이므로 해당 테이블을 사용하는 세션 캐시 영역을 모두 무효화(invalidate)한다.

4.10.3. 2차 캐시

2차 캐시 기능은 상용 캐시 제품을 데이터 오브젝트 팩토리에서 사용 가능하도록 하는 인터페이스 제공을 의미한다. 이 기능을 사용하기 위해서는 데이터 오브젝트에 키(key) 필드가 명시되어 있어야 하며 애플리케이션 설정(APPLICATION_DATAOBJECT_CACHE)에 캐시 기능을 사용할 데이터 오브젝트 팩토리 목록을 기술해야 하고, 시스템 설정(SYSTEM_DATAOBJECT_SECOND_CACHE)에 2차 캐시를 매니징할 클래스를 등록해야 한다.

  • 2차 캐시의 동작범위

    2차 캐시 인터페이스는 데이터 오브젝트 팩토리의 DB 조회 결과를 2차 캐시에 넣거나, 데이터 오브젝트 팩토리의 조회를 수행하는 경우 2차 캐시에서 데이터를 조회하는 기능을 수행한다. 테이블 UPDATE/DELETE가 발생하는 경우 캐시 무효화(invalidate) 기능도 수행한다.

  • 2차 캐시의 동작방식

    2차 캐시는 세션 캐시와 마찬가지로 데이터 오브젝트 팩토리가 생성하는 DO의 키 필드 값을 기준으로 2차 캐시에서 키에 해당하는 DO가 있는지 조회한다.

    데이터 오브젝트 팩토리의 조회를 수행할 때 먼저 세션 캐시에 조회를 시도하고, 세션 캐시에도 데이터가 없으면 2차 캐시에서 조회를 시도한다. 2차 캐시에서 데이터가 조회되면 세션 캐시에 데이터를 전파하고 사용자에게 반환한다.

    2차 캐시의 무효화(invalidate)는 세션 캐시와 연동하여 일어난다. 세션 캐시의 무효화가 발생하면 같은 DO를 캐시하고 있는 2차 캐시의 내용도 무효화한다. 또한 세션 캐시가 DIRTY가 아닌 상태에서 세션 커밋이 수행되면 세션 캐시의 내용을 2차 캐시에 전파한다.

IProObjectCacheManager

IProObjectCacheManager는 2차 캐시에 대한 접근을 위해 ProObject에서 제공하는 인터페이스 클래스다.

데이터 오브젝트 팩토리는 해당 인터페이스를 통해 캐시에 get, put, invalidate를 수행하므로 2차 캐시 제품을 데이터 오브젝트 팩토리에 적용하려면 IProObjectCacheManager를 구현한 구현체를 ProObject에 등록해야 한다.

TmaxSoft 제품인 InfiniCache와 연동을 하기 위해서는 IProObjectCacheMnager를 따로 구현할 필요 없이 system.properties의 SYSTEM_DATAOBJECT_SECOND_CACHE 값을 'com.tmax.proobject.dataobject.cache.InfiniCacheManager'로 입력하면 InfiniCache와 연동하여 캐시 기능을 데이터 오브젝트 팩토리에 적용할 수 있다.

사용자가 구현한 IProObjectCacheManager 객체는 ProObjectSecondCacheManager를 통해서만 접근할 수 있다. ProObjectSecondCacheManager는 IProObjectCacheManager의 API와 동일한 API를 제공하고 있으며, SYSTEM_DATAOBJECT_SECOND_CACHE에 지정한 IProObjectCacheManager의 구현체 클래스의 객체를 생성한다.

ProObjectSecondCacheManager를 사용하기 위해서는 다음과 같은 방법으로 객체를 얻어 온 후 API를 통해 데이터 조회나 업데이트, 추가, 삭제할 수 있다.

com.tmax.proobject.dataobject.cache.ProObjectSecondCacheManager manager = ProObjectSecondCacheManager.getInstance();

다음은 ProObjectSecondCacheManager 객체에 속한 API에 대한 설명이다.

  • get

    캐시에서 데이터를 조회하기 위해 사용한다.

    public <T> T get(String cacheName, Object key)
    파라미터설명
    String cache Name조회하려는 데이터 오브젝트 이름
    Object key캐시에서 데이터를 찾기 위한 키 값
  • put

    캐시에 데이터를 추가하기 위해 사용한다.

    public <T> void put(String cacheName, Object key, T item)

    파라미터설명
    String cache Name추가하려는 데이터 오브젝트 이름
    Object key조회를 하기 위한 캐시의 키 값
    T item추가하려는 데이터 오브젝트
  • invalidate

    입력 받은 데이터 오브젝트 명에 해당하는 모든 데이터 오브젝트를 캐시에서 초기화한다.

    public void invalidate(String cacheName) 
    파라미터설명
    String cacheName초기화하려는 데이터 오브젝트 이름
  • invalidateForTenantId

    입력받은 테넌트 아이디에 해당하는 데이터를 캐시에서 초기화한다.

    public void invalidateForTenantId(String tenantId)
    파라미터설명
    String tenantId초기화하려는 테넌트 아이디
  • invalidateAll

    캐시에 존재하는 모든 데이터를 초기화한다.

    public void invalidateAll()
  • getProductName

    IProObjectCacheManager를 통해 접근하고 있는 캐시 제품의 이름을 반환한다.

    public String getProductName()