내용 목차
본 장에서는 데이터 오브젝트와 데이터 오브젝트 팩토리의 개념과 특징, 개발 방법에 대해 설명한다.
데이터 오브젝트(DataObject, DO)는 ProObject 모듈간 데이터 전달의 기본 단위이며, 서비스 오브젝트의 입출력의 정해진 형식이다.
데이터 오브젝트 팩토리(DataObjectFactory, DOF)는 ProObject에서 DB와 FILE IO 작업을 담당하는 모듈이다. DBIO는 DB 데이터 오브젝트 팩토리를 통해 동작하고, FILEIO는 파일 데이터 오브젝트 팩토리를 통해 개발할 수 있다. 각각의 팩토리는 기반이 되는 데이터 오브젝트가 존재하며, 팩토리는 데이터 오브젝트에 1대1로 매핑하여 개발한다. 데이터 오브젝트 팩토리는 DB 데이터 오브젝트 팩토리와 파일 데이터 오브젝트 팩토리로 나뉜다.
데이터 오브젝트 및 데이터 오브젝트 팩토리와 관련된 옵션은 ProManager를 통해 아래 파일에 설정된다.
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]
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
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
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]
데이터 오브젝트(DataObject)는 ProObject 모듈간 데이터 전달의 기본 단위이며, 서비스 오브젝트의 입출력의 정해진 형식이다. 데이터 오브젝트는 ProManager를 통해 등록한 메타 데이터를 기반으로 ProStudio에서 개발할 수 있으며, 자세한 내용은 "ProObject Studio 개발자 안내서"를 참고한다.
데이터 오브젝트를 개발하기 위해서는 메타 딕셔너리에 개발자가 사용할 메타 데이터가 등록되어 있어야 한다.
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도 지원한다.
데이터 오브젝트의 개발 과정은 다음과 같다.
ProManager를 통해서 메타 딕셔너리를 구성한다.
ProStudio를 통해서 데이터 오브젝트를 생성한다.
ServiceObject, BizObject 등에 데이터 오브젝트를 사용하여 개발한다.
데이터 오브젝트는 기본적으로 멤버변수와 자신의 변수 타입에 대한 Getter/ Setter를 메소드뿐 아니라 부수적인 기능을 갖고 있다.
다음은 데이터 오브젝트의 개발에 사용되는 변수와 주요 기능에 대한 설명이다.
데이터 오브젝트는 다른 데이터 오브젝트를 자신의 부모 클래스로 지정할 수 있다. 개발된 데이터 오브젝트는 개발자가 지정한 부모 클래스를 extends한 형태로 생성된다. 이때 데이터 오브젝트의 부모 클래스로는 데이터 오브젝트 밖에 지정할 수 없으므로 다른 클래스를 부모 클래스로 지정하지 않도록 주의한다.
데이터 오브젝트는 위에서 설명한 8가지 primitive type과 그에 대한 wrapper type, java.sql type 외에도 다른 데이터 오브젝트를 멤버변수로 갖을 수 있다. 개발 방법은 메타 딕셔너리에 메타 데이터를 검색하는 것과 동일하게, ProStudio에서 데이터 오브젝트를 검색하여 메타처럼 활용하여 개발한다. 이때 Super 데이터 오브젝트와 마찬가지로 데이터 오브젝트외에 다른 클래스를 멤버변수로 선언하지 않도록 주의해야 한다.
String 타입의 멤버변수인 경우 마스크(mask)를 지정할 수 있다. 마스킹은 데이터 오브젝트를 메시지로 변환하거나, toString 등으로 멤버변수의 값을 출력할 경우 변수의 값을 마스크 값으로 치환하는 기능이다. 예를 들어 'name' 변수에 마스킹 옵션이 지정되어 있으면 데이터 오브젝트의 toString API를 호출하는 경우 'name'의 값이 '****'와 같이 표시된다.
마스크 옵션 지정은 메타를 통해서 지정되며, 마스크 옵션이 설정된 메타를 이용하여 데이터 오브젝트의 멤버변수를 선언한 경우 해당 멤버변수는 마스킹이 지정된다.
데이터 오브젝트는 ProStudio를 통해서 JSON, FixedLength, Delimiter, XML 형식의 전문을 지원하는 메시지 클래스를 생성하여 사용할 수 있다.
데이터 오브젝트는 기본적인 Java의 primitive 타입 뿐만 아니라, Parent Class 지정 및 다른 DO를 멤버변수로 선언하여 사용할 수 있다.
데이터 오브젝트의 멤버변수는 ProManager를 통해 등록한 메타 데이터를 기반으로 생성된다. 멤버변수는 private으로 선언되며, 멤버변수에 해당하는 Getter와 Setter가 같이 생성된다. 추가적으로 멤버변수마다 '~_nullable', '~_modified' 변수와 해당 필드의 Getter가 생성된다.
데이터 오브젝트를 생성하는 경우 개발자가 지정한 논리명을 갖고 있는 String 변수이다.
필드의 null 값 허용 여부를 나타내는 변수이다. 이 필드는 private으로 선언되며, Getter만 제공된다.
예를 들어 'number'라는 변수의 Java 타입이 Integer이고 null 값을 허용한다면, number_nullable 변수의 값은 true가 되며 개발자가 isNullableNumber() api를 호출하면 리턴 값은 true가 된다. 데이터 오브젝트를 사용하는 경우 사용할 멤버변수가 null 값을 허용하는지 여부를 확인할 때 사용한다.
멤버변수의 Setter 호출 여부를 나타내는 변수이다. 개발자는 isModified~() API를 통해 데이터 오브젝트의 멤버변수의 값이 변경되었는지 확인할 수 있다.
데이터 오브젝트 개발 중 멤버변수의 Setter를 호출하여 변수 값을 변경할 경우 ~_isModified 변수의 값이 true로 변경된다. ~_isModified 변수에 대한 기능은 각 멤버변수의 isModified~() 외에도 isModified()와 getIsModifiedField()를 통해 확인할 수 있다.
데이터 오브젝트의 멤버변수에 해당하는 메타 정보를 저장하고 있는 맵(Map)이다. 맵은 멤버변수명을 key로 하고, 메타 정보를 담고 있는 FieldProperty를 value로 구성된다.
FieldProperty에는 다음과 같은 정보가 담겨 있다.
데이터 오브젝트는 자신의 멤버변수에 해당하는 Getter/Setter 이외에도, 개발의 편의성을 위해 clone, equals, hashCode toString를 제공한다.
맵 데이터 오브젝트(MapDataObject)는 메타를 통한 멤버변수 선언 없이 사용하기 위한 데이터 오브젝트이다. 해당 클래스는 com.tmax.proobject.dataobject에 존재하며, com.tmax.proobject.dataobject.context.ContextDataObject를 상속받아 구현된다.
다음은 오브젝트 사용에 주의할 사항이다.
필드명은 NULL이 될수 없다.
맵 데이터 오브젝트의 변수에는 기본적인 Java 타입 값과 데이터 오브젝트만 설정할 수 있다.
메시지 타입은 com.tmax.proobject.dataobject.MapDataObjectMsgJson 클래스를 통한 JSON 형태만 지원 가능하다.
JSON 형태의 메시지를 맵 데이터 오브젝트로 Unmarshal하는 경우 Object type은 맵 데이터 오브젝트, 숫자는 Number, 문자열은 String, true, false는 Boolean, NULL로 변환하여 제공된다.
맵 데이터 오브젝트는 ContextDataObject를 상속받고 있으므로 본 절에서는 맵 데이터 오브젝트에서만 제공하는 API와 오버라이드한 API에 대해서 설명한다.
사용방법은 ContextDataObject와 동일하며 사용 가능한 API는 다음과 같다.
본 절에서는 DBIO 작업을 위한 DB 데이터 오브젝트 팩토리에 대한 개념과 개발 방법에 대해 설명한다.
DB 데이터 오브젝트 팩토리(DBDataObjectFactory)는 DBIO 작업을 수행하기 위한 모듈이다. 하나의 DB 데이터 오브젝트 팩토리에는 개발자가 지정한 C/R/U/D 쿼리가 매핑되며, 개발자는 DB 데이터 오브젝트 팩토리를 사용하는 경우 수행하려는 쿼리를 선택해야 한다. 그외에도 DB 데이터 오브젝트 팩토리는 원할한 DBIO 작업을 위해 페이징 기능과 beforeImageLog 기능을 제공한다.
DB 데이터 오브젝트 팩토리는 4가지 종류의 쿼리와 쿼리 조건 파라미터를 멤버변수로 구성한다.
DB 데이터 오브젝트 팩토리 개발 방법은 쿼리를 선택한 후 파라미터 입력하는 과정으로 진행된다.
CASE1
팩토리에 자신이 사용할 쿼리를 지정하고 쿼리 조건을 바꿔가면서 사용하는 유형이다 .
CASE2
조건이 일정한 쿼리를 지정하여 사용하는 유형이다.
CASE3
로직에서 사용자로부터 직접 쿼리를 입력받아 수행하는 유형이다.
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
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());
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));
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());
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();
본 절에서는 DB 데이터 오브젝트 팩토리를 개발하는 과정과 개발된 DB 데이터 오브젝트 팩토리를 이용하여 DBIO를 수행로직을 개발하는 방법에 대해서 설명한다.
DB 데이터 오브젝트 팩토리를 개발하기 위해서는 반드시 다음의 과정이 선행되어야 한다.
데이터소스 목록 정의
dbio_config.xml에 DB 데이터 오브젝트 팩토리에서 사용할 데이터소스 목록을 정의해야 한다.
데이터 오브젝트 생성
데이터 오브젝트 팩토리를 개발하기 위해서는 반드시 기반이 될 데이터 오브젝트가 존재해야 한다.
데이터 오브젝트 팩토리를 개발하기 위해서는 데이터 오브젝트가 선행 개발되어야 한다. 데이터 오브젝트는 흔히 쓰이는 개념인 DataTransferObject의 개념에서 확장되어 ORM의 Entity 개념을 포함하고 있다. ProObject에서는 데이터 오브젝트가 서비스 간의 input, output을 담당하며 DBIO와 FILE IO의 단위이다. DBIO로 사용하려는 데이터 오브젝트를 개발할 때는 SQL을 수행하려는 대상 테이블들의 view로 생각한다. 기본적인 데이터 오브젝트에 대한 개발 방법은 “4.3. 데이터 오브젝트”를 참고한다.
다음은 DB 데이터 오브젝트 팩토리를 이용해서 개발하는 경우 유의해야 할 사항에 대한 설명이다.
데이터 오브젝트와 DB 스키마의 연관성이 없는 형태를 뜻한다. DB 스키마 정보가 포함되지 않은 메타를 사용하여 개발된 데이터 오브젝트이다. 이런 형태의 데이터 오브젝트를 DB 데이터 오브젝트 팩토리에서 사용하기 위해서는 데이터 오브젝트의 멤버변수명(META의 Physical Name)과 쿼리에서 사용된 column 이름 또는 alias 이름이 일치해야 한다.
데이터 오브젝트와 DB 스키마가 coupling된 형태를 뜻한다. DB 스키마 정보가 포함된 메타를 사용하여 개발된 데이터 오브젝트이다. 메타의 physical name과 관계없이, 메타에 지정된 schema, table, column이 일치하는 값만 가져온다.
Column과 데이터 오브젝트의 매핑 우선순위는 다음과 같다.
데이터 오브젝트의 멤버변수명(메타의 Physical Name)과 alias 이름이 일치하는 경우
데이터 오브젝트의 멤버변수에 해당하는 컬럼명(메타의 Column Name)과 DB 컬럼 이름이 일치하는 경우
데이터 오브젝트의 멤버변수명(메타의 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를 선택할 때에 데이터 오브젝트의 멤버변수로 다른 데이터 오브젝트를 포함하는 경우는 허용하지 않으므로 주의한다.
데이터 오브젝트 선택
데이터 오브젝트 팩토리의 기반이 될 데이터 오브젝트를 선택한다. DB 데이터 오브젝트 팩토리는 선택된 데이터 오브젝트 형태로 쿼리 조회결과를 리턴하고, insert/update/delete 값을 입력받는다.
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를 데이터 오브젝트로 입력 받는다.
DB 데이터 오브젝트 팩토리를 이용한 개발은 다음의 과정으로 진행된다.
데이터소스 설정 및 객체 생성
DB 데이터 오브젝트 팩토리는 ProObject에서 제공하는 기본 DI(Dependency Injection)의 대상이 아니다. 그러므로 개발자가 직접 DI 기능을 추가해주지 않는한, DB 데이터 오브젝트 팩토리의 객체를 생성해야 한다. DB 데이터 오브젝트 팩토리는 Constructor에 데이터소스명을 직접 입력받도록 되어 있으며, 데이터소스명이 입력되지 않는 경우 ProObject.xml에 설정된 기본 데이터소스를 사용하게 된다. 사용하려는 데이터소스는 반드시 dbio_config.xml에 설정된 PairDataSource의 alias명과 일치해야 한다.
쿼리 선택
사용할 데이터소스명을 지정하여 DB 데이터 오브젝트 팩토리의 객체를 생성한 후 개발에 필요한 쿼리를 선택해야 한다.
사용할 쿼리 종류에 따라서 Full Query의 경우 setFullQuery API를 호출하고 Separated Query의 경우 setSeparatedQuery와 setCondition을 API를 호출하여 사용할 쿼리를 선택한다. 반복해서 사용할 쿼리가 아닌 일회성 쿼리의 경우 setUserQuery API를 통해 쿼리를 입력할 수 있으며, 파라미터는 setBindingParameter API를 호출해서 설정할 수 있다.
선택한 쿼리는 쿼리 수행 API들(get, getForwardList, getScrollableList, getDataObjectList, add, update, delete)이 호출된 후에는 초기화되므로 쿼리를 반복 수행할 때는 반드시 쿼리 수행 API 호출 전에 쿼리 선택 API를 호출한다.
파라미터 값 입력
기존 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로 매핑하도록 제한된다.
쿼리 수행 API 호출
실행할 쿼리를 선택하고 파라미터까지 입력한 다음에는 DBIO 작업을 수행할 API를 호출한다. DBIO용 API는 C/R/U/D별로 나누어져 있으며, SELECT는 다시 단건 조회용 다건 조회용으로 나뉜다. API에 대한 설명은 “4.4.3. DB 데이터 오브젝트 팩토리 API”를 참고한다.
DBIO API를 호출하면 DB 데이터 오브젝트 팩토리에 설정했던 쿼리와 파라미터 정보가 초기화되므로 똑같은 쿼리를 반복 수행한다고 해도 위에서 설명한 '2.쿼리 선택'과 '3.파라미터 입력'까지의 과정을 반복해야 한다.
DB 데이터 오브젝트 팩토리 사용종료 명령
ProObject에서 DB 데이터 오브젝트 팩토리를 사용하는 경우 DB 리소스에 대한 관리를 자동으로 처리한다. 그러나 반복문을 통한 DB 데이터 오브젝트 팩토리 생성 등 단시간에 DB 리소스를 지나치게 사용하는 경우 또는 사용자가 임의로 리소스를 해제하는 경우를 위해 데이터 오브젝트 팩토리는 cleanUp() API를 제공하고 있다. 데이터 오브젝트 팩토리의 cleanUp() API가 호출되면, 데이터 오브젝트 팩토리는 모든 행위를 중단하고 자신이 할당받은 리소스들을 해제한다.
다음은 DB 데이터 오브젝트 팩토리가 제공하는 API 목록이다.
C/R/U/D Operataion 수행 전 쿼리 선택
SELECT 수행
단건 조회 : get
다건 조회 : getForwardList, getScrollableList, getDataObjectList
데이터 갯수 조회 : getResultCount
INSERT, UPDATE, DELETE 수행
모두 파라미터로 데이터 오브젝트를 입력받으며 boolean 값으로 addBatch 여부를 설정할 수 있다.
페이징 기능관련
쿼리 타임아웃
데이터 페치 관련
초기화 관련
다음은 API에서 사용하는 타입에 대한 설명이다.
데이터 삽입을 수행할 때 호출하는 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()
add, update, remove API들을 통해 addBatch된 내역들에 대한 DB 반영을 취소한다. clearBatch는 사용자가 명시적으로 호출할 수도 있지만, 서비스가 실패한 경우 엔진 내부에서 호출한다.
다음은 API의 사용법에 대한 설명이다.
사용법
void clearBatch()
데이터 오브젝트 팩토리의 사용을 종료하고 데이터 오브젝트 팩토리가 사용한 모든 리소스를 반환한다. 일반적으로 서비스가 종료될 때 엔진에서 호출하는 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)
add, update, remove API들을 통해 addBatch된 내역들을 DB에 반영하는 기능을 수행한다.
executeAll은 사용자가 명시적으로 호출할 수도 있지만 다음의 경우 엔진 내부에서 호출하기도 한다.
서비스가 성공적으로 종료된 경우 트랜잭션 커밋을 수행하기 전에 엔진에서 호출한다.
get, getForwardList, getScrollableList, getDataObjectList를 통해서 데이터를 조회하기 전에 호출한다.
이전에 수행한 쿼리와 현재 수행하려는 쿼리가 다른 경우 호출한다. 메소드의 반환값으로 addBatch되었던 내역의 갯수를 사용자에게 반환한다.
다음은 API의 사용법에 대한 설명이다.
사용법
int executeAll()
다건 조회를 수행할 때 호출하는 API이다. 쿼리 수행 결과를 ArrayList에 담아서 결과를 데이터 오브젝트 하나에 매핑하여 반환한다.
다음은 API의 사용법에 대한 설명이다.
사용법
DataObject get(void)
다건 조회를 수행할 때 호출하는 API이다. 쿼리 수행 결과를 ArrayList에 담아서 반환한다. 모든 조회 결과값이 ArrayList에 담겨있기 때문에 결과 개수에 따라 memory overflow를 고려해야 한다.
다음은 API의 사용법에 대한 설명이다.
사용법
ArrayList getDataObjectList(void)
다건 조회를 수행할 때 호출하는 API이다. 쿼리 수행 결과를 ForwardList에 매핑하여 반환한다.
다음은 API의 사용법에 대한 설명이다.
사용법
ForwardList getForwardList(void)
데이터 조회 결과수를 확인하는 API이다.
get(), getForwardList(), getScrollableList(), getDataObjectList()처럼 조회 결과를 받는 것이 아니라 조회 결과의 개수를 long 값으로 반환받기 위한 API다. API 호출 전에 데이터 조회를 위해 쿼리 선택 및 조건값을 설정하고, getResultCount()를 호출한다. 사용자가 선택한 쿼리에 "SELECT COUNT(*)" 쿼리를 붙여서 사용자가 선택한 쿼리의 결과 개수를 구한다. 실제로 조회용 쿼리가 실행되는 것을 참고한다.
다음은 API의 사용법에 대한 설명이다.
사용법
long getResultCount()
다건 조회를 수행할때 호출하는 API이다. 쿼리 수행 결과를 ScrollableList에 매핑하여 반환한다.
다음은 API의 사용법에 대한 설명이다.
사용법
ScrollableList getScrollableList(void)
DB 데이터 오브젝트 팩토리의 C/R/U/D Operataion을 수행하기 전 선택한 SeparatedQuery에 대한 쿼리 조건을 설정한다. DB 데이터 오브젝트 팩토리를 개발하는 경우 지정한 FullQuery의 enum을 입력한다.
다음은 API의 사용법에 대한 설명이다.
사용법
setCondition(FULLQUERY Enum)
데이터를 조회하는 경우 조회할 데이터의 페치 크기를 설정한다. API의 파라미터값을 이용하여 Java JDBC의 PreparedStatement의 setFetchSize를 호출한다.
다음은 API의 사용법에 대한 설명이다.
사용법
void setFetchSize(int fetchSize)
DB 데이터 오브젝트 팩토리의 C/R/U/D Operataion을 수행하기 전 쿼리를 선택하기 위한 메소드이다.
DB 데이터 오브젝트 팩토리를 개발하는 경우 지정한 FullQuery의 enum을 입력한다.
다음은 API의 사용법에 대한 설명이다.
사용법
setFullQuery(FULLQUERY Enum)
DB 데이터 오브젝트 팩토리의 C/R/U/D Operataion을 수행하기 전 쿼리를 선택하기 위한 메소드이다.
DB 데이터 오브젝트 팩토리를 개발하는 경우 지정한 DynamicQueryImple의 enum을 입력한다.
다음은 API의 사용법에 대한 설명이다.
사용법
setDynamicQuery(DynamicDOF.DynamicQueryImple Enum)
페이징 기능을 이용하여 조회할 페이지 번호를 선택한다.
다음은 API의 사용법에 대한 설명이다.
사용법
void setPageNumber(int pageNumber) void setPageNumber(int pageNumber, String sortField, String sortType)
페이징 기능을 사용하여 데이터를 조회했을 때 페이지의 크기를 설정한다.
다음은 API의 사용법에 대한 설명이다.
사용법
void setPageSize(int size)
ProObject의 페이징 기능은 Oracle, Tibero 기준으로 동작한다. Oracle, Tibero 이외의 DB에서 페이징을 원하는 경우 이 API를 이용한다. 예를 들어 DB2의 페이징 기능을 이용하려면 API의 파라미터로 "LIMIT"을 설정한다.
다음은 API의 사용법에 대한 설명이다.
사용법
void setPageQuery(String pageQuery)
DB 데이터 오브젝트 팩토리의 C/R/U/D Operataion을 수행하기 전 쿼리를 선택하기 위한 메소드이다.
DB 데이터 오브젝트 팩토리를 개발하는 경우 지정한 SeparatedQuery의 enum을 입력한다. SeparatedQuery를 사용할 때 반드시 setCondition API를 통해 쿼리에 사용할 조건도 입력해야 한다.
다음은 API의 사용법에 대한 설명이다.
사용법
setSeparatedQuery(SEPARATEDQUERY Enum)
쿼리 수행시간에 제한을 설정할 수 있는 API이다.
API의 파라미터 값을 이용하여 쿼리 수행 전 Java JDBC의 PreparedStatement에 setQueryTimeout을 호출한다. 기본값은 데이터 오브젝트 팩토리의 쿼리가 수행되기 전 남아있는 서비스 타임아웃 시간이다.
다음은 API의 사용법에 대한 설명이다.
사용법
void setTimeout(int timeout)
ProManager를 통해 설정했던 데이터 오브젝트 팩토리의 설정들을 재설정한다.
다음은 API의 사용법에 대한 설명이다.
사용법
void reloadDataObjectConfig()
데이터 삭제를 수행할 때 호출하는 API이다.
데이터 삭제는 삽입과 수정 API와 동일하게 삭제하려는 조건 값을 데이터 오브젝트로 입력받을 수도 있고, 조건 값을 DB 데이터 오브젝트 팩토리에 바로 설정할 수도 있다.
다음은 API의 사용법에 대한 설명이다.
사용법 1)
파라미터 값에 따라 Java JDBC의 addBatch를 수행한다.
int remove(boolean immediate)
사용법 2)
userQuery를 사용하는 경우 데이터를 삽입하기 위한 API로 Java JDBC의 addBatch를 수행하지 않고, 쿼리 결과를 DB에 반영한다.
int remove()
데이터 수정을 수행할 때 호출하는 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()
데이터 오브젝트 팩토리의 BeforeImage 기능은 데이터 오브젝트 팩토리에 매핑한 C/U/D 쿼리를 수행하여 테이블에 변경이 일어나면 변경되기 전의 로우 정보(BeforeImage)와 변경된 로우 정보(AfterImage)를 별도의 테이블에 기록한다. Insert문 수행시 추가된 로우 정보를 기록하고, Delete문 수행시 삭제된 로우 정보를, Update문의 수행시 변경 전과 변경 후의 로우 정보를 모두 기록한다.
이렇게 기록된 정보는 추후 사용자가 특정 시점으로 테이블을 되돌리려고 할 경우 활용될 수 있다.
BeforeImage 테이블 생성
BeforeImage 테이블은 데이터 오브젝트 팩토리에 매핑된 C/U/D 쿼리가 수행되는 테이블명에 "_BF_IMG"가 뒤에 붙은 이름을 가져야 한다. 예를 들어 EMP 테이블이 원장 테이블인 경우 BeforeImage 테이블명은 EMP_BF_IMG이다.
테이블의 컬럼들은 원장 테이블의 모든 컬럼을 필수적이며 다음과 같은 컬럼을 추가로 가진다.
컬럼명 & 데이터타입 | 데이터타입 | 설명 |
---|---|---|
PO_OP_TYPE | VARCHAR(2) |
|
PO_OP_SEQ | NUMBER |
|
PO_OP_TIME | TIMESTAMP |
|
PO_OP_FLAGDEFAULT | VARCHAR(1) |
|
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"가 뒤에 붙은 이름을 가져야 한다.
데이터 오브젝트 팩토리 개발
BeforeImage 기능을 이용하려면 데이터 오브젝트 팩토리를 개발하는 경우 BeforeImage Option을 활성화시켜야 한다.
서비스 설정
BeforeImage 기능을 활성화한 데이터 오브젝트 팩토리를 서비스에서 사용하려면 서비스의 런타임 설정에서 SERVICE_{SERVICE_NAME}_BEFORE_IMAGE_ENABLE 값을 true로 설정해야 한다. 이 설정을 생략하면 BeforeImage 기능을 활성화한 데이터 오브젝트 팩토리를 사용해도 BeforeImage 기능을 이용할 수 없다.
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);
쿼리 오브젝트(QueryObject)는 사용자가 데이터 오브젝트 매퍼에서 사용할 SQL 문을 들고 있는 객체이다. 쿼리 오브젝트의 개발은 스튜디오를 통해 개발하며, 확장자는 .qo이다. 사용자는 사용할 SQL 문과 해당 SQL 문과 매핑할 데이터 오브젝트를 지정한다.
본 절에서는 쿼리 오브젝트를 개발하는 과정에 대해서 설명한다.
쿼리 오브젝트를 개발하기 위해서는 반드시 데이터 오브젝트 생성 과정이 선행되어야 한다.
쿼리 오브젝트를 개발하기 위해서는 데이터 오브젝트가 선행 개발되어야 한다. 데이터 오브젝트는 흔히 쓰이는 개념인 DataTransferObject의 개념에서 확장되어 ORM의 Entity 개념을 포함하고 있다. ProObject에서는 데이터 오브젝트가 서비스 간의 input, output을 담당하며 DBIO와 FILE IO의 단위이다. DBIO로 사용하려는 데이터 오브젝트를 개발할 때는 SQL을 수행하려는 대상 테이블들의 view로 생각한다. 기본적인 데이터 오브젝트에 대한 개발 방법은 “4.3. 데이터 오브젝트”를 참고한다.
데이터 오브젝트의 멤버변수로 다른 데이터 오브젝트를 포함하는 경우는 허용하지 않으므로 주의한다.
쿼리 오브젝트 자체는 개발자가 입력한 쿼리의 정보만을 갖고 있는 객체이다. 그러므로 별도의 수행로직은 없으며 개발자가 쿼리에 대한 정보를 필요한 경우 쿼리 오브젝트 API를 이용하여 원하는 정보를 얻을수 있다.
SQL 입력
쿼리 오브젝트 자체는 개발자가 입력한 쿼리의 정보만을 갖고 있는 객체이다. 그러므로 별도의 수행로직은 없으며 개발자가 쿼리에 대한 정보를 필요한 경우 쿼리 오브젝트 API를 이용하여 원하는 정보를 얻을수 있다. 쿼리 오브젝트에 데이터 오브젝트 매퍼에서 사용할 쿼리를 입력한다. 쿼리는 여러 개를 입력가능하며, SO/BO에서 데이터 오브젝트 매퍼를 사용하는 경우 동작할 쿼리를 선택할 수 있다.
쿼리 오브젝트는 사용할 SQL을 미리 입력하고, SO나 BO에서 데이터 오브젝트 매퍼를 통해 사용할 SQL을 호출한다. 쿼리 오브젝트에 사용할 수 있는 SQL의 개수는 제한이 없으나, 동일한 내용의 SQL을 중복해서 입력할 수 없다. 개발자는 C/R/U/D 타입별로 자신이 사용할 SQL을 쿼리 오브젝트에 입력할 수 있으며, 반드시 SQL에 해당하는 SQL ALIAS를 입력해야 한다. SQL ALIAS는 데이터 오브젝트 매퍼를 통해 개발자가 사용할 SQL을 선택할 때 SQL을 구분하는 단위가 되므로 반드시 사용자가 이해할 수 있는 의미를 담아 지정하도록 한다.
데이터 오브젝트 선택
데이터 오브젝트 팩토리의 경우 사용자가 지정한 모든 쿼리는 Target DO 한가지 타입에 맞춰서 매핑된다. 하지만 쿼리 오브젝트는 쿼리당 매핑할 데이터 오브젝트를 지정할 수 있다. 지정된 데이터 오브젝트 타입은 데이터 오브젝트 매퍼가 쿼리와 매핑하는 기본 데이터 오브젝트가 된다.
다음은 쿼리 오브젝트가 제공하는 API 목록이다.
쿼리 타입 반환
쿼리와 매핑되는 기본 데이터 오브젝트 타입 반환
쿼리에서 사용하는 테이블 목록
쿼리의 파라미터 정보 반환
다이나믹 쿼리 여부 확인
현재 선택된 쿼리 오브젝트의 쿼리에서 사용되는 파라미터들의 정보를 반환하는 API이다.
쿼리에서 사용된 파라미터 정보를 java.util.Map으로 반환한다. Map의 Key는 파라미터명이며, Value는 파라미터 정보를 담고있는 com.tmax.proobject.dataobject.model.Parameter 객체이다. Parameter 객체는 파라미터의 이름, Class type, 파라미터 인덱스(index), 파라미터의 속성(조건, 값, 다이나믹 파라미터 여부)을 담고 있다.
다음은 API의 사용법에 대한 설명이다.
사용법
public Map<String, Parameter> getParameterInfo()
현재 선택된 쿼리 오브젝트의 쿼리 종류를 판단하는 API이다. 데이터 오브젝트 매퍼에서 해당 API를 호출하여 쿼리와 매핑된 데이터 오브젝트 타입을 사용한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public Class<? extends DataObject> getResultType()
현재 선택된 쿼리 오브젝트의 쿼리가 사용하는 테이블 목록을 반환하는 API이다.
다음은 API의 사용법에 대한 설명이다.
사용법
public List<String> getTableList()
현재 선택된 쿼리 오브젝트의 쿼리 종류를 판단하는 API이다.
API를 호출하는 경우 com.tmax.proobject.dataobject.factory.DBType이 반환되며, 해당 클래스는 enum 클래스로 SELECT, INSERT, UPDATE, DELETE 값을 반환한다.
다음은 API의 사용법에 대한 설명이다.
사용법
DBType getType()
데이터 오브젝트 매퍼(DataObjectMapper)는 사용자가 개발한 쿼리 오브젝트의 쿼리를 실행하는 엔진 모듈이다. 데이터 오브젝트 팩토리가 개발할 때 지정된 쿼리만을 사용해야 한다면, 데이터 오브젝트 매퍼는 개발된 모든 쿼리 오브젝트의 쿼리들을 실행시킬 수 있다.
데이터 오브젝트 팩토리와 마찬가지로 DB 조회 결과는 데이터 오브젝트로 반환되며, 기본은 쿼리 오브젝트 개발 당시 지정된 쿼리의 반환 타입을 사용한다.
본 절에서는 데이터 오브젝트를 개발하는 과정에 대해서 설명한다.
데이터 오브젝트 매퍼를 사용하여 개발하기 위해서는 반드시 다음의 과정이 선행되어야 한다.
데이터 오브젝트 생성
데이터 오브젝트 매퍼로 개발하기 위해서는 반드시 데이터 오브젝트가 선행 개발되어야 한다. 데이터 오브젝트 매퍼는 DB 조회 결과를 데이터 오브젝트 형태로 사용자에게 반환하고, 데이터 오브젝트의 데이터들을 DB에 추가, 수정하기 때문이다. 기본적인 데이터 오브젝트에 대한 개발 방법은 “4.3. 데이터 오브젝트”를 참고한다.
데이터 오브젝트의 멤버변수로 다른 데이터 오브젝트를 포함하는 경우는 허용하지 않으므로 주의한다.
쿼리 오브젝트 생성
데이터 오브젝트 매퍼로 개발을 하기 위해서는 반드시 쿼리 오브젝트가 선행 개발되어야 한다. 데이터 오브젝트 매퍼는 쿼리 오브젝트에 존재하는 쿼리들을 실행시키는 것이므로 쿼리 오브젝트가 선행 개발되어 있지 않다면 쿼리를 수행할 수가 없기 때문이다. 기본적인 쿼리 오브젝트에 대한 개발 방법은 “4.6. 쿼리 오브젝트”를 참고한다.
데이터 오브젝트 매퍼를 이용한 개발은 다음의 과정으로 진행된다.
데이터소스 설정 및 객체 생성
데이터 오브젝트 매퍼는 ProObject에서 제공하는 기본 DI(Dependency Injection)의 대상이 아니다. 그러므로 개발자가 직접 DI 기능을 추가하고 데이터 오브젝트 매퍼의 객체를 생성해야 한다.
데이터 오브젝트 매퍼는 Constructor에 데이터소스명을 직접 입력받도록 되어 있으며, 데이터소스명이 입력되지 않는 경우 ProObject.xml에 설정된 기본 데이터소스를 사용한다. 사용하려는 데이터소스는 반드시 dbio_config.xml에 설정된 PairDataSource의 alias명과 일치해야 한다.
또한 데이터 오브젝트 매퍼의 객체를 생성할 때에 매퍼 객체가 사용할 데이터 오브젝트 타입을 Generic 타입으로 명시해줘야 한다. 데이터 오브젝트 매퍼는 객체를 생성할 때 입력받은 Generic 타입으로 DB 조회, 삽입, 수정 작업을 수행한다.
// ds1이라는 데이터소스를 사용하고, Emp를 반환 타입으로 갖는 데이터 오브젝트 매퍼 객체 생성 DataObjectMapper<Emp> mapper = new DataObjectMapper("ds1");
쿼리 선택
사용할 데이터소스명을 지정하여 DB 데이터 오브젝트 팩토리의 객체를 생성했다면 개발에 필요한 쿼리를 선택해야 한다.
데이터 오브젝트 매퍼는 쿼리 오브젝트의 enum 객체를 쿼리로 입력받는다. 데이터 오브젝트 매퍼의 setQuery API를 이용하여 데이터 오브젝트 매퍼가 사용할 쿼리를 설정해야 한다 . 선택한 쿼리는 쿼리 수행 API들(get, getForwardList, getScrollableList, getDataObjectList, add, update, delete)가 호출된 후에는 초기화되므로 쿼리를 반복 수행할 때는 반드시 쿼리 수행 API 호출 전에 쿼리 선택 API를 호출한다.
// 쿼리 오브젝트 BizAQuery의 SELECT1 쿼리 사용 mapper.setQuery(BizAQuery.SELECT1);
파라미터 입력
기존 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로 매핑하도록 제한된다.
쿼리 수행 API 호출
실행할 쿼리를 선택하고 파라미터까지 입력한 다음에는 DBIO 작업을 수행할 API를 호출한다. DBIO용 API는 C/R/U/D별로 나누어져 있으며, SELECT는 다시 단건 조회용 다건 조회용으로 나뉜다.
DBIO API를 호출하면 DB 데이터 오브젝트 매퍼에 설정했던 쿼리와 파라미터 정보가 초기화되므로 똑같은 쿼리를 반복수행한다고 해도 위에서 설명한 '2.쿼리 선택'과 '3.파라미터 입력'까지의 과정을 반복해야 한다.
다음은 데이터 오브젝트 매퍼가 제공하는 API 목록이다.
C/R/U/D Operataion 수행 전 쿼리 선택
SELECT 수행
단건조회 : get
데이터 갯수 조회 : getResultCount
INSERT, UPDATE, DELETE 수행
페이징 기능관련
쿼리 타임아웃
데이터 페치 관련
초기화 관련
데이터 삽입을 수행할 때 호출하는 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)
데이터 오브젝트 팩토리의 사용을 종료하고 데이터 오브젝트 팩토리가 사용한 모든 리소스를 반환한다. 일반적으로 서비스가 종료될 때 엔진에서 호출하는 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)
add, update, remove API들을 통해 addBatch된 내역들에 대한 DB 반영을 취소한다. clearBatch는 사용자 가 명시적으로 호출할 수도 있지만, 서비스가 실패한 경우 엔진 내부에서 호출한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public void clearBatch()
add, update, remove API들을 통해 addBatch된 내역들을 DB에 반영하는 기능을 수행한다.
executeAll은 사용자가 명시적으로 호출할 수도 있지만 다음의 경우 엔진 내부에서 호출하기도 한다.
서비스가 성공적으로 종료된 경우 트랜잭션 커밋을 수행하기 전에 엔진에서 호출한다.
get, getForwardList, getScrollableList, getDataObjectList를 통해서 데이터를 조회하기 전에 호출한다.
이전에 수행한 쿼리와 현재 수행하려는 쿼리가 다른 경우 호출한다. 메소드의 반환값으로 addBatch되었던 내역의 갯수를 사용자에게 반환한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public int executeAll()
데이터 오브젝트 매퍼를 이용하여 단건 조회를 수행할때 호출하는 API이다.
호출하는 경우 데이터 조회 결과를 데이터 오브젝트 매퍼 객체 생성당시 Generic으로 지정한 타입으로 반환한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public T get()
데이터 오브젝트 매퍼를 이용하여 다건 조회를 수행할때 호출하는 API이다.
쿼리 수행 결과를 ArrayList에 담아서 반환한다. 모든 조회 결과 값이 ArrayList에 담겨있기 때문에 결과 개수에 따라 memory overflow를 고려해야 한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public ArrayList<T> getDataObjectList()
데이터 오브젝트 매퍼를 이용하여 다건 조회를 수행할 때 호출하는 API이다. 조회 결과를 ForwardList에 매핑하여 반환한다. forward-only 조회를 수행한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public List<T> getForwardList()
데이터 조회 결과수를 확인하는 API이다.
get(), getForwardList(), getScrollableList(), getDataObjectList()처럼 조회 결과를 받는 것이 아니라 조회 결과의 개수를 long 값으로 반환받기 위한 API다. API 호출 전에 데이터 조회를 위해 쿼리 선택 및 조건값 을 설정하고, getResultCount()를 호출한다. 사용자가 선택한 쿼리에 "SELECT COUNT(*)" 쿼리를 붙여서 사용자가 선택한 쿼리의 결과 개수를 구한다. 실제로 조회용 쿼리가 실행되는 것을 참고한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public long getResultCount()
데이터 오브젝트 매퍼를 이용하여 다건 조회를 수행할때 호출하는 API이다. 조회 결과를 ScrollableList에 매핑하여 반환한다. scroll-insensitive 조회를 수행한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public ScrollableList<T> getScrollableList()
ProManager를 통해 설정했던 데이터 오브젝트 팩토리의 설정들을 재설정한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public void reloadDataObjectConfig()
데이터 삭제를 수행할 때 호출하는 API이다.
다음은 API의 사용법에 대한 설명이다.
사용법 1)
파라미터 값에 따라 Java JDBC의 addBatch를 수행한다.
public int remove(boolean immediate)
사용법 2)
addBatch를 기본으로 수행하는 API이다. 사용법1)에서 immediate 값을 false로 준것과 동일한 기능을 수행한다.
public void remove()
데이터를 조회하는 경우 조회할 데이터의 페치 크기를 설정한다. API의 파라미터값을 이용하여 JavaJDBC 의 PreparedStatement의 setFetchSize를 호출한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public void setFetchSize(int fetchSize)
페이징 기능을 이용하여 조회할 페이지 번호를 선택한다.
다음은 API의 사용법에 대한 설명이다.
사용법 1)
public void setPageNumber(int pageNumber)
사용법 2)
페이징을 수행할 때 특정 필드로 정렬이 필요한 경우 파라미터를 통해서 정렬방법(ASC, DESC)과 정렬의 기준이될 필드를 선택할 수 있다. 기본 정렬 타입은 'ASC'이다.
public void setPageNumber(int pageNumber, String sortField, String sortType)
ProObject의 페이징 기능은 Oracle, Tibero 기준으로 동작한다. Oracle, Tibero 이외의 DB에서 페이징을 원하는 경우 이 API를 이용한다. 예를 들어 DB2의 페이징 기능을 이용하려면 API의 파라미터로 "LIMIT"을 설정한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public void setPageQuery(String pageQuery)
페이징 기능을 사용하여 데이터를 조회했을 때 페이지의 크기를 설정한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public void setPageSize(int size)
데이터 오브젝트 매퍼가 사용할 쿼리를 선택하는 API이다.
다음은 API의 사용법에 대한 설명이다.
사용법 1)
API의 파라미터로 쿼리 오브젝트의 쿼리를 넘겨준다.
public DataObjectMapper<T> setQuery(QueryInfo query)
사용법 2)
쿼리 오브젝트에 지정된 반환 타입과 데이터 오브젝트 매퍼의 Generic 타입이 다른 경우 setQuery API에 데이터 오브젝트 매퍼에 지정한 Generic 타입을 넘겨줘야 한다.
public DataObjectMapper<T> setQuery(QueryInfo query, Class<T> returnType)
쿼리 수행시간에 제한을 설정할 수 있는 API이다.
API의 파라미터 값을 이용하여 쿼리 수행 전 Java JDBC의 PreparedStatement에 setQueryTimeout을 호출한다. 기본값은 데이터 오브젝트 팩토리의 쿼리가 수행되기전 남아 있는 서비스 타임아웃 시간이다.
다음은 API의 사용법에 대한 설명이다.
사용법
public void setTimeout(int timeout)
데이터 수정을 수행할 때 호출하는 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)
본 절에서는 File IO를 수행하는 파일 데이터 오브젝트 팩토리에 대한 개념과 개발방법에 대해 설명한다.
파일 데이터 오브젝트 팩토리는 Fixed Length, Delimiter, XML 3가지 방식으로 파일 읽기, 쓰기 작업을 수행하는 객체다.
파일 데이터 오브젝트 팩토리는 사용자가 지정한 파일에 대해서 라인 단위로 데이터를 읽고 쓸 수 있며, 읽어들인 데이터는 데이터 오브젝트로 객체 매핑이 되고, 데이터 오브젝트 객체를 입력받아 해당 객체의 데이터를 파일에 쓸 수 있다.
파일 데이터 오브젝트 팩토리는 헤더와 테일 기능을 이용하여, 파일의 처음과 마지막에 사용자가 원하는 서식으로 내용을 추가할 수 있는 기능을 제공한다. ProStudio를 통해 바디(body)가 될 파일 데이터 오브젝트 팩토리에 헤더와 테일이 될 파일 데이터 오브젝트 팩토리를 지정한다. 바디 파일 데이터 오브젝트 팩토리에 getHeader()와 getTail() API를 이용하여 헤더와 테일 객체를 얻어올 수 있다.
본 절에서는 파일 데이터 오브젝트 팩토리를 개발하는 과정에 대해서 설명한다.
파일 데이터 오브젝트 팩토리를 개발하기 위해서는 반드시 선행되어야 하는 절차가 필요하다.
데이터 오브젝트 생성
데이터 오브젝트 팩토리를 개발하기 위해서는 반드시 기반이 될 데이터 오브젝트가 존재해야 한다.
데이터 오브젝트 선택
데이터 오브젝트 팩토리의 기반이 될 데이터 오브젝트를 선택한다. 파일 데이터 오브젝트 팩토리는 선택된 데이터 오브젝트 형태로 파일 내용을 읽고, 데이터 오브젝트의 내용을 파일의 라인 하나로 기록한다.
파일 데이터 오브젝트 객체 생성
파일 데이터 오브젝트 객체의 Constructor에 파일 경로를 입력하여 데이터 오브젝트 팩토리 객체를 생성한다.
기능 수행
get, add API를 이용하여 파일의 내용을 읽고 쓰는 동작을 수행한다. DB 데이터 오브젝트 팩토리에서 제공하는 update, delete는 파일 데이터 오브젝트 팩토리에서 제공되지 않는다.
본 절에서는 실제 파일 데이터 오브젝트의 활용 샘플 코드를 통해서 파일 데이터 오브젝트 팩토리를 사용하는 방법에 대해서 설명한다.
파일 데이터 오브젝트를 이용한 개발은 다음의 과정으로 진행된다.
파일 데이터 오브젝트 팩토리 객체 생성 및 파일 설정
//* 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);
파일 데이터 오브젝트 팩토리 압축 파일 설정
//* 압축 파일 설정 factory.applyZipFile(true);
파일 데이터 오브젝트의 I/O 대상이 바이너리 파일인 경우 설정
//* 바이너리 파일 설정 factory.applyBinaryFile(true);
파일 데이터 오브젝트 팩토리 인코딩 캐릭터 셋 설정
factory.setCharSet("UTF-8"); //* 혹은 factory.setCharSet("EUC-KR");
파일 데이터 오브젝트 팩토리 계행문자 설정
//* \n factory.setNewLine(true); // or factory.setNewLineLength(1); //* \r\n factory.setNewLine(false); // or factory.setNewLineLength(2);
파일 데이터 오브젝트 팩토리 파일 읽기
SampleDataObject output = factory.get(); System.out.println(output);
파일 데이터 오브젝트 팩토리 여러 행 읽기
//* 파일의 처음부터 끝까지 루프를 돌면서 한 라인씩 읽는다. ForwardList>SampleDataObject< output = factory.getForwardList(); for(SampleDataObject item : output) { System.out.println(item); }
파일 데이터 오브젝트 팩토리 객체 파일 쓰기
SampleDataObject item = new SampleDataObject(); item.setEmpno(1); item.setEname("Jhon"); item.setSal(100); item.setDeptno(10); factory.add(item); //* 명시적으로 버퍼를 비우고 싶다면 factory.flush();
파일 데이터 오브젝트 팩토리 헤더 파일 읽기
SampleHeaderItem headerItem = factory.getHeader().get(); System.out.println(headerItem);
파일 데이터 오브젝트 팩토리 헤더 파일 쓰기
SampleHeaderItem headerItem = new SampleHeaderItem(); headerItem.setMgr(10); headerItem.setHireDate("2018-03-31"); factory.getHeader().add(headerItem);
파일 데이터 오브젝트 팩토리 테일 파일 읽기
SampleTailItem tailItem = factory.getTail().get(); System.out.println(tailItem);
파일 데이터 오브젝트 팩토리 테일 파일 쓰기
SampleTailItem tailItem = new SampleTailItem(); tailItem.setDname("lab1"); tailItem.setPhoneNumber("010-0000-0000"); factory.getTail().add(tailItem);
파일 데이터 오브젝트 팩토리를 개발하기 위해서 다음의 API를 제공한다.
입력받은 데이터 오브젝트의 데이터들을 파일 데이터 오브젝트의 파일 형식에 따라 한 라인으로 파일에 기록한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public void add(DataObject)
FILE I/O의 대상이 바이너리 파일인지 텍스트 파일인지 선택하기 위한 API이다.
기본적으로 FILE DOF는 setFile로 입력받은 파일은 텍스트 파일로 취급한다. FILE I/O 대상이 바이너리 파일이면 파라미터로 'true'를 텍스트 파일이면 파라미터로 'false'를 전달한다.
다음은 API의 사용법에 대한 설명이다.
사용법
void applyBinaryFile(boolean isBinary)
FILE I/O의 대상이 압축파일인 경우를 설정하기 위한 API이다. 기본적으로 FILE DOF는 setFile로 입력받은 파일은 일반 파일로 취급한다. FILE I/O 대상이 압축파일인 경우 파라미터로 'true'를 전달한다.
다음은 API의 사용법에 대한 설명이다.
사용법
void applyZipFile(boolean isZipFile)
파일 데이터 오브젝트 팩토리가 사용한 파일 리소스와 팩토리의 리소스들을 모두 해제한다. 헤더와 테일이 존재할 경우 헤더 테일의 리소스도 해제한다. 일반적으로 서비스를 종료하는 경우 자동으로 호출되는 메소드이다.
다음은 API의 사용법에 대한 설명이다.
사용법
public void cleanUp(void)
파일 입력 작업 중 버퍼에 남은 내용을 파일에 기록한다. ProObject에서는 서비스를 종료하는 경우 자동으로 호출되는 함수이다.
다음은 API의 사용법에 대한 설명이다.
사용법
public void flush(void)
파일의 내용 하나를 읽어서 데이터 오브젝트로 반환받는다. 파일의 내용은 데이터 오브젝트의 각 멤버변수에 저장된다.
다음은 API의 사용법에 대한 설명이다.
사용법
public DataObject get(void)
현재 파일 데이터 오브젝트 팩토리가 사용하는 인코딩 캐릭터 셋을 반환한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public String getCharSet(void)
현재 파일 데이터 오브젝트 팩토리의 입출력 대상이되는 파일 객체를 반환한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public File getFile(void)
DB 데이터 오브젝트의 getForwardList와 동일한 기능을 수행한다. Iterator, for-each를 수행하면서 파일의 내용을 순차적으로 읽어서 데이터 오브젝트로 반환한다. 또는 ForwardList의 get() API를 호출하는 경우 파일의 한 라인을 데이터 오브젝트로 반환한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public ForwardList<DataObject> getForwardList(void)
현재 파일 데이터 오브젝트 팩토리의 헤더로 지정된 파일 데이터 오브젝트 팩토리를 반환한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public FileDataObjectFactory getHeader(void)
현재 파일 데이터 오브젝트 팩토리의 테일로 지정된 파일 데이터 오브젝트 팩토리를 반환한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public FileDataObjectFactory getTail(void)
현재 FILE DOF의 FILE I/O 대상이 바이너리 파일인지 텍스트 파일인지를 반환한다. 바이너리 파일인 경우 'true'를 반환하고, 텍스트 파일인 경우 'false'를 반환한다.
다음은 API의 사용법에 대한 설명이다.
사용법
boolean isBinaryFile()
현재 FILE DOF의 FILE I/O 대상이 압축 파일인지 여부를 반환한다. 압축 파일인 경우 'true'를 반환하고, 일반 파일인경우 'false'를 반환한다.
다음은 API의 사용법에 대한 설명이다.
사용법
boolean isZipFile()
파일 데이터 오브젝트 팩토리에서 사용할 인코딩 캐릭터 셋을 설정한다.
입력값은 String으로 전달하며 'UTF-8'과 'EUC-KR' 둘 중 하나로 지정할 수 있다. 파일 데이터 오브젝트 팩토리에 헤더와 테일이 존재하면, 인코딩 캐릭터 셋을 동일하게 맞춘다.
다음은 API의 사용법에 대한 설명이다.
사용법
public void setCharSet(String)
파일 데이터 오브젝트가 파일 입출력을 수행해야 하는 대상을 파일 객체로 전달한다.
setFile API 호출을 하면 이전에 호출하였던 applyBinaryFile, applyZipFile로 지정했던 파일 형태가 초기화되므로 참고하도록한다. setFile 호출 이후 바이너리 파일이나 압축 파일에 대한 설정을 추가해야 한다.
다음은 API의 사용법에 대한 설명이다.
사용법 1)
파일 데이터 오브젝트 팩토리를 생성하는 경우 Constructor에 파일 경로를 입력한 경우 팩토리 내부에서 자동으로 호출되는 메소드이다. 이 메소드는 기존 파일 내용에 이어서 입출력 작업을 하는 동작을 위해 호출된다.
public void setFile(File)
사용법 2)
기본 기능은 setFile(File)과 동일하지만 boolean 값에 따라 파일 입출력을 이어서 하거나 새로 할 수 있다. boolean 값이 true인 경우 파일 입출력을 이어서 하고, false인 경우 파일을 입력할 때 새로운 파일을 만들어서 사용한다.
public void setFile(File, boolean)
파일 라인 변경 문자를 지정하기 위한 메소드이다. 파라미터가 true인 경우 '\n'을, false인 경우 '\r\n'을 계행문자로 지정한다.
다음은 API의 사용법에 대한 설명이다.
사용법
public void setNewLine(boolean)
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을 호출한것과 동일한 기능을 수행한다.
비동기 데이터 오브젝트를 위해 execute API를 제공한다.
데이터 조회를 비동기 수행하기 위한 메소드이다.
사용법
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 | 수행할 쿼리가 선택된 데이터 오브젝트 팩토리이다. |
dataobject | INSERT, UPDATE 쿼리를 수행하는 경우 파라미터로 전달할 데이터 오브젝트 객체이다. |
type | DOF가 수행할 타입을 선택한다.
|
serviceMethodNameOnResponse | DB 작업을 수행한 후 응답을 처리할 메소드명이다. |
serviceMethodNameOnError | 비동기 작업 중 예외가 발생한 경우 처리할 메소드명이다. |
AsyncDO의 execute api 호출하는 경우 serviceMethodNameOnResponse 값을 입력했다면, DB I/O 작업이 끝난 후 사용자가 지정한 메소드로 서비스에 재진입하게 된다. 이때 사용자는 반드시 메소드의 파라미터로 com.tmax.proobject.engine.system.dto.async.AsyncDBOperationResult를 지정해야 한다.
AsyncDBOperationResult에는 사용자의 쿼리 조회 결과에 대한 DataObject 목록이나 쿼리수행 결과 반영된 row count 수가 들어 있다.
멤버변수
멤버변수 | 설명 |
---|---|
resultList | 데이터 오브젝트를 담고 있는 List 객체이다. SELECT를 수행한 경우 결과 값이 List에 담겨 있다. |
resultCnt | 쿼리 수행 결과가 담겨 있다.
|
다음은 AsyncDO를 사용하는 과정에 대한 설명이다.
이벤트 서비스 생성
데이터 오브젝트를 비동기식으로 사용하기 위해서는 이벤트 서비스를 생성해야 한다.
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; } }
데이터 조회
다음은 비동기로 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; } }
데이터 추가
다음은 비동기로 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; } }
데이터 수정
다음은 비동기로 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; } }
데이터 삭제
다음은 비동기로 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; } }
ProObject는 데이터 오브젝트 팩토리의 DB 작업을 수행하는데 필요한 시간을 줄임으로써, 좀더 나은 성능 향상을 사용자에게 제공하기 위해 세션 레이어의 캐시 기능을 제공한다. 캐시는 데이터 오브젝트의 키(key) 필드 값을 기준으로 데이터 오브젝트 단위로 캐싱을 한다. 그러므로 키 필드가 없는 경우 캐시 기능은 동작하지 않으므로 주의한다.
또한 system.properties에 SYSTEM_DATAOBJECT_CACHE_ENABLE 항목의 값이 true로 설정되어 있어야 캐시 기능이 동작한다. 해당 옵션이 true에서 false로 변경되면 cache invalidate를 수행한다.
본 절에서는 ProObject에서 캐시를 사용하기 위한 환경 설정과 동작 범위, 방식에 대해서 설명한다.
ProObject에서 캐시 기능을 사용하기 위해서는 데이터 오브젝트에 키(key) 속성이 있는 필드가 존재해야 한다. key 속성은 ProManager를 통해 메타에 추가할 수 있다. 이때 지정하는 키는 테이블의 키가 아닌 ProObject가 캐싱을 데이터 오브젝트의 기준으로 ProManager의 메타에서 키 속성을 지정한 필드가 기준이 된다.
ProStudio를 통해서 DO를 생성하는 경우 반드시 키 속성을 지닌 메타를 사용하여 DO를 생성해야 한다. 키는 DO가 대응하는 테이블의 실제 키 컬럼에 해당하는 메타를 사용해도 되고, 사용자 임의의 키 필드를 선택해도 가능하다. 두 개 이상의 키 필드를 지정할 수도 있다.
세션 캐시는 트랜잭션 단위의 캐시로 한 서비스 내에서의 캐시를 보장한다. 이 기능을 사용하기 위해서는 데이터 오브젝트에 키(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)한다.
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는 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()