본문 바로가기
描く

ibatis # $차이 동적 테이블, 동적 컬럼을 사용해보자.

by 엘리후 2021. 7. 7.

ibatis를 사용하는 경우 동적으로 table name과 column name을 생성하여

쿼리를 실행해야 하는 경우가 발생한다.

예를 들면 다음과 같다.

<select id="fileInfo" resultClass="java.lang.String" parameterClass="java.util.Map">

SELECT $columnName$

FROM $tableName$

WHERE $pkName$ = #pkValue#

</select>

전혀 이상없는 구문이나 실행시 에러가 발생한다.

이상하게도 처음 실행시에는 에러가 발생하지 않으나,

두번째 부터 에러가 발생하기 시작한다.

WAS를 재기동하면 또 첫번째는 이상없으나, 두번째 부터 에러가 발생한다.

에러 내용은 다음과 같다.

(본인은 Spring AOP를 이용하여 ibatis 트랜잭션을 핸들링 하고 있으므로

에러 로그는 틀릴수 있다.)

[03-05 20:41:52] ERROR StandardWrapperValve.java:253 : Servlet.service() for servlet jsp threw exception

java.sql.SQLException: 부적합한 열 이름

at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)

at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:146)

at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:208)

at oracle.jdbc.driver.OracleStatement.getColumnIndex(OracleStatement.java:3296)

at oracle.jdbc.driver.OracleResultSetImpl.findColumn(OracleResultSetImpl.java:1914)

at oracle.jdbc.driver.OracleResultSet.getString(OracleResultSet.java:1515)

at org.apache.tomcat.dbcp.dbcp.DelegatingResultSet.getString(DelegatingResultSet.java:225)

at sun.reflect.GeneratedMethodAccessor36.invoke(Unknown Source)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

at java.lang.reflect.Method.invoke(Method.java:585)

at com.ibatis.common.jdbc.logging.ResultSetLogProxy.invoke(ResultSetLogProxy.java:47)

at $Proxy14.getString(Unknown Source)

at com.ibatis.sqlmap.engine.type.StringTypeHandler.getResult(StringTypeHandler.java:35)

at com.ibatis.sqlmap.engine.mapping.result.BasicResultMap.getPrimitiveResultMappingValue(BasicResultMap.java:611)

at com.ibatis.sqlmap.engine.mapping.result.BasicResultMap.getResults(BasicResultMap.java:344)

at com.ibatis.sqlmap.engine.mapping.result.AutoResultMap.getResults(AutoResultMap.java:55)

at com.ibatis.sqlmap.engine.execution.SqlExecutor.handleResults(SqlExecutor.java:381)

at com.ibatis.sqlmap.engine.execution.SqlExecutor.handleMultipleResults(SqlExecutor.java:301)

at com.ibatis.sqlmap.engine.execution.SqlExecutor.executeQuery(SqlExecutor.java:190)

at com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.sqlExecuteQuery(GeneralStatement.java:205)

at com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeQueryWithCallback(GeneralStatement.java:173)

at com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeQueryForObject(GeneralStatement.java:104)

at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForObject(SqlMapExecutorDelegate.java:566)

at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.queryForObject(SqlMapExecutorDelegate.java:541)

at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.queryForObject(SqlMapSessionImpl.java:106)

at org.springframework.orm.ibatis.SqlMapClientTemplate$1.doInSqlMapClient(SqlMapClientTemplate.java:243)

at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:193)

at org.springframework.orm.ibatis.SqlMapClientTemplate.queryForObject(SqlMapClientTemplate.java:241)

at com.thinkfree.common.CommonDao.fileDelete(CommonDao.java:31)

at com.thinkfree.common.CommonService.fileDelete(CommonService.java:29)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

at java.lang.reflect.Method.invoke(Method.java:585)

at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:299)

at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:172)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:139)

at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:107)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)

at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)

해결하기 위해 구글링을 시작하였다.

