Skip to content

Handle #set inside #repeat correctly #245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-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 @@ -20,6 +20,9 @@
import java.util.Map;

import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.session.Configuration;

public class ParameterMappingCollector {
Expand All @@ -28,15 +31,16 @@ public class ParameterMappingCollector {
private final List<ParameterMapping> parameterMappings = new ArrayList<>();
private final Map<String, Object> context;
private final Configuration configuration;
private final MetaObject metaParameters;

private int uid = 0;
private String itemKey;

public ParameterMappingCollector(ParameterMapping[] newParameterMappingSources, Map<String, Object> newContext,
Configuration newConfiguration) {
this.parameterMappingSources = newParameterMappingSources;
this.context = newContext;
this.configuration = newConfiguration;
this.metaParameters = configuration.newMetaObject(newContext);
}

public void setItemKey(String value) {
Expand All @@ -49,54 +53,36 @@ public String getItemKey() {

public String g(int mapping) {
ParameterMapping parameterMapping = this.parameterMappingSources[mapping];
PropertyInfo vi = getPropertyInfo(parameterMapping.getProperty());
if (vi.isIterable) {
parameterMapping = itemize(parameterMapping, vi);
this.context.put(vi.root, this.context.get(this.itemKey));
}
this.parameterMappings.add(parameterMapping);
this.parameterMappings.add(mappingWithValue(parameterMapping));
return "?";
}

public List<ParameterMapping> getParameterMappings() {
return this.parameterMappings;
}

private ParameterMapping itemize(ParameterMapping source, PropertyInfo var) {
StringBuilder sb = new StringBuilder().append("_RPTITEM_").append(this.uid++);
var.root = sb.toString();
String propertyName = sb.append(var.path).toString();
ParameterMapping.Builder builder = new ParameterMapping.Builder(this.configuration, propertyName,
source.getJavaType());
private ParameterMapping mappingWithValue(ParameterMapping source) {
String property = source.getProperty();
ParameterMapping.Builder builder = new ParameterMapping.Builder(this.configuration, property, source.getJavaType());
builder.expression(source.getExpression()).jdbcType(source.getJdbcType()).jdbcTypeName(source.getJdbcTypeName())
.mode(source.getMode()).numericScale(source.getNumericScale()).resultMapId(source.getResultMapId())
.typeHandler(source.getTypeHandler());
return builder.build();
}

private PropertyInfo getPropertyInfo(String name) {
PropertyInfo i = new PropertyInfo();
if (name != null) {
int p = name.indexOf('.');
if (p == -1) {
i.root = name;
PropertyTokenizer propertyTokenizer = new PropertyTokenizer(property);
Object parameterObject = context.get(SQLScriptSource.PARAMETER_OBJECT_KEY);
if (!ParameterMode.OUT.equals(source.getMode())) {
if (metaParameters.hasGetter(propertyTokenizer.getName())) {
builder.value(metaParameters.getValue(property));
} else if (parameterObject == null) {
builder.value(null);
} else if (configuration.getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass())) {
builder.value(parameterObject);
} else {
i.root = name.substring(0, p);
i.path = name.substring(p);
MetaObject metaObject = configuration.newMetaObject(parameterObject);
builder.value(metaObject.getValue(property));
}
}
i.isIterable = this.itemKey != null && this.itemKey.equals(i.root);
return i;
}

static class PropertyInfo {
boolean isIterable = false;
String root = "";
String path = "";

public PropertyInfo() {
// Prevent synthetic access
}
return builder.build();
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 the original author or authors.
* Copyright 2012-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 @@ -21,6 +21,7 @@
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -273,6 +274,31 @@ void testDynamicSelectWithIterationBoundary() {
}
}

@Test
void testSetInsideRepeat() {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
Name[] names = { new Name(2), new Name(5) };
Map<String, Object> param = new HashMap<>();
param.put("names", names);
param.put("nid", 4);
List<Name> answer = sqlSession.selectList("org.mybatis.scripting.velocity.use.selectWithSetInsideRepeat", param);
assertEquals(2, answer.size());
assertEquals(2, answer.get(0).getId());
assertEquals(5, answer.get(1).getId());
}
}

@Test
void testNestedRepeat() {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
List<List<Integer>> ids = Arrays.asList(Arrays.asList(1, 10), Arrays.asList(100, 1000));
Map<String, Object> param = new HashMap<>();
param.put("ids", ids);
Integer answer = sqlSession.selectOne("org.mybatis.scripting.velocity.use.selectNestedRepeat", param);
assertEquals(202, answer);
}
}

@Test
void testSelectKey() {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
Expand Down
25 changes: 24 additions & 1 deletion src/test/resources/org/mybatis/scripting/velocity/use/mapper.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

Copyright 2012-2022 the original author or authors.
Copyright 2012-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 Down Expand Up @@ -92,6 +92,29 @@
#end
</select>

<select id="selectWithSetInsideRepeat" resultType="org.mybatis.scripting.velocity.use.Name">
SELECT *
FROM names
#where()
#repeat($_parameter.names $name ',' 'id IN (' ')')
#set( $nid = $name.id)
@{nid}
#end
or id = @{nid}
#end
order by id
</select>

<select id="selectNestedRepeat" resultType="int">
SELECT
#repeat($_parameter.ids $level1 '+' '' '')
#repeat($level1 $level2 '+' '' '')
@{level1[0]}
#end
#end
FROM (VALUES(0))
</select>

<select id="selectWithCustomUserDirective" resultType="org.mybatis.scripting.velocity.use.Name">
#genSql()
#end
Expand Down
Loading