diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNode.java b/src/main/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNode.java index 27490c2ed15..cb0c8949f28 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNode.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNode.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2015 the original author or authors. + * Copyright 2009-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package org.apache.ibatis.scripting.xmltags; +import java.util.HashMap; import java.util.Map; import org.apache.ibatis.parsing.GenericTokenParser; @@ -122,6 +123,7 @@ private static String itemizeItem(String item, int i) { private static class FilteredDynamicContext extends DynamicContext { private DynamicContext delegate; + private HierarchyContextMap hierarchyContextMap; private int index; private String itemIndex; private String item; @@ -129,6 +131,7 @@ private static class FilteredDynamicContext extends DynamicContext { public FilteredDynamicContext(Configuration configuration,DynamicContext delegate, String itemIndex, String item, int i) { super(configuration, null); this.delegate = delegate; + this.hierarchyContextMap = new HierarchyContextMap(delegate); this.index = i; this.itemIndex = itemIndex; this.item = item; @@ -136,12 +139,16 @@ public FilteredDynamicContext(Configuration configuration,DynamicContext delegat @Override public Map getBindings() { - return delegate.getBindings(); + return hierarchyContextMap; } @Override public void bind(String name, Object value) { - delegate.bind(name, value); + if (name.startsWith(ITEM_PREFIX)) { + hierarchyContextMap.putGlobal(name, value); + } else { + hierarchyContextMap.putLocal(name, value); + } } @Override @@ -175,12 +182,14 @@ public int getUniqueNumber() { private class PrefixedContext extends DynamicContext { private DynamicContext delegate; + private HierarchyContextMap hierarchyContextMap; private String prefix; private boolean prefixApplied; public PrefixedContext(DynamicContext delegate, String prefix) { super(configuration, null); this.delegate = delegate; + this.hierarchyContextMap = new HierarchyContextMap(delegate); this.prefix = prefix; this.prefixApplied = false; } @@ -191,12 +200,16 @@ public boolean isPrefixApplied() { @Override public Map getBindings() { - return delegate.getBindings(); + return hierarchyContextMap; } @Override public void bind(String name, Object value) { - delegate.bind(name, value); + if (name.startsWith(ITEM_PREFIX)) { + hierarchyContextMap.putGlobal(name, value); + } else { + hierarchyContextMap.putLocal(name, value); + } } @Override @@ -218,5 +231,32 @@ public int getUniqueNumber() { return delegate.getUniqueNumber(); } } + + private static class HierarchyContextMap extends HashMap { + + private static final long serialVersionUID = -787211846381882154L; + private Map localContextMap = new HashMap(); + private DynamicContext delegate; + public HierarchyContextMap(DynamicContext delegate) { + this.delegate = delegate; + } + + public void putLocal(String name, Object value) { + localContextMap.put(name, value); + } + + public void putGlobal(String name, Object value) { + delegate.bind(name, value); + } + + @Override + public Object get(Object key) { + if (localContextMap.containsKey(key)) { + return localContextMap.get(key); + } + + return delegate.getBindings().get(key); + } + } } diff --git a/src/test/java/org/apache/ibatis/builder/xml/dynamic/DynamicSqlSourceTest.java b/src/test/java/org/apache/ibatis/builder/xml/dynamic/DynamicSqlSourceTest.java index 07b9bad68e4..d773a7fb9e7 100644 --- a/src/test/java/org/apache/ibatis/builder/xml/dynamic/DynamicSqlSourceTest.java +++ b/src/test/java/org/apache/ibatis/builder/xml/dynamic/DynamicSqlSourceTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2009-2015 the original author or authors. + * Copyright 2009-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -300,6 +300,20 @@ public void shouldTrimNoSetClause() throws Exception { assertEquals(expected, boundSql.getSql()); } + @Test + public void shouldForeachNotWriteMiddleVariableToGlobalBindings() throws Exception { + final HashMap parameterObject = new HashMap() {{ + put("array", new Integer[]{1,2,3,4,5}); + }}; + final String expected = "SELECT * FROM BLOG WHERE ID in ( ? , ? , ? , ? , ? )"; + DynamicSqlSource source = createDynamicSqlSource( + new TextSqlNode("SELECT * FROM BLOG WHERE ID in"), + new ForEachSqlNode(new Configuration(),mixedContents(new TextSqlNode("#{id}")), "array", "index", "id", "(", ")", ","), + new IfSqlNode(new TextSqlNode("AND id = #{id}"), "id != null")); + BoundSql boundSql = source.getBoundSql(parameterObject); + assertEquals(expected, boundSql.getSql()); + } + @Test public void shouldIterateOnceForEachItemInCollection() throws Exception { final HashMap parameterObject = new HashMap() {{ @@ -316,7 +330,7 @@ public void shouldIterateOnceForEachItemInCollection() throws Exception { assertEquals("__frch_item_1", boundSql.getParameterMappings().get(1).getProperty()); assertEquals("__frch_item_2", boundSql.getParameterMappings().get(2).getProperty()); } - + @Test public void shouldHandleOgnlExpression() throws Exception { final HashMap parameterObject = new HashMap() {{