From 17a99302f2f6276f745723f08fa83087f335b2c9 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 17 Dec 2022 12:39:38 +0900 Subject: [PATCH 01/10] Added a test case for #2754 Also related to #206 #575 --- .../bind_in_foreach/BindInForeachTest.java | 61 +++++++++++++++++++ .../submitted/bind_in_foreach/Mapper.java | 29 +++++++++ .../submitted/bind_in_foreach/User.java | 38 ++++++++++++ .../submitted/bind_in_foreach/CreateDB.sql | 22 +++++++ .../submitted/bind_in_foreach/Mapper.xml | 33 ++++++++++ .../bind_in_foreach/mybatis-config.xml | 41 +++++++++++++ 6 files changed, 224 insertions(+) create mode 100644 src/test/java/org/apache/ibatis/submitted/bind_in_foreach/BindInForeachTest.java create mode 100644 src/test/java/org/apache/ibatis/submitted/bind_in_foreach/Mapper.java create mode 100644 src/test/java/org/apache/ibatis/submitted/bind_in_foreach/User.java create mode 100644 src/test/resources/org/apache/ibatis/submitted/bind_in_foreach/CreateDB.sql create mode 100644 src/test/resources/org/apache/ibatis/submitted/bind_in_foreach/Mapper.xml create mode 100644 src/test/resources/org/apache/ibatis/submitted/bind_in_foreach/mybatis-config.xml diff --git a/src/test/java/org/apache/ibatis/submitted/bind_in_foreach/BindInForeachTest.java b/src/test/java/org/apache/ibatis/submitted/bind_in_foreach/BindInForeachTest.java new file mode 100644 index 00000000000..ff6a3549de0 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/bind_in_foreach/BindInForeachTest.java @@ -0,0 +1,61 @@ +/* + * Copyright 2009-2022 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 + * + * https://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. + */ +package org.apache.ibatis.submitted.bind_in_foreach; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.Reader; +import java.util.List; + +import org.apache.ibatis.BaseDataTest; +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +class BindInForeachTest { + + private static SqlSessionFactory sqlSessionFactory; + + @BeforeAll + static void setUp() throws Exception { + try (Reader reader = Resources + .getResourceAsReader("org/apache/ibatis/submitted/bind_in_foreach/mybatis-config.xml")) { + sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); + } + BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), + "org/apache/ibatis/submitted/bind_in_foreach/CreateDB.sql"); + } + + @Test + void testBindInForeach() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + Mapper mapper = sqlSession.getMapper(Mapper.class); + assertEquals(3, mapper.createUsers(List.of(2, 4, 6))); + List users = mapper.selectUsers(); + assertEquals(3, users.size()); + assertEquals(1, users.get(0).getId()); + assertEquals("User2", users.get(0).getName()); + assertEquals(2, users.get(1).getId()); + assertEquals("User4", users.get(1).getName()); + assertEquals(3, users.get(2).getId()); + assertEquals("User6", users.get(2).getName()); + } + } + +} diff --git a/src/test/java/org/apache/ibatis/submitted/bind_in_foreach/Mapper.java b/src/test/java/org/apache/ibatis/submitted/bind_in_foreach/Mapper.java new file mode 100644 index 00000000000..fd4d914a1c4 --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/bind_in_foreach/Mapper.java @@ -0,0 +1,29 @@ +/* + * Copyright 2009-2022 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 + * + * https://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. + */ +package org.apache.ibatis.submitted.bind_in_foreach; + +import java.util.List; + +import org.apache.ibatis.annotations.Select; + +public interface Mapper { + + @Select("select * from users order by id") + List selectUsers(); + + int createUsers(List numbers); + +} diff --git a/src/test/java/org/apache/ibatis/submitted/bind_in_foreach/User.java b/src/test/java/org/apache/ibatis/submitted/bind_in_foreach/User.java new file mode 100644 index 00000000000..765338fb82a --- /dev/null +++ b/src/test/java/org/apache/ibatis/submitted/bind_in_foreach/User.java @@ -0,0 +1,38 @@ +/* + * Copyright 2009-2022 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 + * + * https://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. + */ +package org.apache.ibatis.submitted.bind_in_foreach; + +public class User { + + private Integer id; + private String name; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/test/resources/org/apache/ibatis/submitted/bind_in_foreach/CreateDB.sql b/src/test/resources/org/apache/ibatis/submitted/bind_in_foreach/CreateDB.sql new file mode 100644 index 00000000000..23d3d13e50b --- /dev/null +++ b/src/test/resources/org/apache/ibatis/submitted/bind_in_foreach/CreateDB.sql @@ -0,0 +1,22 @@ +-- +-- Copyright 2009-2022 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 +-- +-- https://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 users if exists; + +create table users ( + id int, + name varchar(20) +); diff --git a/src/test/resources/org/apache/ibatis/submitted/bind_in_foreach/Mapper.xml b/src/test/resources/org/apache/ibatis/submitted/bind_in_foreach/Mapper.xml new file mode 100644 index 00000000000..c735771b349 --- /dev/null +++ b/src/test/resources/org/apache/ibatis/submitted/bind_in_foreach/Mapper.xml @@ -0,0 +1,33 @@ + + + + + + + insert into users (id, name) values + + + + (#{id}, #{name}) + + + + diff --git a/src/test/resources/org/apache/ibatis/submitted/bind_in_foreach/mybatis-config.xml b/src/test/resources/org/apache/ibatis/submitted/bind_in_foreach/mybatis-config.xml new file mode 100644 index 00000000000..b694f3e402c --- /dev/null +++ b/src/test/resources/org/apache/ibatis/submitted/bind_in_foreach/mybatis-config.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + From 584990a347367c0d63c5b831cceee579c8568911 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 17 Dec 2022 12:07:35 +0900 Subject: [PATCH 02/10] When processing DynamicSqlSource, evaluate param values in scripting phase - Evaluated param values are stored in `ParameterMapping` and later used in DefaultParameterHandler - There is no change when processing RawSqlSource - Removed unused `injectionFilter` from TextSqlNode (gh-117) This should fix #2754 . This might also fix #206 and #575 , but with this patch, it still is not possible to invoke a method with parameter inside a parameter reference like `#{_parameter.mymethod(_parameter.value)}`. It might be possible to accept OGNL expression in a param reference (e.g. `#{${_parameter.mymethod(_parameter.value)}}`), but I'm not sure if that's a good idea. --- .../builder/ParameterMappingTokenHandler.java | 154 ++++++++++++++++++ .../ibatis/builder/SqlSourceBuilder.java | 118 +------------- .../apache/ibatis/executor/BaseExecutor.java | 4 +- .../ibatis/mapping/ParameterMapping.java | 17 ++ .../defaults/DefaultParameterHandler.java | 30 ++-- .../scripting/defaults/RawSqlSource.java | 26 +-- .../scripting/xmltags/DynamicContext.java | 63 ++++++- .../scripting/xmltags/DynamicSqlSource.java | 7 +- .../scripting/xmltags/ForEachSqlNode.java | 60 +++---- .../scripting/xmltags/StaticTextSqlNode.java | 2 +- .../ibatis/scripting/xmltags/TextSqlNode.java | 28 +--- .../ibatis/scripting/xmltags/TrimSqlNode.java | 20 +-- .../ibatis/builder/SqlSourceBuilderTest.java | 29 +--- .../xml/dynamic/DynamicSqlSourceTest.java | 33 +++- .../raw_sql_source/RawSqlSourceTest.java | 43 +++++ 15 files changed, 383 insertions(+), 251 deletions(-) create mode 100644 src/main/java/org/apache/ibatis/builder/ParameterMappingTokenHandler.java diff --git a/src/main/java/org/apache/ibatis/builder/ParameterMappingTokenHandler.java b/src/main/java/org/apache/ibatis/builder/ParameterMappingTokenHandler.java new file mode 100644 index 00000000000..08724361b7b --- /dev/null +++ b/src/main/java/org/apache/ibatis/builder/ParameterMappingTokenHandler.java @@ -0,0 +1,154 @@ +/* + * Copyright 2009-2022 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 + * + * https://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. + */ + +package org.apache.ibatis.builder; + +import java.util.List; +import java.util.Map; + +import org.apache.ibatis.mapping.ParameterMapping; +import org.apache.ibatis.mapping.ParameterMode; +import org.apache.ibatis.parsing.TokenHandler; +import org.apache.ibatis.reflection.MetaClass; +import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.reflection.property.PropertyTokenizer; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.type.JdbcType; + +public class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler { + + private static final String PARAMETER_PROPERTIES = "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName"; + private final List parameterMappings; + private final Class parameterType; + private final MetaObject metaParameters; + private final Object parameterObject; + private final boolean paramExists; + + public ParameterMappingTokenHandler(List parameterMappings, Configuration configuration, + Object parameterObject, Class parameterType, Map additionalParameters, boolean paramExists) { + super(configuration); + this.parameterType = parameterObject == null + ? (parameterType == null ? Object.class : parameterType) + : parameterObject.getClass(); + this.metaParameters = configuration.newMetaObject(additionalParameters); + this.parameterObject = parameterObject; + this.paramExists = paramExists; + this.parameterMappings = parameterMappings; + } + + public ParameterMappingTokenHandler(List parameterMappings, Configuration configuration, + Class parameterType, + Map additionalParameters) { + super(configuration); + this.parameterType = parameterType; + this.metaParameters = configuration.newMetaObject(additionalParameters); + this.parameterObject = null; + this.paramExists = false; + this.parameterMappings = parameterMappings; + } + + public List getParameterMappings() { + return parameterMappings; + } + + @Override + public String handleToken(String content) { + parameterMappings.add(buildParameterMapping(content)); + return "?"; + } + + private ParameterMapping buildParameterMapping(String content) { + Map propertiesMap = parseParameterMapping(content); + String property = propertiesMap.get("property"); + PropertyTokenizer propertyTokenizer = new PropertyTokenizer(property); + Class propertyType; + if (metaParameters.hasGetter(propertyTokenizer.getName())) { // issue #448 get type from additional params + propertyType = metaParameters.getGetterType(property); + } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) { + propertyType = parameterType; + } else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) { + propertyType = java.sql.ResultSet.class; + } else if (property == null || Map.class.isAssignableFrom(parameterType)) { + propertyType = Object.class; + } else { + MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory()); + if (metaClass.hasGetter(property)) { + propertyType = metaClass.getGetterType(property); + } else { + propertyType = Object.class; + } + } + ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType); + Class javaType = propertyType; + String typeHandlerAlias = null; + ParameterMode mode = null; + for (Map.Entry entry : propertiesMap.entrySet()) { + String name = entry.getKey(); + String value = entry.getValue(); + if ("javaType".equals(name)) { + javaType = resolveClass(value); + builder.javaType(javaType); + } else if ("jdbcType".equals(name)) { + builder.jdbcType(resolveJdbcType(value)); + } else if ("mode".equals(name)) { + mode = resolveParameterMode(value); + builder.mode(mode); + } else if ("numericScale".equals(name)) { + builder.numericScale(Integer.valueOf(value)); + } else if ("resultMap".equals(name)) { + builder.resultMapId(value); + } else if ("typeHandler".equals(name)) { + typeHandlerAlias = value; + } else if ("jdbcTypeName".equals(name)) { + builder.jdbcTypeName(value); + } else if ("property".equals(name)) { + // Do Nothing + } else if ("expression".equals(name)) { + throw new BuilderException("Expression based parameters are not supported yet"); + } else { + throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + + "}. Valid properties are " + PARAMETER_PROPERTIES); + } + } + if (typeHandlerAlias != null) { + builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias)); + } + if (!ParameterMode.OUT.equals(mode) && paramExists) { + if (metaParameters.hasGetter(propertyTokenizer.getName())) { + builder.value(metaParameters.getValue(property)); + } else if (parameterObject == null) { + builder.value(null); + } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { + builder.value(parameterObject); + } else { + MetaObject metaObject = configuration.newMetaObject(parameterObject); + builder.value(metaObject.getValue(property)); + } + } + return builder.build(); + } + + private Map parseParameterMapping(String content) { + try { + return new ParameterExpression(content); + } catch (BuilderException ex) { + throw ex; + } catch (Exception ex) { + throw new BuilderException("Parsing error was found in mapping #{" + content + + "}. Check syntax #{property|(expression), var1=value1, var2=value2, ...} ", ex); + } + } +} diff --git a/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java b/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java index 9c6b3fb2765..280447d6a5a 100644 --- a/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java @@ -15,41 +15,27 @@ */ package org.apache.ibatis.builder; -import java.util.ArrayList; import java.util.List; -import java.util.Map; import java.util.StringTokenizer; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.SqlSource; -import org.apache.ibatis.parsing.GenericTokenParser; -import org.apache.ibatis.parsing.TokenHandler; -import org.apache.ibatis.reflection.MetaClass; -import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.Configuration; -import org.apache.ibatis.type.JdbcType; /** * @author Clinton Begin */ -public class SqlSourceBuilder extends BaseBuilder { +public class SqlSourceBuilder { - private static final String PARAMETER_PROPERTIES = "javaType,jdbcType,mode,numericScale,resultMap,typeHandler,jdbcTypeName"; - - public SqlSourceBuilder(Configuration configuration) { - super(configuration); + private SqlSourceBuilder() { + super(); } - public SqlSource parse(String originalSql, Class parameterType, Map additionalParameters) { - ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters); - GenericTokenParser parser = new GenericTokenParser("#{", "}", handler); - String sql; - if (configuration.isShrinkWhitespacesInSql()) { - sql = parser.parse(removeExtraWhitespaces(originalSql)); - } else { - sql = parser.parse(originalSql); - } - return new StaticSqlSource(configuration, sql, handler.getParameterMappings()); + public static SqlSource buildSqlSource(Configuration configuration, String sql, + List parameterMappings) { + return new StaticSqlSource(configuration, + configuration.isShrinkWhitespacesInSql() ? SqlSourceBuilder.removeExtraWhitespaces(sql) : sql, + parameterMappings); } public static String removeExtraWhitespaces(String original) { @@ -66,92 +52,4 @@ public static String removeExtraWhitespaces(String original) { return builder.toString(); } - private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler { - - private final List parameterMappings = new ArrayList<>(); - private final Class parameterType; - private final MetaObject metaParameters; - - public ParameterMappingTokenHandler(Configuration configuration, Class parameterType, Map additionalParameters) { - super(configuration); - this.parameterType = parameterType; - this.metaParameters = configuration.newMetaObject(additionalParameters); - } - - public List getParameterMappings() { - return parameterMappings; - } - - @Override - public String handleToken(String content) { - parameterMappings.add(buildParameterMapping(content)); - return "?"; - } - - private ParameterMapping buildParameterMapping(String content) { - Map propertiesMap = parseParameterMapping(content); - String property = propertiesMap.get("property"); - Class propertyType; - if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params - propertyType = metaParameters.getGetterType(property); - } else if (typeHandlerRegistry.hasTypeHandler(parameterType)) { - propertyType = parameterType; - } else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) { - propertyType = java.sql.ResultSet.class; - } else if (property == null || Map.class.isAssignableFrom(parameterType)) { - propertyType = Object.class; - } else { - MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory()); - if (metaClass.hasGetter(property)) { - propertyType = metaClass.getGetterType(property); - } else { - propertyType = Object.class; - } - } - ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType); - Class javaType = propertyType; - String typeHandlerAlias = null; - for (Map.Entry entry : propertiesMap.entrySet()) { - String name = entry.getKey(); - String value = entry.getValue(); - if ("javaType".equals(name)) { - javaType = resolveClass(value); - builder.javaType(javaType); - } else if ("jdbcType".equals(name)) { - builder.jdbcType(resolveJdbcType(value)); - } else if ("mode".equals(name)) { - builder.mode(resolveParameterMode(value)); - } else if ("numericScale".equals(name)) { - builder.numericScale(Integer.valueOf(value)); - } else if ("resultMap".equals(name)) { - builder.resultMapId(value); - } else if ("typeHandler".equals(name)) { - typeHandlerAlias = value; - } else if ("jdbcTypeName".equals(name)) { - builder.jdbcTypeName(value); - } else if ("property".equals(name)) { - // Do Nothing - } else if ("expression".equals(name)) { - throw new BuilderException("Expression based parameters are not supported yet"); - } else { - throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}. Valid properties are " + PARAMETER_PROPERTIES); - } - } - if (typeHandlerAlias != null) { - builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias)); - } - return builder.build(); - } - - private Map parseParameterMapping(String content) { - try { - return new ParameterExpression(content); - } catch (BuilderException ex) { - throw ex; - } catch (Exception ex) { - throw new BuilderException("Parsing error was found in mapping #{" + content + "}. Check syntax #{property|(expression), var1=value1, var2=value2, ...} ", ex); - } - } - } - } diff --git a/src/main/java/org/apache/ibatis/executor/BaseExecutor.java b/src/main/java/org/apache/ibatis/executor/BaseExecutor.java index 834690abfd9..d2ae95c38f5 100644 --- a/src/main/java/org/apache/ibatis/executor/BaseExecutor.java +++ b/src/main/java/org/apache/ibatis/executor/BaseExecutor.java @@ -208,7 +208,9 @@ public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBo if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); - if (boundSql.hasAdditionalParameter(propertyName)) { + if (parameterMapping.hasValue()) { + value = parameterMapping.getValue(); + } else if (boundSql.hasAdditionalParameter(propertyName)) { value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; diff --git a/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java b/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java index 94abab3bbb4..612cbdc4286 100644 --- a/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java +++ b/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java @@ -38,6 +38,8 @@ public class ParameterMapping { private String resultMapId; private String jdbcTypeName; private String expression; + private Object value; + private boolean hasValue; private ParameterMapping() { } @@ -99,6 +101,12 @@ public Builder expression(String expression) { return this; } + public Builder value(Object value) { + parameterMapping.value = value; + parameterMapping.hasValue = true; + return this; + } + public ParameterMapping build() { resolveTypeHandler(); validate(); @@ -207,6 +215,14 @@ public String getExpression() { return expression; } + public Object getValue() { + return value; + } + + public boolean hasValue() { + return hasValue; + } + @Override public String toString() { final StringBuilder sb = new StringBuilder("ParameterMapping{"); @@ -220,6 +236,7 @@ public String toString() { sb.append(", resultMapId='").append(resultMapId).append('\''); sb.append(", jdbcTypeName='").append(jdbcTypeName).append('\''); sb.append(", expression='").append(expression).append('\''); + sb.append(", value='").append(value).append('\''); sb.append('}'); return sb.toString(); } diff --git a/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java b/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java index a9f5a91a0b8..f2256201422 100644 --- a/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java +++ b/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java @@ -66,18 +66,7 @@ public void setParameters(PreparedStatement ps) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { - Object value; - String propertyName = parameterMapping.getProperty(); - if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params - value = boundSql.getAdditionalParameter(propertyName); - } else if (parameterObject == null) { - value = null; - } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { - value = parameterObject; - } else { - MetaObject metaObject = configuration.newMetaObject(parameterObject); - value = metaObject.getValue(propertyName); - } + Object value = getValue(parameterMapping); TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { @@ -93,4 +82,21 @@ public void setParameters(PreparedStatement ps) { } } + private Object getValue(ParameterMapping parameterMapping) { + if (parameterMapping.hasValue()) { + return parameterMapping.getValue(); + } + String propertyName = parameterMapping.getProperty(); + if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params + return boundSql.getAdditionalParameter(propertyName); + } else if (parameterObject == null) { + return null; + } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { + return parameterObject; + } else { + MetaObject metaObject = configuration.newMetaObject(parameterObject); + return metaObject.getValue(propertyName); + } + } + } diff --git a/src/main/java/org/apache/ibatis/scripting/defaults/RawSqlSource.java b/src/main/java/org/apache/ibatis/scripting/defaults/RawSqlSource.java index 556712e5501..ce21535c2df 100644 --- a/src/main/java/org/apache/ibatis/scripting/defaults/RawSqlSource.java +++ b/src/main/java/org/apache/ibatis/scripting/defaults/RawSqlSource.java @@ -15,19 +15,23 @@ */ package org.apache.ibatis.scripting.defaults; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; +import org.apache.ibatis.builder.ParameterMappingTokenHandler; import org.apache.ibatis.builder.SqlSourceBuilder; import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.SqlSource; +import org.apache.ibatis.parsing.GenericTokenParser; import org.apache.ibatis.scripting.xmltags.DynamicContext; import org.apache.ibatis.scripting.xmltags.DynamicSqlSource; import org.apache.ibatis.scripting.xmltags.SqlNode; import org.apache.ibatis.session.Configuration; /** - * Static SqlSource. It is faster than {@link DynamicSqlSource} because mappings are - * calculated during startup. + * Static SqlSource. It is faster than {@link DynamicSqlSource} because mappings are calculated during startup. * * @since 3.2.0 * @author Eduardo Macarron @@ -37,19 +41,19 @@ public class RawSqlSource implements SqlSource { private final SqlSource sqlSource; public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class parameterType) { - this(configuration, getSql(configuration, rootSqlNode), parameterType); + DynamicContext context = new DynamicContext(configuration, parameterType); + rootSqlNode.apply(context); + String sql = context.getSql(); + sqlSource = SqlSourceBuilder.buildSqlSource(configuration, sql, context.getParameterMappings()); } public RawSqlSource(Configuration configuration, String sql, Class parameterType) { - SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class clazz = parameterType == null ? Object.class : parameterType; - sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>()); - } - - private static String getSql(Configuration configuration, SqlNode rootSqlNode) { - DynamicContext context = new DynamicContext(configuration, null); - rootSqlNode.apply(context); - return context.getSql(); + List parameterMappings = new ArrayList<>(); + ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler(parameterMappings, configuration, + clazz, new HashMap<>()); + GenericTokenParser parser = new GenericTokenParser("#{", "}", tokenHandler); + sqlSource = SqlSourceBuilder.buildSqlSource(configuration, parser.parse(sql), parameterMappings); } @Override diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java index 43d84d96729..74cda88830f 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java @@ -15,17 +15,22 @@ */ package org.apache.ibatis.scripting.xmltags; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.StringJoiner; +import org.apache.ibatis.builder.ParameterMappingTokenHandler; +import org.apache.ibatis.mapping.ParameterMapping; +import org.apache.ibatis.parsing.GenericTokenParser; +import org.apache.ibatis.reflection.MetaObject; +import org.apache.ibatis.session.Configuration; + import ognl.OgnlContext; import ognl.OgnlRuntime; import ognl.PropertyAccessor; -import org.apache.ibatis.reflection.MetaObject; -import org.apache.ibatis.session.Configuration; - /** * @author Clinton Begin */ @@ -38,20 +43,36 @@ public class DynamicContext { OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor()); } - private final ContextMap bindings; + protected final ContextMap bindings; private final StringJoiner sqlBuilder = new StringJoiner(" "); private int uniqueNumber = 0; - public DynamicContext(Configuration configuration, Object parameterObject) { - if (parameterObject != null && !(parameterObject instanceof Map)) { + private final Configuration configuration; + private final Object parameterObject; + private final Class parameterType; + private final boolean paramExists; + + private GenericTokenParser tokenParser; + private ParameterMappingTokenHandler tokenHandler; + + public DynamicContext(Configuration configuration, Class parameterType) { + this(configuration, null, parameterType, false); + } + + public DynamicContext(Configuration configuration, Object parameterObject, Class parameterType, boolean paramExists) { + if (parameterObject == null || parameterObject instanceof Map) { + bindings = new ContextMap(null, false); + } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); boolean existsTypeHandler = configuration.getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass()); bindings = new ContextMap(metaObject, existsTypeHandler); - } else { - bindings = new ContextMap(null, false); } bindings.put(PARAMETER_OBJECT_KEY, parameterObject); bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId()); + this.configuration = configuration; + this.parameterObject = parameterObject; + this.paramExists = paramExists; + this.parameterType = parameterType; } public Map getBindings() { @@ -74,6 +95,30 @@ public int getUniqueNumber() { return uniqueNumber++; } + public List getParameterMappings() { + return tokenHandler == null ? new ArrayList<>() : tokenHandler.getParameterMappings(); + } + + protected String parseParam(String sql) { + if (tokenParser == null) { + tokenHandler = new ParameterMappingTokenHandler(getParameterMappings(), configuration, parameterObject, parameterType, bindings, paramExists); + tokenParser = new GenericTokenParser("#{", "}", tokenHandler); + } + return tokenParser.parse(sql); + } + + protected Object getParameterObject() { + return parameterObject; + } + + protected Class getParameterType() { + return parameterType; + } + + protected boolean isParamExists() { + return paramExists; + } + static class ContextMap extends HashMap { private static final long serialVersionUID = 2977601501966151582L; private final MetaObject parameterMetaObject; @@ -117,7 +162,7 @@ public Object getProperty(Map context, Object target, Object name) { Object parameterObject = map.get(PARAMETER_OBJECT_KEY); if (parameterObject instanceof Map) { - return ((Map)parameterObject).get(name); + return ((Map) parameterObject).get(name); } return null; diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicSqlSource.java b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicSqlSource.java index fce0bfa5396..03ab703b2b7 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicSqlSource.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicSqlSource.java @@ -35,11 +35,10 @@ public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) { @Override public BoundSql getBoundSql(Object parameterObject) { - DynamicContext context = new DynamicContext(configuration, parameterObject); + DynamicContext context = new DynamicContext(configuration, parameterObject, null, true); rootSqlNode.apply(context); - SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); - Class parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); - SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); + String sql = context.getSql(); + SqlSource sqlSource = SqlSourceBuilder.buildSqlSource(configuration, sql, context.getParameterMappings()); BoundSql boundSql = sqlSource.getBoundSql(parameterObject); context.getBindings().forEach(boundSql::setAdditionalParameter); return boundSql; 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 1c7ebbb5f1b..0bf0959d996 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNode.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNode.java @@ -15,9 +15,11 @@ */ package org.apache.ibatis.scripting.xmltags; +import java.util.List; import java.util.Map; import java.util.Optional; +import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.parsing.GenericTokenParser; import org.apache.ibatis.session.Configuration; @@ -74,33 +76,30 @@ public boolean apply(DynamicContext context) { applyOpen(context); int i = 0; for (Object o : iterable) { - DynamicContext oldContext = context; + DynamicContext scopedContext; if (first || separator == null) { - context = new PrefixedContext(context, ""); + scopedContext = new PrefixedContext(context, ""); } else { - context = new PrefixedContext(context, separator); + scopedContext = new PrefixedContext(context, separator); } - int uniqueNumber = context.getUniqueNumber(); + int uniqueNumber = scopedContext.getUniqueNumber(); // Issue #709 if (o instanceof Map.Entry) { @SuppressWarnings("unchecked") Map.Entry mapEntry = (Map.Entry) o; - applyIndex(context, mapEntry.getKey(), uniqueNumber); - applyItem(context, mapEntry.getValue(), uniqueNumber); + applyIndex(scopedContext, mapEntry.getKey(), uniqueNumber); + applyItem(scopedContext, mapEntry.getValue(), uniqueNumber); } else { - applyIndex(context, i, uniqueNumber); - applyItem(context, o, uniqueNumber); + applyIndex(scopedContext, i, uniqueNumber); + applyItem(scopedContext, o, uniqueNumber); } - contents.apply(new FilteredDynamicContext(configuration, context, index, item, uniqueNumber)); + contents.apply(new FilteredDynamicContext(configuration, scopedContext, index, item, uniqueNumber)); if (first) { - first = !((PrefixedContext) context).isPrefixApplied(); + first = !((PrefixedContext) scopedContext).isPrefixApplied(); } - context = oldContext; i++; } applyClose(context); - context.getBindings().remove(item); - context.getBindings().remove(index); return true; } @@ -141,21 +140,12 @@ private static class FilteredDynamicContext extends DynamicContext { private final String item; public FilteredDynamicContext(Configuration configuration,DynamicContext delegate, String itemIndex, String item, int i) { - super(configuration, null); + super(configuration, delegate.getParameterObject(), delegate.getParameterType(), delegate.isParamExists()); this.delegate = delegate; this.index = i; this.itemIndex = itemIndex; this.item = item; - } - - @Override - public Map getBindings() { - return delegate.getBindings(); - } - - @Override - public void bind(String name, Object value) { - delegate.bind(name, value); + this.bindings.putAll(delegate.getBindings()); } @Override @@ -181,6 +171,10 @@ public int getUniqueNumber() { return delegate.getUniqueNumber(); } + @Override + public List getParameterMappings() { + return delegate.getParameterMappings(); + } } @@ -190,26 +184,17 @@ private class PrefixedContext extends DynamicContext { private boolean prefixApplied; public PrefixedContext(DynamicContext delegate, String prefix) { - super(configuration, null); + super(configuration, delegate.getParameterObject(), delegate.getParameterType(), delegate.isParamExists()); this.delegate = delegate; this.prefix = prefix; this.prefixApplied = false; + this.bindings.putAll(delegate.getBindings()); } public boolean isPrefixApplied() { return prefixApplied; } - @Override - public Map getBindings() { - return delegate.getBindings(); - } - - @Override - public void bind(String name, Object value) { - delegate.bind(name, value); - } - @Override public void appendSql(String sql) { if (!prefixApplied && sql != null && sql.trim().length() > 0) { @@ -228,6 +213,11 @@ public String getSql() { public int getUniqueNumber() { return delegate.getUniqueNumber(); } + + @Override + public List getParameterMappings() { + return delegate.getParameterMappings(); + } } } diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNode.java b/src/main/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNode.java index 9a938d5d0f2..e1e9f504350 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNode.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNode.java @@ -27,7 +27,7 @@ public StaticTextSqlNode(String text) { @Override public boolean apply(DynamicContext context) { - context.appendSql(text); + context.appendSql(context.parseParam(text)); return true; } diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/TextSqlNode.java b/src/main/java/org/apache/ibatis/scripting/xmltags/TextSqlNode.java index bce1ab1e263..5bcc749223b 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/TextSqlNode.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/TextSqlNode.java @@ -15,11 +15,8 @@ */ package org.apache.ibatis.scripting.xmltags; -import java.util.regex.Pattern; - import org.apache.ibatis.parsing.GenericTokenParser; import org.apache.ibatis.parsing.TokenHandler; -import org.apache.ibatis.scripting.ScriptingException; import org.apache.ibatis.type.SimpleTypeRegistry; /** @@ -27,15 +24,9 @@ */ public class TextSqlNode implements SqlNode { private final String text; - private final Pattern injectionFilter; public TextSqlNode(String text) { - this(text, null); - } - - public TextSqlNode(String text, Pattern injectionFilter) { this.text = text; - this.injectionFilter = injectionFilter; } public boolean isDynamic() { @@ -47,8 +38,8 @@ public boolean isDynamic() { @Override public boolean apply(DynamicContext context) { - GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter)); - context.appendSql(parser.parse(text)); + GenericTokenParser parser = createParser(new BindingTokenParser(context)); + context.appendSql(context.parseParam(parser.parse(text))); return true; } @@ -59,11 +50,9 @@ private GenericTokenParser createParser(TokenHandler handler) { private static class BindingTokenParser implements TokenHandler { private DynamicContext context; - private Pattern injectionFilter; - public BindingTokenParser(DynamicContext context, Pattern injectionFilter) { + public BindingTokenParser(DynamicContext context) { this.context = context; - this.injectionFilter = injectionFilter; } @Override @@ -75,15 +64,8 @@ public String handleToken(String content) { context.getBindings().put("value", parameter); } Object value = OgnlCache.getValue(content, context.getBindings()); - String srtValue = value == null ? "" : String.valueOf(value); // issue #274 return "" instead of "null" - checkInjection(srtValue); - return srtValue; - } - - private void checkInjection(String value) { - if (injectionFilter != null && !injectionFilter.matcher(value).matches()) { - throw new ScriptingException("Invalid input. Please conform to regex" + injectionFilter.pattern()); - } + // issue #274 return "" instead of "null" + return value == null ? "" : String.valueOf(value); } } diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/TrimSqlNode.java b/src/main/java/org/apache/ibatis/scripting/xmltags/TrimSqlNode.java index 7860214ee15..b6b720577c3 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/TrimSqlNode.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/TrimSqlNode.java @@ -19,9 +19,9 @@ import java.util.Collections; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.StringTokenizer; +import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.session.Configuration; /** @@ -76,11 +76,12 @@ private class FilteredDynamicContext extends DynamicContext { private StringBuilder sqlBuffer; public FilteredDynamicContext(DynamicContext delegate) { - super(configuration, null); + super(configuration, delegate.getParameterObject(), delegate.getParameterType(), delegate.isParamExists()); this.delegate = delegate; this.prefixApplied = false; this.suffixApplied = false; this.sqlBuffer = new StringBuilder(); + this.bindings.putAll(delegate.getBindings()); } public void applyAll() { @@ -93,16 +94,6 @@ public void applyAll() { delegate.appendSql(sqlBuffer.toString()); } - @Override - public Map getBindings() { - return delegate.getBindings(); - } - - @Override - public void bind(String name, Object value) { - delegate.bind(name, value); - } - @Override public int getUniqueNumber() { return delegate.getUniqueNumber(); @@ -118,6 +109,11 @@ public String getSql() { return delegate.getSql(); } + @Override + public List getParameterMappings() { + return delegate.getParameterMappings(); + } + private void applyPrefix(StringBuilder sql, String trimmedUppercaseSql) { if (!prefixApplied) { prefixApplied = true; diff --git a/src/test/java/org/apache/ibatis/builder/SqlSourceBuilderTest.java b/src/test/java/org/apache/ibatis/builder/SqlSourceBuilderTest.java index 871e2b88a00..12f48ce8b7e 100644 --- a/src/test/java/org/apache/ibatis/builder/SqlSourceBuilderTest.java +++ b/src/test/java/org/apache/ibatis/builder/SqlSourceBuilderTest.java @@ -15,41 +15,16 @@ */ package org.apache.ibatis.builder; -import org.apache.ibatis.mapping.BoundSql; -import org.apache.ibatis.mapping.SqlSource; -import org.apache.ibatis.session.Configuration; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -public class SqlSourceBuilderTest { +class SqlSourceBuilderTest { - private static Configuration configuration; - private static SqlSourceBuilder sqlSourceBuilder; private final String sqlFromXml = "\t\n\n SELECT * \n FROM user\n \t WHERE user_id = 1\n\t "; - @BeforeEach - void setUp() { - configuration = new Configuration(); - - sqlSourceBuilder = new SqlSourceBuilder(configuration); - } - - @Test - void testShrinkWhitespacesInSqlIsFalse() { - SqlSource sqlSource = sqlSourceBuilder.parse(sqlFromXml, null, null); - BoundSql boundSql = sqlSource.getBoundSql(null); - String actual = boundSql.getSql(); - Assertions.assertEquals(sqlFromXml, actual); - } - @Test void testShrinkWhitespacesInSqlIsTrue() { - configuration.setShrinkWhitespacesInSql(true); - SqlSource sqlSource = sqlSourceBuilder.parse(sqlFromXml, null, null); - BoundSql boundSql = sqlSource.getBoundSql(null); - String actual = boundSql.getSql(); - + String actual = SqlSourceBuilder.removeExtraWhitespaces(sqlFromXml); String shrankWhitespacesInSql = "SELECT * FROM user WHERE user_id = 1"; Assertions.assertEquals(shrankWhitespacesInSql, actual); } 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 c2c6033179a..a68e0ac5ff6 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 @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import org.apache.ibatis.BaseDataTest; import org.apache.ibatis.io.Resources; @@ -36,6 +37,7 @@ import org.apache.ibatis.scripting.xmltags.MixedSqlNode; import org.apache.ibatis.scripting.xmltags.SetSqlNode; import org.apache.ibatis.scripting.xmltags.SqlNode; +import org.apache.ibatis.scripting.xmltags.StaticTextSqlNode; import org.apache.ibatis.scripting.xmltags.TextSqlNode; import org.apache.ibatis.scripting.xmltags.WhereSqlNode; import org.apache.ibatis.session.Configuration; @@ -43,6 +45,9 @@ import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; class DynamicSqlSourceTest extends BaseDataTest { @@ -324,9 +329,9 @@ void shouldIterateOnceForEachItemInCollection() throws Exception { BoundSql boundSql = source.getBoundSql(parameterObject); assertEquals(expected, boundSql.getSql()); assertEquals(3, boundSql.getParameterMappings().size()); - assertEquals("__frch_item_0", boundSql.getParameterMappings().get(0).getProperty()); - assertEquals("__frch_item_1", boundSql.getParameterMappings().get(1).getProperty()); - assertEquals("__frch_item_2", boundSql.getParameterMappings().get(2).getProperty()); + assertEquals("item", boundSql.getParameterMappings().get(0).getProperty()); + assertEquals("item", boundSql.getParameterMappings().get(1).getProperty()); + assertEquals("item", boundSql.getParameterMappings().get(2).getProperty()); } @Test @@ -371,9 +376,9 @@ void shouldPerformStrictMatchOnForEachVariableSubstitution() throws Exception { BoundSql boundSql = source.getBoundSql(param); assertEquals(4, boundSql.getParameterMappings().size()); assertEquals("uuu.u", boundSql.getParameterMappings().get(0).getProperty()); - assertEquals("__frch_u_0.id", boundSql.getParameterMappings().get(1).getProperty()); - assertEquals("__frch_u_0", boundSql.getParameterMappings().get(2).getProperty()); - assertEquals("__frch_u_0", boundSql.getParameterMappings().get(3).getProperty()); + assertEquals("u.id", boundSql.getParameterMappings().get(1).getProperty()); + assertEquals("u", boundSql.getParameterMappings().get(2).getProperty()); + assertEquals("u", boundSql.getParameterMappings().get(3).getProperty()); } private DynamicSqlSource createDynamicSqlSource(SqlNode... contents) throws IOException, SQLException { @@ -412,4 +417,20 @@ public void setId(String property) { } } + @MethodSource + @ParameterizedTest + void testShrinkWhitespacesInSql(SqlNode input, boolean shrinkWhitespaces, String expected) { + Configuration config = new Configuration(); + config.setShrinkWhitespacesInSql(shrinkWhitespaces); + String actual = new DynamicSqlSource(config, input).getBoundSql(null).getSql(); + assertEquals(expected, actual); + } + + static Stream testShrinkWhitespacesInSql() { + return Stream.of( + Arguments.arguments(new StaticTextSqlNode("\t\n\n SELECT * \n FROM user\n \t WHERE user_id = 1\n\t "), false, + "SELECT * \n FROM user\n \t WHERE user_id = 1"), + Arguments.arguments(new StaticTextSqlNode("\t\n\n SELECT * \n FROM user\n \t WHERE user_id = 1\n\t"), true, + "SELECT * FROM user WHERE user_id = 1")); + } } diff --git a/src/test/java/org/apache/ibatis/submitted/raw_sql_source/RawSqlSourceTest.java b/src/test/java/org/apache/ibatis/submitted/raw_sql_source/RawSqlSourceTest.java index 4e6572267ab..3a312013c74 100644 --- a/src/test/java/org/apache/ibatis/submitted/raw_sql_source/RawSqlSourceTest.java +++ b/src/test/java/org/apache/ibatis/submitted/raw_sql_source/RawSqlSourceTest.java @@ -15,19 +15,28 @@ */ package org.apache.ibatis.submitted.raw_sql_source; +import static org.junit.jupiter.api.Assertions.*; + import java.io.Reader; +import java.util.stream.Stream; import org.apache.ibatis.BaseDataTest; import org.apache.ibatis.io.Resources; import org.apache.ibatis.mapping.SqlSource; import org.apache.ibatis.scripting.defaults.RawSqlSource; import org.apache.ibatis.scripting.xmltags.DynamicSqlSource; +import org.apache.ibatis.scripting.xmltags.SqlNode; +import org.apache.ibatis.scripting.xmltags.StaticTextSqlNode; +import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; class RawSqlSourceTest { @@ -72,4 +81,38 @@ private void test(String statement, Class sqlSource) { } } + @MethodSource + @ParameterizedTest + void testShrinkWhitespacesInSql(String input, boolean shrinkWhitespaces, String expected) { + Configuration config = new Configuration(); + config.setShrinkWhitespacesInSql(shrinkWhitespaces); + String actual = new RawSqlSource(config, input, null).getBoundSql(null).getSql(); + assertEquals(expected, actual); + } + + static Stream testShrinkWhitespacesInSql() { + return Stream.of( + Arguments.arguments("\t\n\n SELECT * \n FROM user\n \t WHERE user_id = 1\n\t ", false, + "\t\n\n SELECT * \n FROM user\n \t WHERE user_id = 1\n\t "), + Arguments.arguments("\t\n\n SELECT * \n FROM user\n \t WHERE user_id = 1\n\t", true, + "SELECT * FROM user WHERE user_id = 1")); + } + + @MethodSource + @ParameterizedTest + void testShrinkWhitespacesInSql_SqlNode(SqlNode input, boolean shrinkWhitespaces, String expected) { + Configuration config = new Configuration(); + config.setShrinkWhitespacesInSql(shrinkWhitespaces); + String actual = new RawSqlSource(config, input, null).getBoundSql(null).getSql(); + assertEquals(expected, actual); + } + + static Stream testShrinkWhitespacesInSql_SqlNode() { + return Stream.of( + Arguments.arguments( + new StaticTextSqlNode("\t\n\n SELECT * \n FROM user\n \t WHERE user_id = 1\n\t "), false, + "SELECT * \n FROM user\n \t WHERE user_id = 1"), + Arguments.arguments(new StaticTextSqlNode("\t\n\n SELECT * \n FROM user\n \t WHERE user_id = 1\n\t"), true, + "SELECT * FROM user WHERE user_id = 1")); + } } From e7f76227fcab374e4e0e5b6fef32430212039b39 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 17 Dec 2022 12:51:02 +0900 Subject: [PATCH 03/10] Stop rewriting item/index variables inside foreach Although it is not the goal, this could improve performance when there are many foreach iterations. --- .../scripting/xmltags/DynamicContext.java | 5 -- .../scripting/xmltags/ForEachSqlNode.java | 81 +++---------------- .../ibatis/scripting/xmltags/TrimSqlNode.java | 5 -- 3 files changed, 12 insertions(+), 79 deletions(-) diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java index 74cda88830f..cd4fb9ba11f 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java @@ -45,7 +45,6 @@ public class DynamicContext { protected final ContextMap bindings; private final StringJoiner sqlBuilder = new StringJoiner(" "); - private int uniqueNumber = 0; private final Configuration configuration; private final Object parameterObject; @@ -91,10 +90,6 @@ public String getSql() { return sqlBuilder.toString().trim(); } - public int getUniqueNumber() { - return uniqueNumber++; - } - public List getParameterMappings() { return tokenHandler == null ? new ArrayList<>() : tokenHandler.getParameterMappings(); } 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 0bf0959d996..066bfb6da3c 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNode.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNode.java @@ -20,14 +20,12 @@ import java.util.Optional; import org.apache.ibatis.mapping.ParameterMapping; -import org.apache.ibatis.parsing.GenericTokenParser; import org.apache.ibatis.session.Configuration; /** * @author Clinton Begin */ public class ForEachSqlNode implements SqlNode { - public static final String ITEM_PREFIX = "__frch_"; private final ExpressionEvaluator evaluator; private final String collectionExpression; @@ -44,14 +42,16 @@ public class ForEachSqlNode implements SqlNode { * @deprecated Since 3.5.9, use the {@link #ForEachSqlNode(Configuration, SqlNode, String, Boolean, String, String, String, String, String)}. */ @Deprecated - public ForEachSqlNode(Configuration configuration, SqlNode contents, String collectionExpression, String index, String item, String open, String close, String separator) { + public ForEachSqlNode(Configuration configuration, SqlNode contents, String collectionExpression, String index, + String item, String open, String close, String separator) { this(configuration, contents, collectionExpression, null, index, item, open, close, separator); } /** * @since 3.5.9 */ - public ForEachSqlNode(Configuration configuration, SqlNode contents, String collectionExpression, Boolean nullable, String index, String item, String open, String close, String separator) { + public ForEachSqlNode(Configuration configuration, SqlNode contents, String collectionExpression, Boolean nullable, + String index, String item, String open, String close, String separator) { this.evaluator = new ExpressionEvaluator(); this.collectionExpression = collectionExpression; this.nullable = nullable; @@ -68,7 +68,7 @@ public ForEachSqlNode(Configuration configuration, SqlNode contents, String coll public boolean apply(DynamicContext context) { Map bindings = context.getBindings(); final Iterable iterable = evaluator.evaluateIterable(collectionExpression, bindings, - Optional.ofNullable(nullable).orElseGet(configuration::isNullableOnForEach)); + Optional.ofNullable(nullable).orElseGet(configuration::isNullableOnForEach)); if (iterable == null || !iterable.iterator().hasNext()) { return true; } @@ -82,18 +82,17 @@ public boolean apply(DynamicContext context) { } else { scopedContext = new PrefixedContext(context, separator); } - int uniqueNumber = scopedContext.getUniqueNumber(); // Issue #709 if (o instanceof Map.Entry) { @SuppressWarnings("unchecked") Map.Entry mapEntry = (Map.Entry) o; - applyIndex(scopedContext, mapEntry.getKey(), uniqueNumber); - applyItem(scopedContext, mapEntry.getValue(), uniqueNumber); + applyIndex(scopedContext, mapEntry.getKey()); + applyItem(scopedContext, mapEntry.getValue()); } else { - applyIndex(scopedContext, i, uniqueNumber); - applyItem(scopedContext, o, uniqueNumber); + applyIndex(scopedContext, i); + applyItem(scopedContext, o); } - contents.apply(new FilteredDynamicContext(configuration, scopedContext, index, item, uniqueNumber)); + contents.apply(scopedContext); if (first) { first = !((PrefixedContext) scopedContext).isPrefixApplied(); } @@ -103,17 +102,15 @@ public boolean apply(DynamicContext context) { return true; } - private void applyIndex(DynamicContext context, Object o, int i) { + private void applyIndex(DynamicContext context, Object o) { if (index != null) { context.bind(index, o); - context.bind(itemizeItem(index, i), o); } } - private void applyItem(DynamicContext context, Object o, int i) { + private void applyItem(DynamicContext context, Object o) { if (item != null) { context.bind(item, o); - context.bind(itemizeItem(item, i), o); } } @@ -129,55 +126,6 @@ private void applyClose(DynamicContext context) { } } - private static String itemizeItem(String item, int i) { - return ITEM_PREFIX + item + "_" + i; - } - - private static class FilteredDynamicContext extends DynamicContext { - private final DynamicContext delegate; - private final int index; - private final String itemIndex; - private final String item; - - public FilteredDynamicContext(Configuration configuration,DynamicContext delegate, String itemIndex, String item, int i) { - super(configuration, delegate.getParameterObject(), delegate.getParameterType(), delegate.isParamExists()); - this.delegate = delegate; - this.index = i; - this.itemIndex = itemIndex; - this.item = item; - this.bindings.putAll(delegate.getBindings()); - } - - @Override - public String getSql() { - return delegate.getSql(); - } - - @Override - public void appendSql(String sql) { - GenericTokenParser parser = new GenericTokenParser("#{", "}", content -> { - String newContent = content.replaceFirst("^\\s*" + item + "(?![^.,:\\s])", itemizeItem(item, index)); - if (itemIndex != null && newContent.equals(content)) { - newContent = content.replaceFirst("^\\s*" + itemIndex + "(?![^.,:\\s])", itemizeItem(itemIndex, index)); - } - return "#{" + newContent + "}"; - }); - - delegate.appendSql(parser.parse(sql)); - } - - @Override - public int getUniqueNumber() { - return delegate.getUniqueNumber(); - } - - @Override - public List getParameterMappings() { - return delegate.getParameterMappings(); - } - } - - private class PrefixedContext extends DynamicContext { private final DynamicContext delegate; private final String prefix; @@ -209,11 +157,6 @@ public String getSql() { return delegate.getSql(); } - @Override - public int getUniqueNumber() { - return delegate.getUniqueNumber(); - } - @Override public List getParameterMappings() { return delegate.getParameterMappings(); diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/TrimSqlNode.java b/src/main/java/org/apache/ibatis/scripting/xmltags/TrimSqlNode.java index b6b720577c3..55017f87963 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/TrimSqlNode.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/TrimSqlNode.java @@ -94,11 +94,6 @@ public void applyAll() { delegate.appendSql(sqlBuffer.toString()); } - @Override - public int getUniqueNumber() { - return delegate.getUniqueNumber(); - } - @Override public void appendSql(String sql) { sqlBuffer.append(sql); From e8a476ffbf6f1f513ebc59e4aa015778cc988331 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Thu, 2 Jan 2025 18:15:28 +0900 Subject: [PATCH 04/10] wip --- .../builder/ParameterMappingTokenHandler.java | 9 +++------ .../apache/ibatis/builder/SqlSourceBuilder.java | 2 +- .../org/apache/ibatis/executor/BaseExecutor.java | 2 +- .../apache/ibatis/mapping/ParameterMapping.java | 2 +- .../defaults/DefaultParameterHandler.java | 2 +- .../ibatis/scripting/defaults/RawSqlSource.java | 2 +- .../ibatis/scripting/xmltags/DynamicContext.java | 16 +++++++++------- .../scripting/xmltags/StaticTextSqlNode.java | 2 +- .../ibatis/scripting/xmltags/TextSqlNode.java | 4 ++-- .../ibatis/scripting/xmltags/TrimSqlNode.java | 2 +- .../ibatis/builder/SqlSourceBuilderTest.java | 2 +- .../xml/dynamic/DynamicSqlSourceTest.java | 5 +++-- .../bind_in_foreach/BindInForeachTest.java | 4 ++-- .../raw_sql_source/RawSqlSourceTest.java | 4 ++-- 14 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/apache/ibatis/builder/ParameterMappingTokenHandler.java b/src/main/java/org/apache/ibatis/builder/ParameterMappingTokenHandler.java index 08724361b7b..83b76279c8a 100644 --- a/src/main/java/org/apache/ibatis/builder/ParameterMappingTokenHandler.java +++ b/src/main/java/org/apache/ibatis/builder/ParameterMappingTokenHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2022 the original author or authors. + * Copyright 2009-2025 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. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.ibatis.builder; import java.util.List; @@ -40,8 +39,7 @@ public class ParameterMappingTokenHandler extends BaseBuilder implements TokenHa public ParameterMappingTokenHandler(List parameterMappings, Configuration configuration, Object parameterObject, Class parameterType, Map additionalParameters, boolean paramExists) { super(configuration); - this.parameterType = parameterObject == null - ? (parameterType == null ? Object.class : parameterType) + this.parameterType = parameterObject == null ? (parameterType == null ? Object.class : parameterType) : parameterObject.getClass(); this.metaParameters = configuration.newMetaObject(additionalParameters); this.parameterObject = parameterObject; @@ -50,8 +48,7 @@ public ParameterMappingTokenHandler(List parameterMappings, Co } public ParameterMappingTokenHandler(List parameterMappings, Configuration configuration, - Class parameterType, - Map additionalParameters) { + Class parameterType, Map additionalParameters) { super(configuration); this.parameterType = parameterType; this.metaParameters = configuration.newMetaObject(additionalParameters); diff --git a/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java b/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java index c319821c2b4..928436b13cd 100644 --- a/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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. diff --git a/src/main/java/org/apache/ibatis/executor/BaseExecutor.java b/src/main/java/org/apache/ibatis/executor/BaseExecutor.java index 8955cd97d0f..509f4e84fcd 100644 --- a/src/main/java/org/apache/ibatis/executor/BaseExecutor.java +++ b/src/main/java/org/apache/ibatis/executor/BaseExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2023 the original author or authors. + * Copyright 2009-2025 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. diff --git a/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java b/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java index 29aa951d1b3..6fedbda2537 100644 --- a/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java +++ b/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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. diff --git a/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java b/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java index 018f6f6e691..678c791f92d 100644 --- a/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java +++ b/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2023 the original author or authors. + * Copyright 2009-2025 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. diff --git a/src/main/java/org/apache/ibatis/scripting/defaults/RawSqlSource.java b/src/main/java/org/apache/ibatis/scripting/defaults/RawSqlSource.java index 349c6171aef..ab96078fe50 100644 --- a/src/main/java/org/apache/ibatis/scripting/defaults/RawSqlSource.java +++ b/src/main/java/org/apache/ibatis/scripting/defaults/RawSqlSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2023 the original author or authors. + * Copyright 2009-2025 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. diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java index e1e88014831..f01e01b1fb7 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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. @@ -21,16 +21,16 @@ import java.util.Map; import java.util.StringJoiner; +import ognl.OgnlContext; +import ognl.OgnlRuntime; +import ognl.PropertyAccessor; + import org.apache.ibatis.builder.ParameterMappingTokenHandler; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.parsing.GenericTokenParser; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.session.Configuration; -import ognl.OgnlContext; -import ognl.OgnlRuntime; -import ognl.PropertyAccessor; - /** * @author Clinton Begin */ @@ -58,7 +58,8 @@ public DynamicContext(Configuration configuration, Class parameterType) { this(configuration, null, parameterType, false); } - public DynamicContext(Configuration configuration, Object parameterObject, Class parameterType, boolean paramExists) { + public DynamicContext(Configuration configuration, Object parameterObject, Class parameterType, + boolean paramExists) { if (parameterObject == null || parameterObject instanceof Map) { bindings = new ContextMap(null, false); } else { @@ -96,7 +97,8 @@ public List getParameterMappings() { protected String parseParam(String sql) { if (tokenParser == null) { - tokenHandler = new ParameterMappingTokenHandler(getParameterMappings(), configuration, parameterObject, parameterType, bindings, paramExists); + tokenHandler = new ParameterMappingTokenHandler(getParameterMappings(), configuration, parameterObject, + parameterType, bindings, paramExists); tokenParser = new GenericTokenParser("#{", "}", tokenHandler); } return tokenParser.parse(sql); diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNode.java b/src/main/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNode.java index 7390b06cb5b..af42ce8c64b 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNode.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2023 the original author or authors. + * Copyright 2009-2025 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. diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/TextSqlNode.java b/src/main/java/org/apache/ibatis/scripting/xmltags/TextSqlNode.java index 45381206a8a..e760f4408ad 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/TextSqlNode.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/TextSqlNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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. @@ -64,7 +64,7 @@ public String handleToken(String content) { context.getBindings().put("value", parameter); } Object value = OgnlCache.getValue(content, context.getBindings()); - // issue #274 return "" instead of "null" + // issue #274 return "" instead of "null" return value == null ? "" : String.valueOf(value); } } diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/TrimSqlNode.java b/src/main/java/org/apache/ibatis/scripting/xmltags/TrimSqlNode.java index 71f39661cb3..1a1053794ec 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/TrimSqlNode.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/TrimSqlNode.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2023 the original author or authors. + * Copyright 2009-2025 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. diff --git a/src/test/java/org/apache/ibatis/builder/SqlSourceBuilderTest.java b/src/test/java/org/apache/ibatis/builder/SqlSourceBuilderTest.java index 7b7aed1dba0..5d2b8ea5f3f 100644 --- a/src/test/java/org/apache/ibatis/builder/SqlSourceBuilderTest.java +++ b/src/test/java/org/apache/ibatis/builder/SqlSourceBuilderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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. 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 be8f9efcf76..3fdda6ff8a8 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-2024 the original author or authors. + * Copyright 2009-2025 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. @@ -393,7 +393,8 @@ void testShrinkWhitespacesInSql(SqlNode input, boolean shrinkWhitespaces, String static Stream testShrinkWhitespacesInSql() { return Stream.of( - Arguments.arguments(new StaticTextSqlNode("\t\n\n SELECT * \n FROM user\n \t WHERE user_id = 1\n\t "), false, + Arguments.arguments( + new StaticTextSqlNode("\t\n\n SELECT * \n FROM user\n \t WHERE user_id = 1\n\t "), false, "SELECT * \n FROM user\n \t WHERE user_id = 1"), Arguments.arguments(new StaticTextSqlNode("\t\n\n SELECT * \n FROM user\n \t WHERE user_id = 1\n\t"), true, "SELECT * FROM user WHERE user_id = 1")); diff --git a/src/test/java/org/apache/ibatis/submitted/bind_in_foreach/BindInForeachTest.java b/src/test/java/org/apache/ibatis/submitted/bind_in_foreach/BindInForeachTest.java index ff6a3549de0..1cca47711c1 100644 --- a/src/test/java/org/apache/ibatis/submitted/bind_in_foreach/BindInForeachTest.java +++ b/src/test/java/org/apache/ibatis/submitted/bind_in_foreach/BindInForeachTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2022 the original author or authors. + * Copyright 2009-2025 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,7 +15,7 @@ */ package org.apache.ibatis.submitted.bind_in_foreach; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.Reader; import java.util.List; diff --git a/src/test/java/org/apache/ibatis/submitted/raw_sql_source/RawSqlSourceTest.java b/src/test/java/org/apache/ibatis/submitted/raw_sql_source/RawSqlSourceTest.java index 672b62f577f..bc42b85badf 100644 --- a/src/test/java/org/apache/ibatis/submitted/raw_sql_source/RawSqlSourceTest.java +++ b/src/test/java/org/apache/ibatis/submitted/raw_sql_source/RawSqlSourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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,7 +15,7 @@ */ package org.apache.ibatis.submitted.raw_sql_source; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.Reader; import java.util.stream.Stream; From ecee90b23f35eb7384bd458f34ff7b1d6089b725 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Fri, 3 Jan 2025 02:34:45 +0900 Subject: [PATCH 05/10] Fixed an issue with a statement with no preceding text node --- .../ibatis/scripting/xmltags/DynamicContext.java | 15 ++++++++++----- .../builder/xml/dynamic/DynamicSqlSourceTest.java | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java index f01e01b1fb7..3a9966fd844 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java @@ -91,16 +91,21 @@ public String getSql() { return sqlBuilder.toString().trim(); } + private void initTokenParser(List parameterMappings) { + if (tokenParser == null) { + tokenHandler = new ParameterMappingTokenHandler(parameterMappings, configuration, parameterObject, parameterType, + bindings, paramExists); + tokenParser = new GenericTokenParser("#{", "}", tokenHandler); + } + } + public List getParameterMappings() { + initTokenParser(new ArrayList<>()); return tokenHandler == null ? new ArrayList<>() : tokenHandler.getParameterMappings(); } protected String parseParam(String sql) { - if (tokenParser == null) { - tokenHandler = new ParameterMappingTokenHandler(getParameterMappings(), configuration, parameterObject, - parameterType, bindings, paramExists); - tokenParser = new GenericTokenParser("#{", "}", tokenHandler); - } + initTokenParser(getParameterMappings()); return tokenParser.parse(sql); } 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 3fdda6ff8a8..e66ddd15a2c 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 @@ -343,6 +343,21 @@ void shouldPerformStrictMatchOnForEachVariableSubstitution() throws Exception { assertEquals("u", boundSql.getParameterMappings().get(3).getProperty()); } + @Test + void cornerCase_ForeachComesFirst() throws Exception { + final Map param = new HashMap<>(); + List beans = new ArrayList<>(); + beans.add(new Bean("bean id 1")); + beans.add(new Bean("bean id 2")); + param.put("beans", beans); + DynamicSqlSource source = createDynamicSqlSource(new ForEachSqlNode(new Configuration(), + mixedContents(new TextSqlNode("#{b.id}")), "beans", false, null, "b", "(", ")", ",")); + BoundSql boundSql = source.getBoundSql(param); + assertEquals(2, boundSql.getParameterMappings().size()); + assertEquals("b.id", boundSql.getParameterMappings().get(0).getProperty()); + assertEquals("b.id", boundSql.getParameterMappings().get(1).getProperty()); + } + private DynamicSqlSource createDynamicSqlSource(SqlNode... contents) throws IOException, SQLException { createBlogDataSource(); final String resource = "org/apache/ibatis/builder/MapperConfig.xml"; From 26aedfb6d7c46bcd4a2ce58834281e7d8478bf3d Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Fri, 3 Jan 2025 02:33:00 +0900 Subject: [PATCH 06/10] Disabled broken tests that mocks dynamic context It is nice to have these tests assuming they improve the coverage, however, they should be written without mocks. See the tests in DynamicSqlSourceTest. --- .../apache/ibatis/scripting/xmltags/ChooseSqlNodeTest.java | 4 +++- .../apache/ibatis/scripting/xmltags/ForEachSqlNodeTest.java | 4 +++- .../org/apache/ibatis/scripting/xmltags/IfSqlNodeTest.java | 4 +++- .../org/apache/ibatis/scripting/xmltags/MixedSqlNodeTest.java | 4 +++- .../org/apache/ibatis/scripting/xmltags/SetSqlNodeTest.java | 4 +++- .../ibatis/scripting/xmltags/StaticTextSqlNodeTest.java | 4 +++- .../org/apache/ibatis/scripting/xmltags/TextSqlNodeTest.java | 4 +++- .../org/apache/ibatis/scripting/xmltags/TrimSqlNodeTest.java | 4 +++- .../org/apache/ibatis/scripting/xmltags/WhereSqlNodeTest.java | 4 +++- 9 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/ChooseSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/ChooseSqlNodeTest.java index 73e8e4e7bed..6c5ddb7d3e2 100644 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/ChooseSqlNodeTest.java +++ b/src/test/java/org/apache/ibatis/scripting/xmltags/ChooseSqlNodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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. @@ -25,6 +25,7 @@ import org.apache.ibatis.domain.blog.Author; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** @@ -49,6 +50,7 @@ * * @see choose */ +@Disabled("Broken by #2760") class ChooseSqlNodeTest extends SqlNodeBase { private static final String FIRST_TEXT = " AND title like #{title}"; diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNodeTest.java index 7a551bc0a8d..e553a8afefb 100644 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNodeTest.java +++ b/src/test/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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. @@ -26,6 +26,7 @@ import java.util.List; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -44,6 +45,7 @@ * * @see foreach */ +@Disabled("Broken by #2760") class ForEachSqlNodeTest extends SqlNodeBase { private SqlNode sqlNode; diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/IfSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/IfSqlNodeTest.java index 71e2b460479..f76fef1a0d1 100644 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/IfSqlNodeTest.java +++ b/src/test/java/org/apache/ibatis/scripting/xmltags/IfSqlNodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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. @@ -24,6 +24,7 @@ import java.util.HashMap; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** @@ -37,6 +38,7 @@ * * @see if */ +@Disabled("Broken by #2760") class IfSqlNodeTest extends SqlNodeBase { private static final String CONDITION = "title != null"; diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/MixedSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/MixedSqlNodeTest.java index 63c98cc1126..feff45be908 100644 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/MixedSqlNodeTest.java +++ b/src/test/java/org/apache/ibatis/scripting/xmltags/MixedSqlNodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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. @@ -20,11 +20,13 @@ import java.util.Arrays; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** * @author mawen12 */ +@Disabled("Broken by #2760") class MixedSqlNodeTest extends SqlNodeBase { private static final String FIRST_TEXT = "abc"; diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/SetSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/SetSqlNodeTest.java index e09c32709ba..44c93a92ba3 100644 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/SetSqlNodeTest.java +++ b/src/test/java/org/apache/ibatis/scripting/xmltags/SetSqlNodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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. @@ -23,6 +23,7 @@ import java.util.HashMap; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** @@ -43,6 +44,7 @@ * * @see trim-where-set */ +@Disabled("Broken by #2760") class SetSqlNodeTest extends SqlNodeBase { private static final String FIRST_TEXT = " username = #{username},"; diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNodeTest.java index 5f15c888f43..c5702de9773 100644 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNodeTest.java +++ b/src/test/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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. @@ -18,11 +18,13 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.verify; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** * @author mawen12 */ +@Disabled("Broken by #2760") class StaticTextSqlNodeTest extends SqlNodeBase { private static final String TEXT = "select 1 from dual"; diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/TextSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/TextSqlNodeTest.java index 0cbed0b8895..94fd74ba799 100644 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/TextSqlNodeTest.java +++ b/src/test/java/org/apache/ibatis/scripting/xmltags/TextSqlNodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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. @@ -22,11 +22,13 @@ import java.util.HashMap; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** * @author mawen12 */ +@Disabled("Broken by #2760") class TextSqlNodeTest extends SqlNodeBase { private static final String TEXT = "select 1 from dual"; diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/TrimSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/TrimSqlNodeTest.java index a47172f33e3..bbe0aec4aa3 100644 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/TrimSqlNodeTest.java +++ b/src/test/java/org/apache/ibatis/scripting/xmltags/TrimSqlNodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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. @@ -23,6 +23,7 @@ import java.util.HashMap; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** @@ -43,6 +44,7 @@ * * @see trim-where-set */ +@Disabled("Broken by #2760") class TrimSqlNodeTest extends SqlNodeBase { private static final String FIRST_TEXT = " AND id = #{id}"; diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/WhereSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/WhereSqlNodeTest.java index d0818ff336b..2fbda011154 100644 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/WhereSqlNodeTest.java +++ b/src/test/java/org/apache/ibatis/scripting/xmltags/WhereSqlNodeTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2009-2024 the original author or authors. + * Copyright 2009-2025 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. @@ -23,6 +23,7 @@ import java.util.HashMap; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** @@ -43,6 +44,7 @@ * * @see trim-where-set */ +@Disabled("Broken by #2760") class WhereSqlNodeTest extends SqlNodeBase { private static final String FIRST_TEXT = " AND id = #{id}"; From f2c04e1449ff977d77437ad0b88223e5fa13c25d Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Fri, 3 Jan 2025 16:24:05 +0900 Subject: [PATCH 07/10] Deleting SqlNodeBase and its subclasses I took a look again, but these tests are not useful, I'm afraid. To test these classes' functionality, see DynamicSqlSourceTest. https://github.com/mybatis/mybatis-3/blob/8ac3920afd669f1ae54e21a2d00aa88416f9c29a/src/test/java/org/apache/ibatis/builder/xml/dynamic/DynamicSqlSourceTest.java --- .../scripting/xmltags/ChooseSqlNodeTest.java | 116 ---------------- .../scripting/xmltags/ForEachSqlNodeTest.java | 86 ------------ .../scripting/xmltags/IfSqlNodeTest.java | 87 ------------ .../scripting/xmltags/MixedSqlNodeTest.java | 51 ------- .../scripting/xmltags/SetSqlNodeTest.java | 123 ----------------- .../ibatis/scripting/xmltags/SqlNodeBase.java | 38 ------ .../xmltags/StaticTextSqlNodeTest.java | 45 ------- .../scripting/xmltags/TextSqlNodeTest.java | 72 ---------- .../scripting/xmltags/TrimSqlNodeTest.java | 125 ------------------ .../scripting/xmltags/VarDeclSqlNodeTest.java | 76 ----------- .../scripting/xmltags/WhereSqlNodeTest.java | 123 ----------------- 11 files changed, 942 deletions(-) delete mode 100644 src/test/java/org/apache/ibatis/scripting/xmltags/ChooseSqlNodeTest.java delete mode 100644 src/test/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNodeTest.java delete mode 100644 src/test/java/org/apache/ibatis/scripting/xmltags/IfSqlNodeTest.java delete mode 100644 src/test/java/org/apache/ibatis/scripting/xmltags/MixedSqlNodeTest.java delete mode 100644 src/test/java/org/apache/ibatis/scripting/xmltags/SetSqlNodeTest.java delete mode 100644 src/test/java/org/apache/ibatis/scripting/xmltags/SqlNodeBase.java delete mode 100644 src/test/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNodeTest.java delete mode 100644 src/test/java/org/apache/ibatis/scripting/xmltags/TextSqlNodeTest.java delete mode 100644 src/test/java/org/apache/ibatis/scripting/xmltags/TrimSqlNodeTest.java delete mode 100644 src/test/java/org/apache/ibatis/scripting/xmltags/VarDeclSqlNodeTest.java delete mode 100644 src/test/java/org/apache/ibatis/scripting/xmltags/WhereSqlNodeTest.java diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/ChooseSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/ChooseSqlNodeTest.java deleted file mode 100644 index 6c5ddb7d3e2..00000000000 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/ChooseSqlNodeTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2009-2025 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 - * - * https://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. - */ -package org.apache.ibatis.scripting.xmltags; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; - -import org.apache.ibatis.domain.blog.Author; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -/** - *
{@code
- *  SELECT *
- *  FROM BLOG
- *  WHERE state = 'active'
- *  
- * 		
- * 		 	AND title like #{title}
- * 		
- * 		
- * 		 	AND author_name like #{author.username}
- * 		
- * 		
- * 		 	AND featured = 1
- * 		
- *  
- * }
- * - * @author mawen12 - * - * @see choose - */ -@Disabled("Broken by #2760") -class ChooseSqlNodeTest extends SqlNodeBase { - - private static final String FIRST_TEXT = " AND title like #{title}"; - private static final String SECOND_TEXT = " AND author_name like #{author.username}"; - private static final String OTHERWISE_TEXT = " AND featured = 1"; - - private SqlNode sqlNode; - - @BeforeEach - void setup() { - SqlNode first = new IfSqlNode(new StaticTextSqlNode(FIRST_TEXT), "title != null"); - SqlNode second = new IfSqlNode(new StaticTextSqlNode(SECOND_TEXT), "author != null && author.username != null"); - List ifNodes = Arrays.asList(first, second); - - SqlNode defaultNode = new StaticTextSqlNode(OTHERWISE_TEXT); - - this.sqlNode = new ChooseSqlNode(ifNodes, defaultNode); - } - - @Test - @Override - public void shouldApply() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>() { - private static final long serialVersionUID = 1L; - - { - put("title", "abc"); - put("author", new Author(1, "mybatis", "***", null, null, null)); - } - }); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql(FIRST_TEXT); - } - - @Test - void shouldAppendSecond() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>() { - private static final long serialVersionUID = 1L; - - { - put("author", new Author(1, "mybatis", "***", null, null, null)); - } - }); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql(SECOND_TEXT); - } - - @Test - void shouldAppendOtherwise() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>()); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql(OTHERWISE_TEXT); - } -} diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNodeTest.java deleted file mode 100644 index e553a8afefb..00000000000 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/ForEachSqlNodeTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2009-2025 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 - * - * https://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. - */ -package org.apache.ibatis.scripting.xmltags; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -/** - *
{@code
- * 	SELECT *
- * 	FROM POST
- * 	
- * 	    
- * 	        #{item}
- * 	    
- * 	
- * }
- * - * @author mawen12 - * - * @see foreach - */ -@Disabled("Broken by #2760") -class ForEachSqlNodeTest extends SqlNodeBase { - - private SqlNode sqlNode; - - @BeforeEach - void setup() { - SqlNode contents = new StaticTextSqlNode("#{name}"); - this.sqlNode = new ForEachSqlNode(configuration, contents, "list", "index", "item", "ID in (", ")", ","); - } - - @Test - @Override - public void shouldApply() throws Exception { - ArgumentCaptor bindKeyCaptor = ArgumentCaptor.forClass(String.class); - ArgumentCaptor bindValueCaptor = ArgumentCaptor.forClass(Object.class); - doNothing().when(context).bind(bindKeyCaptor.capture(), bindValueCaptor.capture()); - - when(context.getBindings()).thenReturn(new HashMap<>() { - private static final long serialVersionUID = 1L; - - { - put("list", Arrays.asList("a", "b", "c")); - } - }); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql("ID in ("); - verify(context).appendSql(")"); - - List allKeyValues = bindKeyCaptor.getAllValues(); - List allValValues = bindValueCaptor.getAllValues(); - assertEquals(Arrays.asList("index", "__frch_index_0", "item", "__frch_item_0", "index", "__frch_index_0", "item", - "__frch_item_0", "index", "__frch_index_0", "item", "__frch_item_0"), allKeyValues); - assertEquals(Arrays.asList(0, 0, "a", "a", 1, 1, "b", "b", 2, 2, "c", "c"), allValValues); - } -} diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/IfSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/IfSqlNodeTest.java deleted file mode 100644 index f76fef1a0d1..00000000000 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/IfSqlNodeTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2009-2025 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 - * - * https://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. - */ -package org.apache.ibatis.scripting.xmltags; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.HashMap; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -/** - *
{@code
- * 	mawen12
- *
- * @see if
- */
-@Disabled("Broken by #2760")
-class IfSqlNodeTest extends SqlNodeBase {
-
-  private static final String CONDITION = "title != null";
-  private static final String TEXT = "AND title like #{title}";
-
-  private SqlNode sqlNode;
-
-  @BeforeEach
-  void setup() {
-    SqlNode contents = new StaticTextSqlNode(TEXT);
-    this.sqlNode = new IfSqlNode(contents, CONDITION);
-  }
-
-  @Test
-  @Override
-  public void shouldApply() throws Exception {
-    when(context.getBindings()).thenReturn(new HashMap<>() {
-      private static final long serialVersionUID = 1L;
-
-      {
-        put("title", "ENGLISH");
-      }
-    });
-
-    boolean result = sqlNode.apply(context);
-
-    assertTrue(result);
-    verify(context).appendSql(TEXT);
-  }
-
-  @Test
-  void shouldAppendNone() {
-    when(context.getBindings()).thenReturn(new HashMap<>() {
-      private static final long serialVersionUID = 1L;
-
-      {
-        put("title", null);
-      }
-    });
-
-    boolean result = sqlNode.apply(context);
-
-    assertFalse(result);
-    verify(context, never()).appendSql(TEXT);
-  }
-}
diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/MixedSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/MixedSqlNodeTest.java
deleted file mode 100644
index feff45be908..00000000000
--- a/src/test/java/org/apache/ibatis/scripting/xmltags/MixedSqlNodeTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- *    Copyright 2009-2025 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
- *
- *       https://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.
- */
-package org.apache.ibatis.scripting.xmltags;
-
-import static org.mockito.Mockito.verify;
-
-import java.util.Arrays;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-/**
- * @author mawen12
- */
-@Disabled("Broken by #2760")
-class MixedSqlNodeTest extends SqlNodeBase {
-
-  private static final String FIRST_TEXT = "abc";
-  private static final String SECOND_TEXT = "bcd";
-  private SqlNode sqlNode;
-
-  @BeforeEach
-  void setup() {
-    SqlNode first = new StaticTextSqlNode(FIRST_TEXT);
-    SqlNode second = new StaticTextSqlNode(SECOND_TEXT);
-    this.sqlNode = new MixedSqlNode(Arrays.asList(first, second));
-  }
-
-  @Test
-  @Override
-  public void shouldApply() throws Exception {
-    sqlNode.apply(context);
-
-    verify(context).appendSql("abc");
-    verify(context).appendSql("bcd");
-  }
-}
diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/SetSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/SetSqlNodeTest.java
deleted file mode 100644
index 44c93a92ba3..00000000000
--- a/src/test/java/org/apache/ibatis/scripting/xmltags/SetSqlNodeTest.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- *    Copyright 2009-2025 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
- *
- *       https://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.
- */
-package org.apache.ibatis.scripting.xmltags;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Arrays;
-import java.util.HashMap;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-/**
- * 
{@code
- * 	UPDATE author
- * 	
- * 		
- * 		 	password = #{password}
- * 		
- * 	
- * 	WHERE id = #{id}
- * }
- * - * @author mawen12 - * - * @see trim-where-set - */ -@Disabled("Broken by #2760") -class SetSqlNodeTest extends SqlNodeBase { - - private static final String FIRST_TEXT = " username = #{username},"; - private static final String SECOND_TEXT = " password = #{password}"; - - private SqlNode sqlNode; - - @BeforeEach - void setup() { - SqlNode first = new IfSqlNode(new StaticTextSqlNode(FIRST_TEXT), "username != null"); - SqlNode second = new IfSqlNode(new StaticTextSqlNode(SECOND_TEXT), "password != null"); - SqlNode contents = new MixedSqlNode(Arrays.asList(first, second)); - - this.sqlNode = new SetSqlNode(configuration, contents); - } - - @Test - @Override - public void shouldApply() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>() { - private static final long serialVersionUID = 1L; - - { - put("username", "Jack"); - put("password", "***"); - } - }); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql("SET username = #{username}, password = #{password}"); - } - - @Test - void shouldAppendOnlyUsername() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>() { - private static final long serialVersionUID = 1L; - - { - put("username", "Jack"); - } - }); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql("SET username = #{username}"); - } - - @Test - void shouldAppendOnlyPassword() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>() { - private static final long serialVersionUID = 1L; - - { - put("password", "***"); - } - }); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql("SET password = #{password}"); - } - - @Test - void shouldAppendNone() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>()); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql(""); - } -} diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/SqlNodeBase.java b/src/test/java/org/apache/ibatis/scripting/xmltags/SqlNodeBase.java deleted file mode 100644 index 03864e3e07d..00000000000 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/SqlNodeBase.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2009-2024 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 - * - * https://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. - */ -package org.apache.ibatis.scripting.xmltags; - -import org.apache.ibatis.session.Configuration; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -/** - * @author mawen12 - * - * @see SqlNode - */ -@ExtendWith(MockitoExtension.class) -abstract class SqlNodeBase { - - @Mock - protected Configuration configuration; - - @Mock - protected DynamicContext context; - - public abstract void shouldApply() throws Exception; -} diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNodeTest.java deleted file mode 100644 index c5702de9773..00000000000 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNodeTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2009-2025 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 - * - * https://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. - */ -package org.apache.ibatis.scripting.xmltags; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.verify; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -/** - * @author mawen12 - */ -@Disabled("Broken by #2760") -class StaticTextSqlNodeTest extends SqlNodeBase { - - private static final String TEXT = "select 1 from dual"; - - @Test - @Override - public void shouldApply() throws Exception { - // given - SqlNode sqlNode = new StaticTextSqlNode(TEXT); - - // when - boolean result = sqlNode.apply(context); - - // then - assertTrue(result); - verify(context).appendSql(TEXT); - } -} diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/TextSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/TextSqlNodeTest.java deleted file mode 100644 index 94fd74ba799..00000000000 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/TextSqlNodeTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2009-2025 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 - * - * https://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. - */ -package org.apache.ibatis.scripting.xmltags; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.HashMap; - -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -/** - * @author mawen12 - */ -@Disabled("Broken by #2760") -class TextSqlNodeTest extends SqlNodeBase { - - private static final String TEXT = "select 1 from dual"; - private static final String DYNAMIC_TEXT = "select * from user where id = ${id}"; - - @Test - @Override - public void shouldApply() throws Exception { - // given - TextSqlNode sqlNode = new TextSqlNode(TEXT); - - // when - boolean result = sqlNode.apply(context); - - // then - assertTrue(result); - assertFalse(sqlNode.isDynamic()); - verify(context).appendSql(TEXT); - } - - @Test - void shouldApplyDynamic() { - // given - TextSqlNode sqlNode = new TextSqlNode(DYNAMIC_TEXT); - when(context.getBindings()).thenReturn(new HashMap<>() { - private static final long serialVersionUID = 1L; - - { - put("id", 1); - } - }); - - // when - boolean result = sqlNode.apply(context); - - // then - assertTrue(result); - assertTrue(sqlNode.isDynamic()); - verify(context).appendSql("select * from user where id = 1"); - } -} diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/TrimSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/TrimSqlNodeTest.java deleted file mode 100644 index bbe0aec4aa3..00000000000 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/TrimSqlNodeTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2009-2025 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 - * - * https://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. - */ -package org.apache.ibatis.scripting.xmltags; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.HashMap; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -/** - *
{@code
- * 	SELECT *
- * 	FROM users
- * 	
- * 	    
- * 			AND id = #{id}
- * 	    
- * 	    
- * 			AND name = #{name}
- * 	    
- * 	
- * }
- * - * @author mawen12 - * - * @see trim-where-set - */ -@Disabled("Broken by #2760") -class TrimSqlNodeTest extends SqlNodeBase { - - private static final String FIRST_TEXT = " AND id = #{id}"; - private static final String SECOND_TEXT = " AND name = #{name}"; - private static final String PREFIX = "WHERE"; - private static final String PREFIX_OVERRIDES = "AND |OR "; - - private SqlNode sqlNode; - - @BeforeEach - void setup() { - SqlNode first = new IfSqlNode(new StaticTextSqlNode(FIRST_TEXT), "id != null"); - SqlNode second = new IfSqlNode(new StaticTextSqlNode(SECOND_TEXT), "name != null"); - SqlNode contents = new MixedSqlNode(Arrays.asList(first, second)); - - this.sqlNode = new TrimSqlNode(configuration, contents, PREFIX, PREFIX_OVERRIDES, null, null); - } - - @Test - @Override - public void shouldApply() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>() { - private static final long serialVersionUID = 1L; - - { - put("id", 1); - put("name", "mybatis"); - } - }); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql("WHERE id = #{id} AND name = #{name}"); - } - - @Test - void shouldAppendOnlyId() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>() { - private static final long serialVersionUID = 1L; - - { - put("id", 1); - } - }); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql("WHERE id = #{id}"); - } - - @Test - void shouldAppendOnlyName() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>() { - private static final long serialVersionUID = 1L; - - { - put("name", "mybatis"); - } - }); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql("WHERE name = #{name}"); - } - - @Test - void shouldAppendNone() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>()); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql(""); - } -} diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/VarDeclSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/VarDeclSqlNodeTest.java deleted file mode 100644 index 8e58f5b5bb9..00000000000 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/VarDeclSqlNodeTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2009-2024 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 - * - * https://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. - */ -package org.apache.ibatis.scripting.xmltags; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.HashMap; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -/** - *
{@code
- * 	
- * 	SELECT * FROM BLOG
- * 	WHERE title like #{pattern}
- * }
- * - * @author mawen12 - * - * @see bind - */ -class VarDeclSqlNodeTest extends SqlNodeBase { - - private SqlNode sqlNode; - - @BeforeEach - void setup() { - this.sqlNode = new VarDeclSqlNode("pattern", "'%' + _parameter.getTitle() + '%'"); - } - - @Test - @Override - public void shouldApply() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>() { - private static final long serialVersionUID = 1L; - - { - put("_parameter", new Bean("abc")); - } - }); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).bind("pattern", "%abc%"); - } - - private static class Bean { - - private String title; - - public Bean(String title) { - this.title = title; - } - - public String getTitle() { - return title; - } - } -} diff --git a/src/test/java/org/apache/ibatis/scripting/xmltags/WhereSqlNodeTest.java b/src/test/java/org/apache/ibatis/scripting/xmltags/WhereSqlNodeTest.java deleted file mode 100644 index 2fbda011154..00000000000 --- a/src/test/java/org/apache/ibatis/scripting/xmltags/WhereSqlNodeTest.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2009-2025 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 - * - * https://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. - */ -package org.apache.ibatis.scripting.xmltags; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.HashMap; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; - -/** - *
{@code
- * 	SELECT *
- * 	FROM users
- * 	
- * 	    
- * 			AND id = #{id}
- * 	    
- * 	    
- * 			AND name = #{name}
- * 	    
- * 	
- * }
- * - * @author mawen12 - * - * @see trim-where-set - */ -@Disabled("Broken by #2760") -class WhereSqlNodeTest extends SqlNodeBase { - - private static final String FIRST_TEXT = " AND id = #{id}"; - private static final String SECOND_TEXT = " AND name = #{name}"; - - private SqlNode sqlNode; - - @BeforeEach - void setup() { - SqlNode first = new IfSqlNode(new StaticTextSqlNode(FIRST_TEXT), "id != null"); - SqlNode second = new IfSqlNode(new StaticTextSqlNode(SECOND_TEXT), "name != null"); - SqlNode contents = new MixedSqlNode(Arrays.asList(first, second)); - - this.sqlNode = new WhereSqlNode(configuration, contents); - } - - @Test - @Override - public void shouldApply() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>() { - private static final long serialVersionUID = 1L; - - { - put("id", 1); - put("name", "mybatis"); - } - }); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql("WHERE id = #{id} AND name = #{name}"); - } - - @Test - void shouldAppendOnlyId() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>() { - private static final long serialVersionUID = 1L; - - { - put("id", 1); - } - }); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql("WHERE id = #{id}"); - } - - @Test - void shouldAppendOnlyName() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>() { - private static final long serialVersionUID = 1L; - - { - put("name", "mybatis"); - } - }); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql("WHERE name = #{name}"); - } - - @Test - void shouldAppendNone() throws Exception { - when(context.getBindings()).thenReturn(new HashMap<>()); - - boolean result = sqlNode.apply(context); - - assertTrue(result); - verify(context).appendSql(""); - } -} From f2047c3a4edb15e46a716f745b87b7cb97c9f512 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sun, 26 Jan 2025 12:38:51 +0900 Subject: [PATCH 08/10] Cleaned up a little bit --- .../apache/ibatis/scripting/xmltags/DynamicContext.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java index 3a9966fd844..195f9b2cfbb 100644 --- a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java +++ b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java @@ -93,15 +93,15 @@ public String getSql() { private void initTokenParser(List parameterMappings) { if (tokenParser == null) { - tokenHandler = new ParameterMappingTokenHandler(parameterMappings, configuration, parameterObject, parameterType, - bindings, paramExists); + tokenHandler = new ParameterMappingTokenHandler(parameterMappings != null ? parameterMappings : new ArrayList<>(), + configuration, parameterObject, parameterType, bindings, paramExists); tokenParser = new GenericTokenParser("#{", "}", tokenHandler); } } public List getParameterMappings() { - initTokenParser(new ArrayList<>()); - return tokenHandler == null ? new ArrayList<>() : tokenHandler.getParameterMappings(); + initTokenParser(null); + return tokenHandler.getParameterMappings(); } protected String parseParam(String sql) { From 7043d8a2e67b12a9e8bb253cd27f17f7d8e035d3 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sun, 26 Jan 2025 21:28:32 +0900 Subject: [PATCH 09/10] Making the intent clearer --- .../java/org/apache/ibatis/mapping/ParameterMapping.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java b/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java index 6fedbda2537..dbe1ddaafad 100644 --- a/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java +++ b/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java @@ -27,6 +27,7 @@ */ public class ParameterMapping { + private static Object UNSET = new Object(); private Configuration configuration; private String property; @@ -38,8 +39,7 @@ public class ParameterMapping { private String resultMapId; private String jdbcTypeName; private String expression; - private Object value; - private boolean hasValue; + private Object value = UNSET; private ParameterMapping() { } @@ -103,7 +103,6 @@ public Builder expression(String expression) { public Builder value(Object value) { parameterMapping.value = value; - parameterMapping.hasValue = true; return this; } @@ -218,7 +217,7 @@ public Object getValue() { } public boolean hasValue() { - return hasValue; + return value != UNSET; } @Override From eb8749e055a8bce5f1bc260f30ad5fc12ce2eb92 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Mon, 27 Jan 2025 05:40:23 +0900 Subject: [PATCH 10/10] Forgot to add `final` --- src/main/java/org/apache/ibatis/mapping/ParameterMapping.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java b/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java index dbe1ddaafad..7265499c600 100644 --- a/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java +++ b/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java @@ -27,7 +27,7 @@ */ public class ParameterMapping { - private static Object UNSET = new Object(); + private static final Object UNSET = new Object(); private Configuration configuration; private String property;