From f21fb7bc0b4ad79421decd6825ba97cdeaa75d4c Mon Sep 17 00:00:00 2001
From: kmoco2am
Date: Sun, 11 Jan 2015 17:17:20 +0100
Subject: [PATCH 01/11] Support for parametrized include added.
---
.../builder/xml/XMLIncludeTransformer.java | 96 ++++++++++++++++++-
.../ibatis/builder/xml/mybatis-3-mapper.dtd | 8 +-
.../org/apache/ibatis/builder/PostMapper.xml | 6 +-
.../ibatis/submitted/includes/Mapper.xml | 16 +++-
4 files changed, 117 insertions(+), 9 deletions(-)
mode change 100644 => 100755 src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
mode change 100644 => 100755 src/main/java/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd
mode change 100644 => 100755 src/test/java/org/apache/ibatis/builder/PostMapper.xml
mode change 100644 => 100755 src/test/java/org/apache/ibatis/submitted/includes/Mapper.xml
diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java b/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
old mode 100644
new mode 100755
index 110fbe5a18c..48d92c873df
--- a/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
+++ b/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
@@ -17,17 +17,25 @@
import org.apache.ibatis.builder.IncompleteElementException;
import org.apache.ibatis.builder.MapperBuilderAssistant;
+import org.apache.ibatis.parsing.GenericTokenParser;
import org.apache.ibatis.parsing.PropertyParser;
+import org.apache.ibatis.parsing.TokenHandler;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.session.Configuration;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
+import java.util.*;
+
/**
* @author Frank D. Martinez [mnesarco]
*/
public class XMLIncludeTransformer {
+ public static final String PLACEHOLDER_START = "${";
+
+ public static final String PLACEHOLDER_END = "}";
+
private final Configuration configuration;
private final MapperBuilderAssistant builderAssistant;
@@ -37,9 +45,27 @@ public XMLIncludeTransformer(Configuration configuration, MapperBuilderAssistant
}
public void applyIncludes(Node source) {
+ applyIncludes(source, new HashMap());
+ }
+
+ private void applyIncludes(Node source, final Map placeholderContext) {
+ GenericTokenParser tokenParser = new GenericTokenParser(PLACEHOLDER_START, PLACEHOLDER_END, new PlaceholderTokenHandler(placeholderContext));
if (source.getNodeName().equals("include")) {
- Node toInclude = findSqlFragment(getStringAttribute(source, "refid"));
- applyIncludes(toInclude);
+ Map fullContext = placeholderContext;
+
+ Node toInclude = findSqlFragment(tokenParser.parse(getStringAttribute(source, "refid")));
+ Map newPlaceholderContext = getPlaceholderContext(source);
+ if (!newPlaceholderContext.isEmpty()) {
+ for (String name : newPlaceholderContext.keySet()) {
+ newPlaceholderContext.put(name, tokenParser.parse(newPlaceholderContext.get(name)));
+ }
+ applyInheritedContext(newPlaceholderContext, placeholderContext);
+ fullContext = newPlaceholderContext;
+ }
+ if (!fullContext.isEmpty()) {
+ toInclude = toInclude.cloneNode(true);
+ }
+ applyIncludes(toInclude, fullContext);
if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {
toInclude = source.getOwnerDocument().importNode(toInclude, true);
}
@@ -51,8 +77,12 @@ public void applyIncludes(Node source) {
} else if (source.getNodeType() == Node.ELEMENT_NODE) {
NodeList children = source.getChildNodes();
for (int i=0; i newContext, Map inheritedContext) {
+ for (Map.Entry e : inheritedContext.entrySet()) {
+ if (!newContext.containsKey(e.getKey())) {
+ newContext.put(e.getKey(), e.getValue());
+ }
+ }
+ }
+
+ private Map getPlaceholderContext(Node node) {
+ List subElements = getSubElements(node);
+ if (subElements.isEmpty()) {
+ return Collections.emptyMap();
+ } else {
+ Map placeholderContext = new HashMap(subElements.size());
+ for (Node placeholderValue : subElements) {
+ String name = getStringAttribute(placeholderValue, "name");
+ String value = getStringAttribute(placeholderValue, "value");
+ // Push new value
+ String originalValue = placeholderContext.put(name, value);
+ if (originalValue != null) {
+ throw new IllegalArgumentException("Placeholder " + name + " defined twice in the same include definition");
+ }
+ }
+ return placeholderContext;
+ }
+ }
+
+ private List getSubElements(Node node) {
+ NodeList children = node.getChildNodes();
+ if (children.getLength() == 0) {
+ return Collections.emptyList();
+ } else {
+ List elements = new ArrayList();
+ for (int i = 0; i < children.getLength(); i++) {
+ Node n = children.item(i);
+ if (n.getNodeType() == Node.ELEMENT_NODE) {
+ elements.add(n);
+ }
+ }
+ return elements;
+ }
+ }
+
+ private class PlaceholderTokenHandler implements TokenHandler {
+
+ private final Map context;
+
+ public PlaceholderTokenHandler(Map context) {
+ this.context = context;
+ }
+
+ @Override
+ public String handleToken(String content) {
+ if (context != null && context.containsKey(content)) {
+ return context.get(content);
+ }
+ return PLACEHOLDER_START + content + PLACEHOLDER_END;
+ }
+ }
}
diff --git a/src/main/java/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd b/src/main/java/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd
old mode 100644
new mode 100755
index 71744452ac8..44a5673c7a5
--- a/src/main/java/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd
+++ b/src/main/java/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd
@@ -238,11 +238,17 @@ lang CDATA #IMPLIED
-
+
+
+
+
- blog_id = #{blog_id}
+ ${prefix}_id = #{blog_id}
@@ -102,7 +102,9 @@
-
+
+
+
diff --git a/src/test/java/org/apache/ibatis/submitted/includes/Mapper.xml b/src/test/java/org/apache/ibatis/submitted/includes/Mapper.xml
old mode 100644
new mode 100755
index 8c2b3a1f9e0..8ec4162cf90
--- a/src/test/java/org/apache/ibatis/submitted/includes/Mapper.xml
+++ b/src/test/java/org/apache/ibatis/submitted/includes/Mapper.xml
@@ -23,13 +23,21 @@
- SomeTable
+ ${prefix}Table
+
+
+
+
field1, field2, field3
- from
+ from
+
+
+
+
@@ -38,7 +46,9 @@
-
+
+
+
set Field2 = #{field2,jdbcType=INTEGER},
Field3 = #{field3,jdbcType=VARCHAR},
where field1 = #{field1,jdbcType=INTEGER}
From 31c477f4f499b5453382e1574e76f2ebf2d09664 Mon Sep 17 00:00:00 2001
From: kmoco2am
Date: Sun, 11 Jan 2015 22:26:23 +0100
Subject: [PATCH 02/11] Adding more comments, one more test in SQL, removing
node duplication
---
.../builder/xml/XMLIncludeTransformer.java | 34 ++++++++++++++-----
.../ibatis/submitted/includes/Mapper.xml | 4 ++-
2 files changed, 29 insertions(+), 9 deletions(-)
diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java b/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
index 48d92c873df..57d9fbb41d2 100755
--- a/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
+++ b/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
@@ -47,13 +47,22 @@ public XMLIncludeTransformer(Configuration configuration, MapperBuilderAssistant
public void applyIncludes(Node source) {
applyIncludes(source, new HashMap());
}
-
+
+ /**
+ * Recursively apply includes through all SQL fragments.
+ * @param source Include node in DOM tree
+ * @param placeholderContext Current context for static placeholders with values
+ */
private void applyIncludes(Node source, final Map placeholderContext) {
GenericTokenParser tokenParser = new GenericTokenParser(PLACEHOLDER_START, PLACEHOLDER_END, new PlaceholderTokenHandler(placeholderContext));
if (source.getNodeName().equals("include")) {
+ // new full context for included SQL - contains inherited context and new variables from current include node
Map fullContext = placeholderContext;
-
- Node toInclude = findSqlFragment(tokenParser.parse(getStringAttribute(source, "refid")));
+
+ String refid = getStringAttribute(source, "refid");
+ // replace placeholders also in include refid value
+ refid = tokenParser.parse(refid);
+ Node toInclude = findSqlFragment(refid);
Map newPlaceholderContext = getPlaceholderContext(source);
if (!newPlaceholderContext.isEmpty()) {
for (String name : newPlaceholderContext.keySet()) {
@@ -62,9 +71,6 @@ private void applyIncludes(Node source, final Map placeholderCon
applyInheritedContext(newPlaceholderContext, placeholderContext);
fullContext = newPlaceholderContext;
}
- if (!fullContext.isEmpty()) {
- toInclude = toInclude.cloneNode(true);
- }
applyIncludes(toInclude, fullContext);
if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {
toInclude = source.getOwnerDocument().importNode(toInclude, true);
@@ -80,8 +86,10 @@ private void applyIncludes(Node source, final Map placeholderCon
applyIncludes(children.item(i), placeholderContext);
}
} else if (source.getNodeType() == Node.ATTRIBUTE_NODE && !placeholderContext.isEmpty()) {
+ // replace placeholders in all attribute values
source.setNodeValue(tokenParser.parse(source.getNodeValue()));
} else if (source.getNodeType() == Node.TEXT_NODE && !placeholderContext.isEmpty()) {
+ // replace placeholder ins all text nodes
source.setNodeValue(tokenParser.parse(source.getNodeValue()));
}
}
@@ -100,7 +108,12 @@ private Node findSqlFragment(String refid) {
private String getStringAttribute(Node node, String name) {
return node.getAttributes().getNamedItem(name).getNodeValue();
}
-
+
+ /**
+ * Add inherited context into newly created one.
+ * @param newContext placeholders for current include instance where inherited values will be placed
+ * @param inheritedContext all inherited placeholder values
+ */
private void applyInheritedContext(Map newContext, Map inheritedContext) {
for (Map.Entry e : inheritedContext.entrySet()) {
if (!newContext.containsKey(e.getKey())) {
@@ -108,7 +121,12 @@ private void applyInheritedContext(Map newContext, Map getPlaceholderContext(Node node) {
List subElements = getSubElements(node);
if (subElements.isEmpty()) {
diff --git a/src/test/java/org/apache/ibatis/submitted/includes/Mapper.xml b/src/test/java/org/apache/ibatis/submitted/includes/Mapper.xml
index 8ec4162cf90..8f9c2f83af1 100755
--- a/src/test/java/org/apache/ibatis/submitted/includes/Mapper.xml
+++ b/src/test/java/org/apache/ibatis/submitted/includes/Mapper.xml
@@ -31,7 +31,9 @@
-
+
+
+
field1, field2, field3
from
From bd80dc1a4dbfd74aa59a0c708415ed4f3109bffd Mon Sep 17 00:00:00 2001
From: kmoco2am
Date: Sun, 1 Feb 2015 14:00:13 +0100
Subject: [PATCH 03/11] Reusing Properties instead of Map because it is done
also for configuration variables, processing simplified by using
PropertyParser, more documentation added.
---
.../builder/xml/XMLIncludeTransformer.java | 66 ++++++++-----------
1 file changed, 27 insertions(+), 39 deletions(-)
diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java b/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
index 57d9fbb41d2..5e551b67057 100755
--- a/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
+++ b/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
@@ -32,10 +32,6 @@
*/
public class XMLIncludeTransformer {
- public static final String PLACEHOLDER_START = "${";
-
- public static final String PLACEHOLDER_END = "}";
-
private final Configuration configuration;
private final MapperBuilderAssistant builderAssistant;
@@ -45,7 +41,12 @@ public XMLIncludeTransformer(Configuration configuration, MapperBuilderAssistant
}
public void applyIncludes(Node source) {
- applyIncludes(source, new HashMap());
+ Properties placeholderContext = new Properties();
+ Properties configurationVariables = configuration.getVariables();
+ if (configurationVariables != null) {
+ placeholderContext.putAll(configurationVariables);
+ }
+ applyIncludes(source, placeholderContext);
}
/**
@@ -53,23 +54,27 @@ public void applyIncludes(Node source) {
* @param source Include node in DOM tree
* @param placeholderContext Current context for static placeholders with values
*/
- private void applyIncludes(Node source, final Map placeholderContext) {
- GenericTokenParser tokenParser = new GenericTokenParser(PLACEHOLDER_START, PLACEHOLDER_END, new PlaceholderTokenHandler(placeholderContext));
+ private void applyIncludes(Node source, final Properties placeholderContext) {
if (source.getNodeName().equals("include")) {
// new full context for included SQL - contains inherited context and new variables from current include node
- Map fullContext = placeholderContext;
+ Properties fullContext;
String refid = getStringAttribute(source, "refid");
- // replace placeholders also in include refid value
- refid = tokenParser.parse(refid);
+ // replace placeholders and variables in include refid value
+ refid = PropertyParser.parse(refid, placeholderContext);
Node toInclude = findSqlFragment(refid);
- Map newPlaceholderContext = getPlaceholderContext(source);
+ Properties newPlaceholderContext = getPlaceholderContext(source);
if (!newPlaceholderContext.isEmpty()) {
- for (String name : newPlaceholderContext.keySet()) {
- newPlaceholderContext.put(name, tokenParser.parse(newPlaceholderContext.get(name)));
+ // replace placeholders in new variables too
+ for (Object name : newPlaceholderContext.keySet()) {
+ newPlaceholderContext.put(name, PropertyParser.parse(newPlaceholderContext.get(name).toString(), placeholderContext));
}
+ // merge new and inherited into new full one
applyInheritedContext(newPlaceholderContext, placeholderContext);
fullContext = newPlaceholderContext;
+ } else {
+ // no new context - use inherited fully
+ fullContext = placeholderContext;
}
applyIncludes(toInclude, fullContext);
if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {
@@ -87,15 +92,14 @@ private void applyIncludes(Node source, final Map placeholderCon
}
} else if (source.getNodeType() == Node.ATTRIBUTE_NODE && !placeholderContext.isEmpty()) {
// replace placeholders in all attribute values
- source.setNodeValue(tokenParser.parse(source.getNodeValue()));
+ source.setNodeValue(PropertyParser.parse(source.getNodeValue(), placeholderContext));
} else if (source.getNodeType() == Node.TEXT_NODE && !placeholderContext.isEmpty()) {
// replace placeholder ins all text nodes
- source.setNodeValue(tokenParser.parse(source.getNodeValue()));
+ source.setNodeValue(PropertyParser.parse(source.getNodeValue(), placeholderContext));
}
}
private Node findSqlFragment(String refid) {
- refid = PropertyParser.parse(refid, configuration.getVariables());
refid = builderAssistant.applyCurrentNamespace(refid, true);
try {
XNode nodeToInclude = configuration.getSqlFragments().get(refid);
@@ -111,11 +115,11 @@ private String getStringAttribute(Node node, String name) {
/**
* Add inherited context into newly created one.
- * @param newContext placeholders for current include instance where inherited values will be placed
+ * @param newContext placeholders defined current include clause where inherited values will be placed
* @param inheritedContext all inherited placeholder values
*/
- private void applyInheritedContext(Map newContext, Map inheritedContext) {
- for (Map.Entry e : inheritedContext.entrySet()) {
+ private void applyInheritedContext(Properties newContext, Properties inheritedContext) {
+ for (Map.Entry e : inheritedContext.entrySet()) {
if (!newContext.containsKey(e.getKey())) {
newContext.put(e.getKey(), e.getValue());
}
@@ -127,17 +131,17 @@ private void applyInheritedContext(Map newContext, Map getPlaceholderContext(Node node) {
+ private Properties getPlaceholderContext(Node node) {
List subElements = getSubElements(node);
if (subElements.isEmpty()) {
- return Collections.emptyMap();
+ return new Properties();
} else {
- Map placeholderContext = new HashMap(subElements.size());
+ Properties placeholderContext = new Properties();
for (Node placeholderValue : subElements) {
String name = getStringAttribute(placeholderValue, "name");
String value = getStringAttribute(placeholderValue, "value");
// Push new value
- String originalValue = placeholderContext.put(name, value);
+ Object originalValue = placeholderContext.put(name, value);
if (originalValue != null) {
throw new IllegalArgumentException("Placeholder " + name + " defined twice in the same include definition");
}
@@ -162,20 +166,4 @@ private List getSubElements(Node node) {
}
}
- private class PlaceholderTokenHandler implements TokenHandler {
-
- private final Map context;
-
- public PlaceholderTokenHandler(Map context) {
- this.context = context;
- }
-
- @Override
- public String handleToken(String content) {
- if (context != null && context.containsKey(content)) {
- return context.get(content);
- }
- return PLACEHOLDER_START + content + PLACEHOLDER_END;
- }
- }
}
From 463a4a551fc92aff7bd7052e0071837fef5fcb40 Mon Sep 17 00:00:00 2001
From: kmoco2am
Date: Sun, 1 Feb 2015 18:27:17 +0100
Subject: [PATCH 04/11] Tests added for parametrized include
---
.../ibatis/submitted/includes/CreateDB.sql | 26 +++++++++++
.../submitted/includes/IncludeTest.java | 46 ++++++++++++++++---
2 files changed, 65 insertions(+), 7 deletions(-)
create mode 100755 src/test/java/org/apache/ibatis/submitted/includes/CreateDB.sql
mode change 100644 => 100755 src/test/java/org/apache/ibatis/submitted/includes/IncludeTest.java
diff --git a/src/test/java/org/apache/ibatis/submitted/includes/CreateDB.sql b/src/test/java/org/apache/ibatis/submitted/includes/CreateDB.sql
new file mode 100755
index 00000000000..77d2af45e50
--- /dev/null
+++ b/src/test/java/org/apache/ibatis/submitted/includes/CreateDB.sql
@@ -0,0 +1,26 @@
+--
+-- Copyright 2009-2015 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.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+
+drop table SomeTable if exists;
+
+create table SomeTable (
+ id int,
+ field1 varchar(20),
+ field2 varchar(20),
+ field3 varchar(20)
+);
+
+insert into SomeTable (id, field1, field2, field3) values(1, 'a', 'b', 'c');
\ No newline at end of file
diff --git a/src/test/java/org/apache/ibatis/submitted/includes/IncludeTest.java b/src/test/java/org/apache/ibatis/submitted/includes/IncludeTest.java
old mode 100644
new mode 100755
index 3ef1946ce8a..b91554e226b
--- a/src/test/java/org/apache/ibatis/submitted/includes/IncludeTest.java
+++ b/src/test/java/org/apache/ibatis/submitted/includes/IncludeTest.java
@@ -16,26 +16,46 @@
package org.apache.ibatis.submitted.includes;
import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.jdbc.ScriptRunner;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import static org.junit.Assert.assertNotNull;
+
+import org.junit.BeforeClass;
import org.junit.Test;
import java.io.Reader;
+import java.sql.Connection;
+import java.util.Map;
+
import org.apache.ibatis.session.SqlSession;
import org.junit.Assert;
public class IncludeTest {
+ private static SqlSessionFactory sqlSessionFactory;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ // create a SqlSessionFactory
+ Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/includes/MapperConfig.xml");
+ sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
+ reader.close();
+
+ // populate in-memory database
+ SqlSession session = sqlSessionFactory.openSession();
+ Connection conn = session.getConnection();
+ reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/includes/CreateDB.sql");
+ ScriptRunner runner = new ScriptRunner(conn);
+ runner.setLogWriter(null);
+ runner.runScript(reader);
+ reader.close();
+ session.close();
+ }
+
@Test
public void testIncludes() throws Exception {
- String resource = "org/apache/ibatis/submitted/includes/MapperConfig.xml";
- Reader reader = Resources.getResourceAsReader(resource);
- SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
- SqlSessionFactory sqlMapper = builder.build(reader);
- assertNotNull(sqlMapper);
-
- final SqlSession sqlSession = sqlMapper.openSession();
+ final SqlSession sqlSession = sqlSessionFactory.openSession();
try {
final Integer result = sqlSession.selectOne("org.apache.ibatis.submitted.includes.mapper.selectWithProperty");
Assert.assertEquals(Integer.valueOf(1), result);
@@ -43,4 +63,16 @@ public void testIncludes() throws Exception {
sqlSession.close();
}
}
+
+ @Test
+ public void testParametrizedIncludes() throws Exception {
+ final SqlSession sqlSession = sqlSessionFactory.openSession();
+ try {
+ final Map result = sqlSession.selectOne("org.apache.ibatis.submitted.includes.mapper.select");
+ //Assert.assertEquals(Integer.valueOf(1), result);
+ } finally {
+ sqlSession.close();
+ }
+ }
+
}
From f06a2e40bf7b9fc863608037f47d964b308da681 Mon Sep 17 00:00:00 2001
From: kmoco2am
Date: Mon, 2 Feb 2015 07:42:02 +0100
Subject: [PATCH 05/11] Using standard property clause for placeholders in XML
include
---
.../org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd | 8 +-------
src/test/java/org/apache/ibatis/builder/PostMapper.xml | 2 +-
.../java/org/apache/ibatis/submitted/includes/Mapper.xml | 8 ++++----
3 files changed, 6 insertions(+), 12 deletions(-)
diff --git a/src/main/java/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd b/src/main/java/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd
index 44a5673c7a5..54610173590 100755
--- a/src/main/java/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd
+++ b/src/main/java/org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd
@@ -238,17 +238,11 @@ lang CDATA #IMPLIED
-
+
-
-
-
-
+
diff --git a/src/test/java/org/apache/ibatis/submitted/includes/Mapper.xml b/src/test/java/org/apache/ibatis/submitted/includes/Mapper.xml
index 8f9c2f83af1..e0d3b02ad4e 100755
--- a/src/test/java/org/apache/ibatis/submitted/includes/Mapper.xml
+++ b/src/test/java/org/apache/ibatis/submitted/includes/Mapper.xml
@@ -32,13 +32,13 @@
-
+
field1, field2, field3
from
-
-
+
+
@@ -49,7 +49,7 @@
-
+
set Field2 = #{field2,jdbcType=INTEGER},
Field3 = #{field3,jdbcType=VARCHAR},
From 7d95da4ecbd3f818e19e81f8bfdbda8e773091ae Mon Sep 17 00:00:00 2001
From: kmoco2am
Date: Mon, 2 Feb 2015 07:43:03 +0100
Subject: [PATCH 06/11] Changing terms - using "variable" instead of
"placeholder" (the same is used in configuration.getVariables)
---
.../builder/xml/XMLIncludeTransformer.java | 66 +++++++++----------
1 file changed, 33 insertions(+), 33 deletions(-)
diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java b/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
index 5e551b67057..6be81532386 100755
--- a/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
+++ b/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
@@ -41,40 +41,40 @@ public XMLIncludeTransformer(Configuration configuration, MapperBuilderAssistant
}
public void applyIncludes(Node source) {
- Properties placeholderContext = new Properties();
+ Properties variablesContext = new Properties();
Properties configurationVariables = configuration.getVariables();
if (configurationVariables != null) {
- placeholderContext.putAll(configurationVariables);
+ variablesContext.putAll(configurationVariables);
}
- applyIncludes(source, placeholderContext);
+ applyIncludes(source, variablesContext);
}
/**
* Recursively apply includes through all SQL fragments.
* @param source Include node in DOM tree
- * @param placeholderContext Current context for static placeholders with values
+ * @param variablesContext Current context for static variables with values
*/
- private void applyIncludes(Node source, final Properties placeholderContext) {
+ private void applyIncludes(Node source, final Properties variablesContext) {
if (source.getNodeName().equals("include")) {
// new full context for included SQL - contains inherited context and new variables from current include node
Properties fullContext;
String refid = getStringAttribute(source, "refid");
- // replace placeholders and variables in include refid value
- refid = PropertyParser.parse(refid, placeholderContext);
+ // replace variables in include refid value
+ refid = PropertyParser.parse(refid, variablesContext);
Node toInclude = findSqlFragment(refid);
- Properties newPlaceholderContext = getPlaceholderContext(source);
- if (!newPlaceholderContext.isEmpty()) {
- // replace placeholders in new variables too
- for (Object name : newPlaceholderContext.keySet()) {
- newPlaceholderContext.put(name, PropertyParser.parse(newPlaceholderContext.get(name).toString(), placeholderContext));
+ Properties newVariablesContext = getVariablesContext(source);
+ if (!newVariablesContext.isEmpty()) {
+ // replace variables in variable values too
+ for (Object name : newVariablesContext.keySet()) {
+ newVariablesContext.put(name, PropertyParser.parse(newVariablesContext.get(name).toString(), variablesContext));
}
// merge new and inherited into new full one
- applyInheritedContext(newPlaceholderContext, placeholderContext);
- fullContext = newPlaceholderContext;
+ applyInheritedContext(newVariablesContext, variablesContext);
+ fullContext = newVariablesContext;
} else {
// no new context - use inherited fully
- fullContext = placeholderContext;
+ fullContext = variablesContext;
}
applyIncludes(toInclude, fullContext);
if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {
@@ -88,14 +88,14 @@ private void applyIncludes(Node source, final Properties placeholderContext) {
} else if (source.getNodeType() == Node.ELEMENT_NODE) {
NodeList children = source.getChildNodes();
for (int i=0; i e : inheritedContext.entrySet()) {
@@ -129,24 +129,24 @@ private void applyInheritedContext(Properties newContext, Properties inheritedCo
/**
* Read placholders and their values from include node definition.
* @param node Include node instance
- * @return placeholder context from include instance (no inherited values)
+ * @return variables context from include instance (no inherited values)
*/
- private Properties getPlaceholderContext(Node node) {
+ private Properties getVariablesContext(Node node) {
List subElements = getSubElements(node);
if (subElements.isEmpty()) {
return new Properties();
} else {
- Properties placeholderContext = new Properties();
- for (Node placeholderValue : subElements) {
- String name = getStringAttribute(placeholderValue, "name");
- String value = getStringAttribute(placeholderValue, "value");
+ Properties variablesContext = new Properties();
+ for (Node variableValue : subElements) {
+ String name = getStringAttribute(variableValue, "name");
+ String value = getStringAttribute(variableValue, "value");
// Push new value
- Object originalValue = placeholderContext.put(name, value);
+ Object originalValue = variablesContext.put(name, value);
if (originalValue != null) {
- throw new IllegalArgumentException("Placeholder " + name + " defined twice in the same include definition");
+ throw new IllegalArgumentException("Variable " + name + " defined twice in the same include definition");
}
}
- return placeholderContext;
+ return variablesContext;
}
}
From 260abff8779781731f851cc1709340708b1ea90d Mon Sep 17 00:00:00 2001
From: kmoco2am
Date: Mon, 2 Feb 2015 08:06:28 +0100
Subject: [PATCH 07/11] Documentation updated for parametrized include
---
src/site/xdoc/sqlmap-xml.xml | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
mode change 100644 => 100755 src/site/xdoc/sqlmap-xml.xml
diff --git a/src/site/xdoc/sqlmap-xml.xml b/src/site/xdoc/sqlmap-xml.xml
old mode 100644
new mode 100755
index 194c64213d6..c21598a053e
--- a/src/site/xdoc/sqlmap-xml.xml
+++ b/src/site/xdoc/sqlmap-xml.xml
@@ -485,18 +485,19 @@ ps.setInt(1,id);]]>
This element can be used to define a reusable fragment of SQL code that can be
- included in other statements. For example:
+ included in other statements. It can be statically (during load phase) parametrized and also included many time
+ with different property values. For example:
- id,username,password ]]>
+ ${alias}.id,${alias}.username,${alias}.password ]]>
The SQL fragment can then be included in another statement, for example:
- select
- from some_table
+ select
+ from some_table t
where id = #{id}
]]>
From 80181c899bb1e824aad24011fcf2cfcda12389ed Mon Sep 17 00:00:00 2001
From: kmoco2am
Date: Mon, 2 Feb 2015 19:11:03 +0100
Subject: [PATCH 08/11] Documentation added for complex parametrized includes
---
src/site/xdoc/sqlmap-xml.xml | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/src/site/xdoc/sqlmap-xml.xml b/src/site/xdoc/sqlmap-xml.xml
index c21598a053e..b93d5b02767 100755
--- a/src/site/xdoc/sqlmap-xml.xml
+++ b/src/site/xdoc/sqlmap-xml.xml
@@ -500,6 +500,28 @@ ps.setInt(1,id);]]>
from some_table t
where id = #{id}
]]>
+
+
+ Property value can be also used in include attributes (e.g. refid), for example:
+
+
+
+ ${prefix}Table
+
+
+
+ from
+
+
+
+
+ select
+ field1, field2, field3
+
+
+
+
+ ]]>
From fd1544643dffeb15a96a7ee8dc1cb95f73be6160 Mon Sep 17 00:00:00 2001
From: kmoco2am
Date: Mon, 2 Feb 2015 19:26:51 +0100
Subject: [PATCH 09/11] Doc update
---
src/site/xdoc/sqlmap-xml.xml | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/src/site/xdoc/sqlmap-xml.xml b/src/site/xdoc/sqlmap-xml.xml
index b93d5b02767..be67e12e1b2 100755
--- a/src/site/xdoc/sqlmap-xml.xml
+++ b/src/site/xdoc/sqlmap-xml.xml
@@ -485,8 +485,8 @@ ps.setInt(1,id);]]>
This element can be used to define a reusable fragment of SQL code that can be
- included in other statements. It can be statically (during load phase) parametrized and also included many time
- with different property values. For example:
+ included in other statements. It can be statically (during load phase) parametrized. Different property values can
+ vary in include instances. For example:
${alias}.id,${alias}.username,${alias}.password ]]>
@@ -496,13 +496,15 @@ ps.setInt(1,id);]]>
- select
- from some_table t
- where id = #{id}
+ select
+ ,
+
+ from some_table t1
+ cross join some_table t2
]]>
- Property value can be also used in include attributes (e.g. refid), for example:
+ Property value can be also used in include refid attribute or property values inside include clause, for example:
From aca6951b2f91ee5b775bd52c579fa51c70cb1da1 Mon Sep 17 00:00:00 2001
From: kmoco2am
Date: Fri, 27 Feb 2015 22:17:21 +0100
Subject: [PATCH 10/11] Reflecting feedback on pull request - simplifications,
imports fix, concrete exception used
---
.../builder/xml/XMLIncludeTransformer.java | 41 +++++++------------
1 file changed, 15 insertions(+), 26 deletions(-)
diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java b/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
index 6be81532386..ad56c2e0f9a 100755
--- a/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
+++ b/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
@@ -15,17 +15,19 @@
*/
package org.apache.ibatis.builder.xml;
+import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.builder.IncompleteElementException;
import org.apache.ibatis.builder.MapperBuilderAssistant;
-import org.apache.ibatis.parsing.GenericTokenParser;
import org.apache.ibatis.parsing.PropertyParser;
-import org.apache.ibatis.parsing.TokenHandler;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.session.Configuration;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
/**
* @author Frank D. Martinez [mnesarco]
@@ -63,15 +65,12 @@ private void applyIncludes(Node source, final Properties variablesContext) {
// replace variables in include refid value
refid = PropertyParser.parse(refid, variablesContext);
Node toInclude = findSqlFragment(refid);
- Properties newVariablesContext = getVariablesContext(source);
+ Properties newVariablesContext = getVariablesContext(source, variablesContext);
if (!newVariablesContext.isEmpty()) {
- // replace variables in variable values too
- for (Object name : newVariablesContext.keySet()) {
- newVariablesContext.put(name, PropertyParser.parse(newVariablesContext.get(name).toString(), variablesContext));
- }
- // merge new and inherited into new full one
- applyInheritedContext(newVariablesContext, variablesContext);
- fullContext = newVariablesContext;
+ // merge contexts
+ fullContext = new Properties();
+ fullContext.putAll(variablesContext);
+ fullContext.putAll(newVariablesContext);
} else {
// no new context - use inherited fully
fullContext = variablesContext;
@@ -113,25 +112,13 @@ private String getStringAttribute(Node node, String name) {
return node.getAttributes().getNamedItem(name).getNodeValue();
}
- /**
- * Add inherited context into newly created one.
- * @param newContext variables defined current include clause where inherited values will be placed
- * @param inheritedContext all inherited variables values
- */
- private void applyInheritedContext(Properties newContext, Properties inheritedContext) {
- for (Map.Entry e : inheritedContext.entrySet()) {
- if (!newContext.containsKey(e.getKey())) {
- newContext.put(e.getKey(), e.getValue());
- }
- }
- }
-
/**
* Read placholders and their values from include node definition.
* @param node Include node instance
+ * @param inheritedVariablesContext Current context used for replace variables in new variables values
* @return variables context from include instance (no inherited values)
*/
- private Properties getVariablesContext(Node node) {
+ private Properties getVariablesContext(Node node, Properties inheritedVariablesContext) {
List subElements = getSubElements(node);
if (subElements.isEmpty()) {
return new Properties();
@@ -140,10 +127,12 @@ private Properties getVariablesContext(Node node) {
for (Node variableValue : subElements) {
String name = getStringAttribute(variableValue, "name");
String value = getStringAttribute(variableValue, "value");
+ // Replace variables inside
+ value = PropertyParser.parse(value, inheritedVariablesContext);
// Push new value
Object originalValue = variablesContext.put(name, value);
if (originalValue != null) {
- throw new IllegalArgumentException("Variable " + name + " defined twice in the same include definition");
+ throw new BuilderException("Variable " + name + " defined twice in the same include definition");
}
}
return variablesContext;
From 4df0298a5e09eb578b1da0dcb2faa7749481b525 Mon Sep 17 00:00:00 2001
From: kmoco2am
Date: Fri, 27 Feb 2015 22:24:21 +0100
Subject: [PATCH 11/11] Reflecting feedback on pull request - merging two
methods into one (simplification)
---
.../builder/xml/XMLIncludeTransformer.java | 36 +++++--------------
1 file changed, 8 insertions(+), 28 deletions(-)
diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java b/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
index ad56c2e0f9a..cea66ae5fbd 100755
--- a/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
+++ b/src/main/java/org/apache/ibatis/builder/xml/XMLIncludeTransformer.java
@@ -24,9 +24,6 @@
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
import java.util.Properties;
/**
@@ -119,14 +116,13 @@ private String getStringAttribute(Node node, String name) {
* @return variables context from include instance (no inherited values)
*/
private Properties getVariablesContext(Node node, Properties inheritedVariablesContext) {
- List subElements = getSubElements(node);
- if (subElements.isEmpty()) {
- return new Properties();
- } else {
- Properties variablesContext = new Properties();
- for (Node variableValue : subElements) {
- String name = getStringAttribute(variableValue, "name");
- String value = getStringAttribute(variableValue, "value");
+ Properties variablesContext = new Properties();
+ NodeList children = node.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ Node n = children.item(i);
+ if (n.getNodeType() == Node.ELEMENT_NODE) {
+ String name = getStringAttribute(n, "name");
+ String value = getStringAttribute(n, "value");
// Replace variables inside
value = PropertyParser.parse(value, inheritedVariablesContext);
// Push new value
@@ -135,24 +131,8 @@ private Properties getVariablesContext(Node node, Properties inheritedVariablesC
throw new BuilderException("Variable " + name + " defined twice in the same include definition");
}
}
- return variablesContext;
- }
- }
-
- private List getSubElements(Node node) {
- NodeList children = node.getChildNodes();
- if (children.getLength() == 0) {
- return Collections.emptyList();
- } else {
- List elements = new ArrayList();
- for (int i = 0; i < children.getLength(); i++) {
- Node n = children.item(i);
- if (n.getNodeType() == Node.ELEMENT_NODE) {
- elements.add(n);
- }
- }
- return elements;
}
+ return variablesContext;
}
}