velocity-scripting
velocity-scripting copied to clipboard
#set inside #repeat does not work correctly
Mapper:
<update id="velocity-nested-set" lang="velocity">
#repeat( $_parameter.list $level1)
#set( $foo = $level1)
@{foo}
#end
</update>
Test:
@Test
public void velocityTestNestedSet() throws SQLException, IOException {
expect(connection.getAutoCommit()).andStubReturn(false);
expect(connection.prepareStatement("\n" +
" ?\n" +
" ?\n" +
" ")).andReturn(statement);
statement.setInt(1, 1);
statement.addBatch();
statement.setInt(2, 2);
statement.addBatch();
expect(statement.executeBatch()).andStubReturn(new int[]{2});
statement.close();
connection.setAutoCommit(true);
connection.rollback();
connection.close();
replay();
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(
Resources.getResourceAsStream("configuration.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, connection);
List<Integer> param = Arrays.asList(1,2);
sqlSession.insert("velocity-nested-set", param);
sqlSession.flushStatements();
sqlSession.close();
}
Exception:
java.lang.AssertionError:
Unexpected method call setInt(1, 2):
setInt(1, 1): expected: 1, actual: 0
addBatch(): expected: 2, actual: 0
setInt(2, 2): expected: 1, actual: 0
close(): expected: 1, actual: 0
at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:29)
at org.easymock.internal.ObjectMethodsFilter.invoke(ObjectMethodsFilter.java:56)
at com.sun.proxy.$Proxy7.setInt(Unknown Source)
at org.apache.ibatis.type.IntegerTypeHandler.setNonNullParameter(IntegerTypeHandler.java:28)
at org.apache.ibatis.type.IntegerTypeHandler.setNonNullParameter(IntegerTypeHandler.java:23)
at org.apache.ibatis.type.BaseTypeHandler.setParameter(BaseTypeHandler.java:46)
at org.apache.ibatis.type.UnknownTypeHandler.setNonNullParameter(UnknownTypeHandler.java:42)
at org.apache.ibatis.type.BaseTypeHandler.setParameter(BaseTypeHandler.java:46)
at org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:77)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:77)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler.java:58)
at org.apache.ibatis.executor.BatchExecutor.doUpdate(BatchExecutor.java:68)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:100)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:75)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:148)
at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:137)
Velocity vars like $foo and mybatis binding parameters like @{foo} has different scopes, so in this case @{foo} are always pointing to the last assigned value of $foo. @{foo} knows nothing about the iteration state.
In the current implementation, velocity runs before binding, in different phases, so it is not possible or at least not easy nor efficient way to support this requirement without a major refactoring.
Well, I understand this. But this limits usages a lot. I've got a question: why do you use this approach? As for me, using #p($foo) instead of @{foo) is a little more verbose, but much more flexible.
@emacarron This is a very old issue. Its root are in mybatis core because scripting evaluation and jdbc parameter binding are handled in different phases. I remember we have discussed this some years ago... Do you think it is time to address this or we will keep current mybatis way forever?