역시나 다른 사람들 또한 이런문제로 고민을 했었다.

ibatis는 특정 id의 쿼리를 실행 한 후 해당 id의 쿼리를

캐쉬로 보관하고 있다가

동일한 id에 대한 요청시 캐쉬에 저장된 쿼리정보를 토대로 result값을 맵핑 시킨다고 한다.

(ibatis에서 제공하는 CacheModel과는 전혀무관한 얘기임)

이를 해결하기 위해선 remapResults="true" 속성을 추가해 주면 된다.

default remapResults="false" 이기때문에 항상 요청된 id와 동일한게

캐쉬에 저장되어 있다면 캐쉬의 내용으로 맵핑하려 할것이고

에러가 날것이다. 위 속성을 true로 지정하면

캐쉬의 내용을 무시하고 현재의 값을 가지고 맵핑시킴.

즉 아래와 같이 사용하면 된다.

<select id="fileInfo" remapResults="true" resultClass="java.lang.String" parameterClass="java.util.Map">

SELECT $columnName$

FROM $tableName$

WHERE $pkName$ = #pkValue#

</select>

출처: http://jace.tistory.com/57 [Jace's Blog]

ibatis # $차이 동적 테이블, 동적 컬럼을 사용해보자.

 

개발을 하다보면 같은 쿼리문인데 컬럼이나 테이블명만 다른 경우가 있다.

이럴때 동적으로 컬럼과 테이블만 매핑해주면

 

쿼리의 양을 줄일 수 있다.

그럼 그 사용방법을 알아보자.

 

보통 iBatis를 활용해 값을 바인딩 할때 #value# 를 사용했을 것이다.

예를 들면

SELECT *

FROM TABLE1

WHERE COLUME1 = #value#

이런식으로.

 

위와 같이 #을 사용하면

SELECT *

FROM TABLE1

WHERE COLUME1 = ?

와 같은 쿼리의 ? 에 해당 #value# 값이 바인딩 되는 것이다.

 

테이블이나 컬럼위치에 값을 바인딩해서 사용하면

SELECT #COLUME1#

FROM TABLE1

 

java.sql.SQLException: ORA-01747: 열명을 올바르게 지정해 주십시오

 

위와 같은 에러를 보게 될 것이다.

 

이를 해결하기 위해 사용하는 것이 $ 이다.

위의 예에서 #만 $으로 바꾸어 사용하게 되면

 

 

 

SELECT $COLUME1$

FROM TABLE1

 

원하는 값이 나오게 된다.

table도 마찮가지 이다.

 

SELECT COLUME1

FROM $TABLE1$

 

이렇게 $를 사용하면 직접 그 값을 불러와 사용할 수 있다.

 

하지만 여기에도 주의점이 있다. 여러군데서 동적으로 쿼리문을 사용할 경우

iBatis 에 저장된 캐쉬에 의해 자꾸 전에 사용된 컬럼이나 테이블 값이 매핑된다.

 

이전 테이블에서 사용한 키나 테이블이 매핑이 되니 오류는 자연스럽게 발생한다.

 

java.sql.SQLException: 부적합한 열 이름

 

이를 해결하는 옵션이 remapResults="true" 이다.

 

remapResults="true" parameterClass="searchVO" resultClass="java.lang.String">

SELECT MAX(T1.$keyName$) KEEP(DENSE_RANK FIRST ORDER BY REG_DTM DESC)

FROM $tableName$ T1

 

 

위와 같이 사용하면 동적 테이블이나 컬럼을 활용할 수 있다.

 

위의 예에서 사용된 Oracle KEEP() 함수의 사용법은 아래의 Link를 참조한다.

 

Link : Oracle 오라클 KEEP()함수 사용법 그룹함수로 조회한 행 중에 다른 컬럼 가져오는 방법

출처: http://aljjabaegi.tistory.com/239 [알짜배기 프로그래머]

댓글