@@ -1,5 +1,5 @@ /* * Copyright 2018-2022 the original author or authors. * Copyright 2018-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. Expand All @@ -16,19 +16,25 @@ package org.mybatis.scripting.thymeleaf; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.function.BiFunction; 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.reflection.MetaClass; import org.apache.ibatis.scripting.xmltags.DynamicContext; import org.apache.ibatis.session.Configuration; Expand All @@ -53,7 +59,6 @@ private static class TemporaryTakeoverKeys { private final Configuration configuration; private final SqlGenerator sqlGenerator; private final SqlSourceBuilder sqlSourceBuilder; private final String sqlTemplate; private final Class<?> parameterType; Expand All @@ -75,7 +80,6 @@ private static class TemporaryTakeoverKeys { this.sqlGenerator = sqlGenerator; this.sqlTemplate = sqlTemplate; this.parameterType = parameterType; this.sqlSourceBuilder = new SqlSourceBuilder(configuration); } /** Expand All @@ -90,20 +94,33 @@ public BoundSql getBoundSql(Object parameterObject) { processingParameterType = parameterType; } DynamicContext dynamicContext = new DynamicContext(configuration, parameterObject); Map<String, Object> customVariables = dynamicContext.getBindings(); Map<String, Object> bindings = new HashMap<>(); bindings.put(DynamicContext.PARAMETER_OBJECT_KEY, parameterObject); bindings.put(DynamicContext.DATABASE_ID_KEY, configuration.getDatabaseId()); Map<String, Object> customVariables = bindings; customVariables.put(TemporaryTakeoverKeys.CONFIGURATION, configuration); customVariables.put(TemporaryTakeoverKeys.DYNAMIC_CONTEXT,dynamicContext ); customVariables.put(TemporaryTakeoverKeys.DYNAMIC_CONTEXT,bindings ); customVariables.put(TemporaryTakeoverKeys.PROCESSING_PARAMETER_TYPE, processingParameterType); String sql = sqlGenerator.generate(sqlTemplate, parameterObject,dynamicContext::bind , customVariables); String sql = sqlGenerator.generate(sqlTemplate, parameterObject,bindings::put , customVariables); SqlSource sqlSource =sqlSourceBuilder. parse(sql,processingParameterType, dynamicContext.getBindings() ); SqlSource sqlSource = parse(configuration, sql,parameterObject, bindings ); BoundSql boundSql = sqlSource.getBoundSql(parameterObject); dynamicContext.getBindings() .forEach(boundSql::setAdditionalParameter);bindings .forEach(boundSql::setAdditionalParameter); return boundSql; } private static SqlSource parse(Configuration configuration, String originalSql, Object parameterObject, Map<String, Object> additionalParameters) { Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); List<ParameterMapping> parameterMappings = new ArrayList<>(); ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(parameterMappings, configuration, parameterObject, parameterType, additionalParameters, true); GenericTokenParser parser = new GenericTokenParser("#{", "}", handler); return SqlSourceBuilder.buildSqlSource(configuration, parser.parse(originalSql), parameterMappings); } /** * The factory class for Thymeleaf's context. * Expand All @@ -116,20 +133,20 @@ static class ContextFactory implements BiFunction<Object, Map<String, Object>, I @Override public IContext apply(Object parameter, Map<String, Object> customVariable) { Configuration configuration = (Configuration) customVariable.remove(TemporaryTakeoverKeys.CONFIGURATION); DynamicContext dynamicContext = (DynamicContext ) customVariable.remove(TemporaryTakeoverKeys.DYNAMIC_CONTEXT);Map<String, Object> bindings = (Map<String, Object> ) customVariable.remove(TemporaryTakeoverKeys.DYNAMIC_CONTEXT); Class<?> processingParameterType = (Class<?>) customVariable .remove(TemporaryTakeoverKeys.PROCESSING_PARAMETER_TYPE); MyBatisBindingContext bindingContext = new MyBatisBindingContext( parameter != null && configuration.getTypeHandlerRegistry().hasTypeHandler(processingParameterType)); dynamicContext.bind (MyBatisBindingContext.CONTEXT_VARIABLE_NAME, bindingContext);bindings.put (MyBatisBindingContext.CONTEXT_VARIABLE_NAME, bindingContext); IContext context; if (parameter instanceof Map) { @SuppressWarnings(value = "unchecked") Map<String, Object> map = (Map<String, Object>) parameter; context = new MapBasedContext(map,dynamicContext , configuration.getVariables()); context = new MapBasedContext(map,bindings , configuration.getVariables()); } else { MetaClass metaClass = MetaClass.forClass(processingParameterType, configuration.getReflectorFactory()); context = new MetaClassBasedContext(parameter, metaClass, processingParameterType,dynamicContext , context = new MetaClassBasedContext(parameter, metaClass, processingParameterType,bindings , configuration.getVariables()); } return context; Expand All @@ -138,15 +155,15 @@ public IContext apply(Object parameter, Map<String, Object> customVariable) { private abstract static class AbstractContext implements IContext { private finalDynamicContext dynamicContext; private finalMap<String, Object> dynamicContext; private final Properties configurationProperties; private final Set<String> variableNames; private AbstractContext(DynamicContext dynamicContext, Properties configurationProperties) { private AbstractContext(Map<String, Object> dynamicContext, Properties configurationProperties) { this.dynamicContext = dynamicContext; this.configurationProperties = configurationProperties; this.variableNames = new HashSet<>(); addVariableNames(dynamicContext.getBindings(). keySet()); addVariableNames(dynamicContext.keySet()); Optional.ofNullable(configurationProperties).ifPresent(v -> addVariableNames(v.stringPropertyNames())); } Expand Down Expand Up @@ -183,8 +200,8 @@ public Set<String> getVariableNames() { */ @Override public Object getVariable(String name) { if (dynamicContext.getBindings(). containsKey(name)) { return dynamicContext.getBindings(). get(name); if (dynamicContext.containsKey(name)) { return dynamicContext.get(name); } if (configurationProperties != null && configurationProperties.containsKey(name)) { return configurationProperties.getProperty(name); Expand All @@ -200,7 +217,7 @@ private static class MapBasedContext extends AbstractContext { private final Map<String, Object> variables; private MapBasedContext(Map<String, Object> parameterMap,DynamicContext dynamicContext, private MapBasedContext(Map<String, Object> parameterMap,Map<String, Object> dynamicContext, Properties configurationProperties) { super(dynamicContext, configurationProperties); this.variables = parameterMap; Expand All @@ -224,7 +241,7 @@ private static class MetaClassBasedContext extends AbstractContext { private final Class<?> parameterType; private MetaClassBasedContext(Object parameterObject, MetaClass parameterMetaClass, Class<?> parameterType, DynamicContext dynamicContext, Properties configurationProperties) {Map<String, Object> dynamicContext, Properties configurationProperties) { super(dynamicContext, configurationProperties); this.parameterObject = parameterObject; this.parameterMetaClass = parameterMetaClass; Expand Down