Skip to content

Commit 0261e21

Browse files
Merge pull request #8 from FuriousPws002/07-dynamic-sql
07 dynamic sql
2 parents 25f7af7 + 083c167 commit 0261e21

File tree

14 files changed

+501
-2
lines changed

14 files changed

+501
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
[4.参数绑定](https://github.com/FuriousPws002/mini-mybatis/wiki/4.%E5%8F%82%E6%95%B0%E7%BB%91%E5%AE%9A "Markdown") <br>
88
[5.resultType结果集处理](https://github.com/FuriousPws002/mini-mybatis/wiki/5.resultType%E7%BB%93%E6%9E%9C%E9%9B%86%E5%A4%84%E7%90%86 "Markdown") <br>
99
[6.resultMap结果集处理](https://github.com/FuriousPws002/mini-mybatis/wiki/6.resultMap%E7%BB%93%E6%9E%9C%E9%9B%86%E5%A4%84%E7%90%86 "Markdown") <br>
10+
[7.动态sql](https://github.com/FuriousPws002/mini-mybatis/wiki/7.%E5%8A%A8%E6%80%81sql "Markdown") <br>

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@
3434
<artifactId>commons-lang3</artifactId>
3535
<version>3.14.0</version>
3636
</dependency>
37+
<dependency>
38+
<groupId>ognl</groupId>
39+
<artifactId>ognl</artifactId>
40+
<version>3.4.2</version>
41+
</dependency>
3742
</dependencies>
3843

3944
<build>

src/main/java/org/apache/ibatis/mapping/BoundSql.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.apache.ibatis.mapping;
22

3+
import java.util.HashMap;
34
import java.util.List;
5+
import java.util.Map;
46

57
/**
68
* @author furious 2024/4/7
@@ -10,15 +12,21 @@ public class BoundSql {
1012
private final String sql;
1113
private final List<ParameterMapping> parameterMappings;
1214
private final Object parameterObject;
15+
private final Map<String, Object> additionalParameters;
1316

1417
public BoundSql(String sql) {
1518
this(sql, null, null);
1619
}
1720

1821
public BoundSql(String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
22+
this(sql, parameterMappings, parameterObject, new HashMap<>());
23+
}
24+
25+
public BoundSql(String sql, List<ParameterMapping> parameterMappings, Object parameterObject, Map<String, Object> additionalParameters) {
1926
this.sql = sql;
2027
this.parameterMappings = parameterMappings;
2128
this.parameterObject = parameterObject;
29+
this.additionalParameters = additionalParameters;
2230
}
2331

2432
public List<ParameterMapping> getParameterMappings() {
@@ -32,4 +40,20 @@ public Object getParameterObject() {
3240
public String getSql() {
3341
return this.sql;
3442
}
43+
44+
public void setAdditionalParameter(String key, Object value) {
45+
additionalParameters.put(key, value);
46+
}
47+
48+
public Object getAdditionalParameter(String key) {
49+
return additionalParameters.get(key);
50+
}
51+
52+
public boolean hasAdditionalParameter(String key) {
53+
return additionalParameters.containsKey(key);
54+
}
55+
56+
public Map<String, Object> getAdditionalParameters() {
57+
return additionalParameters;
58+
}
3559
}

src/main/java/org/apache/ibatis/mapping/MappedStatement.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public Configuration getConfiguration() {
2828

2929
public BoundSql getBoundSql(Object parameterObject) {
3030
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
31-
return new BoundSql(boundSql.getSql(),boundSql.getParameterMappings(),parameterObject);
31+
return new BoundSql(boundSql.getSql(), boundSql.getParameterMappings(), parameterObject, boundSql.getAdditionalParameters());
3232
}
3333

3434
public ResultMap getResultMap() {

src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ public void setParameters(PreparedStatement ps) throws SQLException {
4747
ParameterMapping parameterMapping = parameterMappings.get(i);
4848
Object value;
4949
String propertyName = parameterMapping.getProperty();
50-
if (Objects.isNull(parameterObject)) {
50+
if (boundSql.hasAdditionalParameter(propertyName)) {
51+
value = boundSql.getAdditionalParameter(propertyName);
52+
}else if (Objects.isNull(parameterObject)) {
5153
value = null;
5254
} else if (configuration.getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass())) {
5355
value = parameterObject;

src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.apache.ibatis.scripting.xmltags;
22

3+
import java.util.HashMap;
4+
import java.util.Map;
35
import java.util.StringJoiner;
46

57
/**
@@ -8,15 +10,37 @@
810
public class DynamicContext {
911

1012
private final StringJoiner sqlBuilder = new StringJoiner(" ");
13+
private Object parameterObject;
14+
private final Map<String, Object> bindings = new HashMap<>();
1115

1216
public DynamicContext() {
1317
}
1418

19+
public DynamicContext(Object parameterObject) {
20+
this.parameterObject = parameterObject;
21+
}
22+
1523
public void appendSql(String sql) {
1624
sqlBuilder.add(sql);
1725
}
1826

1927
public String getSql() {
2028
return sqlBuilder.toString().trim();
2129
}
30+
31+
public Object getParameterObject() {
32+
return parameterObject;
33+
}
34+
35+
public void setParameterObject(Object parameterObject) {
36+
this.parameterObject = parameterObject;
37+
}
38+
39+
public Map<String, Object> getBindings() {
40+
return bindings;
41+
}
42+
43+
public void bind(String key, Object value) {
44+
bindings.put(key, value);
45+
}
2246
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.apache.ibatis.scripting.xmltags;
2+
3+
import org.apache.ibatis.builder.SqlSourceBuilder;
4+
import org.apache.ibatis.mapping.BoundSql;
5+
import org.apache.ibatis.mapping.SqlSource;
6+
import org.apache.ibatis.session.Configuration;
7+
8+
/**
9+
* @author furious 2024/4/16
10+
*/
11+
public class DynamicSqlSource implements SqlSource {
12+
13+
private final Configuration configuration;
14+
private final SqlNode rootSqlNode;
15+
16+
public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
17+
this.configuration = configuration;
18+
this.rootSqlNode = rootSqlNode;
19+
}
20+
21+
@Override
22+
public BoundSql getBoundSql(Object parameterObject) {
23+
DynamicContext context = new DynamicContext(parameterObject);
24+
rootSqlNode.apply(context);
25+
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
26+
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
27+
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType);
28+
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
29+
context.getBindings().forEach(boundSql::setAdditionalParameter);
30+
return boundSql;
31+
}
32+
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package org.apache.ibatis.scripting.xmltags;
2+
3+
import java.util.Map;
4+
import java.util.Objects;
5+
6+
import org.apache.commons.lang3.StringUtils;
7+
import org.apache.ibatis.parsing.GenericTokenParser;
8+
9+
import ognl.Ognl;
10+
11+
/**
12+
* @author furious 2024/4/17
13+
*/
14+
public class ForeachSqlNode implements SqlNode {
15+
16+
public static final String ITEM_PREFIX = ForeachSqlNode.class.getName();
17+
18+
private final SqlNode contents;
19+
private final String collection;
20+
private final String item;
21+
private final String open;
22+
private final String close;
23+
private final String separator;
24+
25+
public ForeachSqlNode(SqlNode contents, String collection, String item, String open, String close, String separator) {
26+
this.contents = contents;
27+
this.collection = collection;
28+
this.item = item;
29+
this.open = open;
30+
this.close = close;
31+
this.separator = separator;
32+
}
33+
34+
@Override
35+
@SuppressWarnings("rawtypes")
36+
public boolean apply(DynamicContext context) {
37+
Iterable<?> iterable = null;
38+
try {
39+
Object value = Ognl.getValue(Ognl.parseExpression(collection), context.getParameterObject());
40+
if (value instanceof Iterable && ((Iterable) value).iterator().hasNext()) {
41+
iterable = (Iterable) value;
42+
}
43+
} catch (Exception e) {
44+
return false;
45+
}
46+
if (Objects.isNull(iterable)) {
47+
return true;
48+
}
49+
50+
applyOpen(context);
51+
int index = 0;
52+
for (Object o : iterable) {
53+
DynamicContext oldContext = context;
54+
if (index == 0) {
55+
context = new SeparatorContext(context, "");
56+
} else {
57+
context = new SeparatorContext(context, separator);
58+
}
59+
applyItem(context, o, index);
60+
contents.apply(new FilteredDynamicContext(context, item, index));
61+
context = oldContext;
62+
index++;
63+
}
64+
applyClose(context);
65+
return true;
66+
}
67+
68+
private void applyOpen(DynamicContext context) {
69+
if (Objects.nonNull(open)) {
70+
context.appendSql(open);
71+
}
72+
}
73+
74+
private void applyItem(DynamicContext context, Object o, int i) {
75+
if (Objects.nonNull(item)) {
76+
context.bind(item, o);
77+
context.bind(itemizeItem(item, i), o);
78+
}
79+
}
80+
81+
private void applyClose(DynamicContext context) {
82+
if (Objects.nonNull(close)) {
83+
context.appendSql(close);
84+
}
85+
}
86+
87+
private static String itemizeItem(String item, int i) {
88+
return ITEM_PREFIX + item + "_" + i;
89+
}
90+
91+
private static class SeparatorContext extends DynamicContext {
92+
93+
private final DynamicContext delegate;
94+
private final String separator;
95+
96+
public SeparatorContext(DynamicContext delegate, String separator) {
97+
this.delegate = delegate;
98+
this.separator = separator;
99+
}
100+
101+
@Override
102+
public Map<String, Object> getBindings() {
103+
return delegate.getBindings();
104+
}
105+
106+
@Override
107+
public void bind(String name, Object value) {
108+
delegate.bind(name, value);
109+
}
110+
111+
@Override
112+
public void appendSql(String sql) {
113+
if (StringUtils.isNoneBlank(sql)) {
114+
delegate.appendSql(separator);
115+
}
116+
delegate.appendSql(sql);
117+
}
118+
119+
@Override
120+
public String getSql() {
121+
return delegate.getSql();
122+
}
123+
}
124+
125+
private static class FilteredDynamicContext extends DynamicContext {
126+
private final DynamicContext delegate;
127+
private final String item;
128+
private final int index;
129+
130+
public FilteredDynamicContext(DynamicContext delegate, String item, int index) {
131+
this.delegate = delegate;
132+
this.item = item;
133+
this.index = index;
134+
}
135+
136+
@Override
137+
public Map<String, Object> getBindings() {
138+
return delegate.getBindings();
139+
}
140+
141+
@Override
142+
public void bind(String name, Object value) {
143+
delegate.bind(name, value);
144+
}
145+
146+
@Override
147+
public String getSql() {
148+
return delegate.getSql();
149+
}
150+
151+
@Override
152+
public void appendSql(String sql) {
153+
GenericTokenParser parser = new GenericTokenParser("#{", "}", content -> "#{" + itemizeItem(item, index) + "}");
154+
delegate.appendSql(parser.parse(sql));
155+
}
156+
}
157+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.apache.ibatis.scripting.xmltags;
2+
3+
import ognl.Ognl;
4+
5+
/**
6+
* @author furious 2024/4/16
7+
*/
8+
public class IfSqlNode implements SqlNode {
9+
10+
private final String test;
11+
private final SqlNode contents;
12+
13+
public IfSqlNode(String test, SqlNode contents) {
14+
this.test = test;
15+
this.contents = contents;
16+
}
17+
18+
@Override
19+
public boolean apply(DynamicContext context) {
20+
try {
21+
Object value = Ognl.getValue(Ognl.parseExpression(test), context.getParameterObject());
22+
if (value instanceof Boolean && (Boolean) value) {
23+
contents.apply(context);
24+
}
25+
return true;
26+
} catch (Exception e) {
27+
return false;
28+
}
29+
}
30+
}

0 commit comments

Comments
 (0)