글
[ibatis] 동적으로 column명을 사용할때 java.sql.SQLException: 부적합한 열 이름 에러 해결
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>