본문 바로가기

Backend

iBatis 쿼리 가져오기

iBatis 쿼리를 저장해야 할 일이 생겼다.
결론부터 말하면 오버헤드가 생각보다 클 것 같아서 구현해놓고도 써도될까 싶다.

iBatis를 사용하는 시스템의 경우 쿼리가 엄청나게 길다

정확히는 실제 실행 된 쿼리를 가져오는건 아니고 당시의 파라미터와 다이나믹 쿼리를 조합하여 구성하는 것.
스택오버플로우를 참고하여 작성했다.

public void getQueryString(HashMap parameterMap) throws Exception {
    ApplicationContext context = new ClassPathXmlApplicationContext("conf/appContext.xml");
    SqlMapClientImpl sqlMapClient = (SqlMapClientImpl) context.getBean("sqlMapClient");
    
    MappedStatement mappedStatement = sqlMapClient.getMappedStatement("MySqlMap.getTableNames");
    
    RequestScope requestScope = new RequestScope();
    mappedStatement.initRequest(requestScope);
    
    String sqlString =  mappedStatement.getSql().getSql(requestScope, parameterMap);
    Object[] params = mappedStatement.getSql().getParameterMap(requestScope, parameterMap).getParameterObjectValues(requestScope, parameterMap);
    
    System.out.println(">>>>> USER_ID: " + getSessionUser());
    System.out.println(">>>>> QUERY_STRING: " + bindQueryParam(sqlString, params));
    
    return;
}

public String bindQueryParam(String sql, Object... params) {
    for (Object param : params) {
       sql = sql.replaceFirst("\\?",
               param == null ? "null" : "'"+param.toString()+"'");
    }
    return sql;
}

public String getSessionUser() {
    return (String) RequestContextHolder.getRequestAttributes().getAttribute("USER_ID", RequestAttributes.SCOPE_SESSION);
}

 

멀티 스레드 환경 영향도 분석

MappedStatement, Sql은 같은 객체를(아마도) 재사용하지만 해당 객체가 변하지는 않는다. 즉 유틸처럼 활용된다.
→ initRequest로 statementScope를 초기화(mappedStatement를 변경하는게 아님)한다.
→ getSql(requestScope, ParameterMap) 호출 시 Sql 문자열이 새로 생성된다
   (객체 내부에서 new SqlTagContext()로 매 쿼리마다 새로 생성 됨).

>>>>> 따라서 내부적으로 공유자원 없이 동작하기 때문에 Thread-safe하다.

 

MyBatis의 경우 이러한 복잡한 절차 없이 Plugin/Interceptor를 사용하면 손쉽게 구현이 가능하다.

 

참고:
https://stackoverflow.com/questions/44048186/how-to-get-parameterized-query-with-ibatis-spring
https://github.com/mybatis/ibatis-2