요즘 추세가 DB 프로시져를 사용하지 않고 자바에서 비즈니스 로직을 처리한다.
복잡한 테이블 관계가 얽혀 있는 업무라면 많은 제약이 따른다.
헤더와 라인 테이블이 있다.
헤더 테이블 1,000개의 row 데이터를 조회하면서, 실시간으로 어떤 집계 작업을 row 단위로 해서 금액을 만들어 보여줘야 한다.
라인테이블에 금액이 존재하는데 약간의 계산이 필요하다. 기준정보 테이블 3~4개 정도 뒤진다.
자바 메모리 자체에 데이터를 들고 있지 않는 이상. database와 통신을 여러 번 하면서 1,000번을 돌려야 하기에 이런 경우는 DB 프로시져나 펑션을 가미해서 만든 로직의 속도를 자바로 만든 프로그램이 이길 수가 없다.
어떤 작은 프로젝트에 참여하게 됐는데, ASIS 서비스에서는 DB프로시져로 되어 있는 로직을 TOBE에서는 자바로 변경해 달라는 요청이 있었다.
평소 500 개의 데이터가 조회가 되는데 500개 row가 조회되면서 실시간 집계 로직이돌아서 컬럼을 추가하는 조회 프로그램이다. 기존 ASIS 시스템에서는 3초 내로 조회가 되는데 이것을 TOBE 시스템에서 자바로 바꾸니 4분이 걸린다.
계산에 필요한 컬럼만 명시하고 최대한 자바에서 DB select를 최소화해도 500 * 3 정도의 mybatis access를 하니 빠를 수가 없다.
단순히 Sub Query로만 만들 수 있는 수준이 아니었다.
SM 인원이나 IT 관리자들에게 ASIS 프로그램이 관리상으로도 문제없고, PROCEDURE 소스가 몇개 안되니 굳이 DB 프로그램을 자바로 옮겨야 하는지 물었지만, 명확하게 목적을 모르는 듯했다. 그냥 그렇게 하기로 했고 사내 다른 프로젝트들도 모두 그렇게 하기로 전사표준으로 잡았으니 그렇게 해야 한단다.
결국 기존 사용자들의 반발이 예상되어 DELCARE로 Mybatis에 계산로직을 넣어서 실행하게 했다. 웃긴 건 이것은 DB Object가 아니니 가능하다고 한다.
이런...... 할 말이 없다.... 뭐 소스가 SVN에 올라가니 형상관리가 된다고...
DB 프로시져가 빠를수 밖에 없음을 인지하고 있었지만.. 그래도 한번 비교해봤다.
아래는 단순한 성능 테스트용이다.
예제) 헤더 테이블에서 자료를 읽어 들여서 해당 라인의 어떤 정보를 다시 쿼리해와서 리턴하는 예제이다.
헤더에는 칼럼 5개, 라인에도 칼럼 5개가 존재하며 , 헤더는 총 1,000개의 row, 라인은 10,000의 데이터를 연관 지어 생성했다.
@RequestMapping("/case01")
public String case01(){
List<Map<String, Object>> headerList = testService.selectClmHeaderList()
.stream()
.map(map ->{
Map<String, Object> param = new HashMap<>();
param.put("CLM_ID",map.get("CLM_ID"));
List<Map<String, Object>> linelist = testService.selectClmLineAmt(param);
if(linelist.size() > 0){
log.info(" 라인 정보가 있음 ===========================================");
map.put("NEW_AMT",map.get("NEW_AMT"));
}else{
log.info(" 라인 정보가 없음 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
}
return map;
})
.collect(Collectors.toList());
return "성공";
}
두 번째 예제 동일한 데이터를 DB프로그램으로 만들어서 데이터를 조회한다.
서브쿼리로 만들어서 간단한 예제를 만들었는데, loop 돌려서 라인의 특정 데이터를 리턴한다.
중요.참고로 커서를 오픈하면 맵리스로트 못받는다.
리스트 리턴부분은 주석 필요
@RequestMapping("/declare02")
public String declareExample02(){
Map<String, Object> map = new HashMap<>();
testService.selectMyDeclare2(map);
return map.toString();
}
<!-- 커서를 정의해서 정보를 자바로 받아보기 -->
<resultMap id="resultList" type="Hashmap" />
<select id="selectMyDeclare2" statementType="CALLABLE" parameterType="HashMap">
<![CDATA[
{
CALL
DECLARE
V_CLM_ID NUMBER;
V_CLM01_AMT NUMBER;
V_CNT NUMBER := 0 ;
V_AMT NUMBER := 0 ;
CUR_CLM_HEADER SYS_REFCURSOR;
BEGIN
OPEN CUR_CLM_HEADER FOR
SELECT CLM_ID,
CLM01_AMT
FROM TB_CLM_HEADER A;
LOOP
FETCH CUR_CLM_HEADER INTO V_CLM_ID, V_CLM01_AMT;
EXIT WHEN CUR_CLM_HEADER%NOTFOUND;
V_CNT := V_CNT + 1;
SELECT SUM(CLM01_AMT)
INTO V_AMT
FROM TB_CLM_LINE
WHERE CLM_ID = V_CLM_ID;
END LOOP;
CLOSE CUR_CLM_HEADER; --커서를 닫기전에 받는다.
#{OUT_AMT, mode=OUT , jdbcType=VARCHAR, javaType=String} := V_AMT;
#{OUT_CNT, mode=OUT , jdbcType=VARCHAR, javaType=String} := V_CNT;
#{resultList, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=resultList}:=CUR_CLM_HEADER;
END
}]]>
</select>
목적에 따라 애플리케이션을 구축하는 방법이나 활용 기술은 유연하게 대처해야 된다고 생각된다.
유연하게 생각하고 대처하는 자세가 중요하다.
DB Procedure 나 Function를 사용하지 않으려는 이유들...
- 객체지향 언어인 자바와 프로그래밍 패러다임이 다르다. 많은 문제들이 부수적으로 생긴다.
- 데이터베이스는 비싸다.
- 스케일업, 아웃에 취약할 수 있다.
- 확장에 유연하지 못하다.
- 소스관리(형상) 관리가 어렵다.
단점도 있지만 장점도 존재한다.
Was가 여러대 멀티로 구성되어 있으며 사용하는 프로그래밍 언어가 다른 경우다. 데이터베이스에 비즈니스 로직이 프로시져로 하나로 되어 있다면? 동일한 로직으로 DB트랜잭션이 발생되어 유지보수에 용이하다.
그리고 이번 포스팅의 예제와 같이 속도가 빠르다는 장점도 있다.
하지만 데이터베이스는 스케일 업이나 기타 확장에 비용이 많이 든다. 또한 소스관리가 쉽지 않은 점이 존재한다. 프로시져나 펑션 관리를 Git이나 Svn 스타일로 못할바는 없다. DB오브젝트들도 보관하는 자체 테이블들이 존재한다. 버전만 먹이면 된다.
'슬기로운 자바 개발자 생활 > 스프링 및 자바웹 서비스' 카테고리의 다른 글
Java map object value to integer Casting (0) | 2023.06.15 |
---|---|
스프링부트 WebFlux 스트리밍 (1) | 2023.06.11 |
스프링에서 oracle declare 문 사용해보기 (0) | 2023.05.14 |
RestTemplate와 WebClient 사용해보기 (0) | 2023.03.14 |
JSP page request, response (0) | 2023.01.14 |
댓글