Skip to content

Commit

Permalink
Merge pull request #8 from FuriousPws002/07-dynamic-sql
Browse files Browse the repository at this point in the history
07 dynamic sql
  • Loading branch information
FuriousPws002 authored Apr 17, 2024
2 parents 25f7af7 + 083c167 commit 0261e21
Show file tree
Hide file tree
Showing 14 changed files with 501 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
[4.参数绑定](https://github.com/FuriousPws002/mini-mybatis/wiki/4.%E5%8F%82%E6%95%B0%E7%BB%91%E5%AE%9A "Markdown") <br>
[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>
[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>
[7.动态sql](https://github.com/FuriousPws002/mini-mybatis/wiki/7.%E5%8A%A8%E6%80%81sql "Markdown") <br>
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
<artifactId>commons-lang3</artifactId>
<version>3.14.0</version>
</dependency>
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.4.2</version>
</dependency>
</dependencies>

<build>
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/org/apache/ibatis/mapping/BoundSql.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.apache.ibatis.mapping;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

public BoundSql(String sql) {
this(sql, null, null);
}

public BoundSql(String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
this(sql, parameterMappings, parameterObject, new HashMap<>());
}

public BoundSql(String sql, List<ParameterMapping> parameterMappings, Object parameterObject, Map<String, Object> additionalParameters) {
this.sql = sql;
this.parameterMappings = parameterMappings;
this.parameterObject = parameterObject;
this.additionalParameters = additionalParameters;
}

public List<ParameterMapping> getParameterMappings() {
Expand All @@ -32,4 +40,20 @@ public Object getParameterObject() {
public String getSql() {
return this.sql;
}

public void setAdditionalParameter(String key, Object value) {
additionalParameters.put(key, value);
}

public Object getAdditionalParameter(String key) {
return additionalParameters.get(key);
}

public boolean hasAdditionalParameter(String key) {
return additionalParameters.containsKey(key);
}

public Map<String, Object> getAdditionalParameters() {
return additionalParameters;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public Configuration getConfiguration() {

public BoundSql getBoundSql(Object parameterObject) {
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
return new BoundSql(boundSql.getSql(),boundSql.getParameterMappings(),parameterObject);
return new BoundSql(boundSql.getSql(), boundSql.getParameterMappings(), parameterObject, boundSql.getAdditionalParameters());
}

public ResultMap getResultMap() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ public void setParameters(PreparedStatement ps) throws SQLException {
ParameterMapping parameterMapping = parameterMappings.get(i);
Object value;
String propertyName = parameterMapping.getProperty();
if (Objects.isNull(parameterObject)) {
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
}else if (Objects.isNull(parameterObject)) {
value = null;
} else if (configuration.getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.apache.ibatis.scripting.xmltags;

import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;

/**
Expand All @@ -8,15 +10,37 @@
public class DynamicContext {

private final StringJoiner sqlBuilder = new StringJoiner(" ");
private Object parameterObject;
private final Map<String, Object> bindings = new HashMap<>();

public DynamicContext() {
}

public DynamicContext(Object parameterObject) {
this.parameterObject = parameterObject;
}

public void appendSql(String sql) {
sqlBuilder.add(sql);
}

public String getSql() {
return sqlBuilder.toString().trim();
}

public Object getParameterObject() {
return parameterObject;
}

public void setParameterObject(Object parameterObject) {
this.parameterObject = parameterObject;
}

public Map<String, Object> getBindings() {
return bindings;
}

public void bind(String key, Object value) {
bindings.put(key, value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.apache.ibatis.scripting.xmltags;

import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.session.Configuration;

/**
* @author furious 2024/4/16
*/
public class DynamicSqlSource implements SqlSource {

private final Configuration configuration;
private final SqlNode rootSqlNode;

public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
this.configuration = configuration;
this.rootSqlNode = rootSqlNode;
}

@Override
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(parameterObject);
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType);
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
context.getBindings().forEach(boundSql::setAdditionalParameter);
return boundSql;
}
}
157 changes: 157 additions & 0 deletions src/main/java/org/apache/ibatis/scripting/xmltags/ForeachSqlNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package org.apache.ibatis.scripting.xmltags;

import java.util.Map;
import java.util.Objects;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.parsing.GenericTokenParser;

import ognl.Ognl;

/**
* @author furious 2024/4/17
*/
public class ForeachSqlNode implements SqlNode {

public static final String ITEM_PREFIX = ForeachSqlNode.class.getName();

private final SqlNode contents;
private final String collection;
private final String item;
private final String open;
private final String close;
private final String separator;

public ForeachSqlNode(SqlNode contents, String collection, String item, String open, String close, String separator) {
this.contents = contents;
this.collection = collection;
this.item = item;
this.open = open;
this.close = close;
this.separator = separator;
}

@Override
@SuppressWarnings("rawtypes")
public boolean apply(DynamicContext context) {
Iterable<?> iterable = null;
try {
Object value = Ognl.getValue(Ognl.parseExpression(collection), context.getParameterObject());
if (value instanceof Iterable && ((Iterable) value).iterator().hasNext()) {
iterable = (Iterable) value;
}
} catch (Exception e) {
return false;
}
if (Objects.isNull(iterable)) {
return true;
}

applyOpen(context);
int index = 0;
for (Object o : iterable) {
DynamicContext oldContext = context;
if (index == 0) {
context = new SeparatorContext(context, "");
} else {
context = new SeparatorContext(context, separator);
}
applyItem(context, o, index);
contents.apply(new FilteredDynamicContext(context, item, index));
context = oldContext;
index++;
}
applyClose(context);
return true;
}

private void applyOpen(DynamicContext context) {
if (Objects.nonNull(open)) {
context.appendSql(open);
}
}

private void applyItem(DynamicContext context, Object o, int i) {
if (Objects.nonNull(item)) {
context.bind(item, o);
context.bind(itemizeItem(item, i), o);
}
}

private void applyClose(DynamicContext context) {
if (Objects.nonNull(close)) {
context.appendSql(close);
}
}

private static String itemizeItem(String item, int i) {
return ITEM_PREFIX + item + "_" + i;
}

private static class SeparatorContext extends DynamicContext {

private final DynamicContext delegate;
private final String separator;

public SeparatorContext(DynamicContext delegate, String separator) {
this.delegate = delegate;
this.separator = separator;
}

@Override
public Map<String, Object> getBindings() {
return delegate.getBindings();
}

@Override
public void bind(String name, Object value) {
delegate.bind(name, value);
}

@Override
public void appendSql(String sql) {
if (StringUtils.isNoneBlank(sql)) {
delegate.appendSql(separator);
}
delegate.appendSql(sql);
}

@Override
public String getSql() {
return delegate.getSql();
}
}

private static class FilteredDynamicContext extends DynamicContext {
private final DynamicContext delegate;
private final String item;
private final int index;

public FilteredDynamicContext(DynamicContext delegate, String item, int index) {
this.delegate = delegate;
this.item = item;
this.index = index;
}

@Override
public Map<String, Object> getBindings() {
return delegate.getBindings();
}

@Override
public void bind(String name, Object value) {
delegate.bind(name, value);
}

@Override
public String getSql() {
return delegate.getSql();
}

@Override
public void appendSql(String sql) {
GenericTokenParser parser = new GenericTokenParser("#{", "}", content -> "#{" + itemizeItem(item, index) + "}");
delegate.appendSql(parser.parse(sql));
}
}
}
30 changes: 30 additions & 0 deletions src/main/java/org/apache/ibatis/scripting/xmltags/IfSqlNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.apache.ibatis.scripting.xmltags;

import ognl.Ognl;

/**
* @author furious 2024/4/16
*/
public class IfSqlNode implements SqlNode {

private final String test;
private final SqlNode contents;

public IfSqlNode(String test, SqlNode contents) {
this.test = test;
this.contents = contents;
}

@Override
public boolean apply(DynamicContext context) {
try {
Object value = Ognl.getValue(Ognl.parseExpression(test), context.getParameterObject());
if (value instanceof Boolean && (Boolean) value) {
contents.apply(context);
}
return true;
} catch (Exception e) {
return false;
}
}
}
Loading

0 comments on commit 0261e21

Please sign in to comment.