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} field1, field2, field3 - from + from + + + + - + + + 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 @@ @@ -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 + + + +]]> 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; } }