Skip to content

Commit

Permalink
添加if标签和trim标签支持
Browse files Browse the repository at this point in the history
  • Loading branch information
FuriousPws002 committed Apr 16, 2024
1 parent 25f7af7 commit b1af0bc
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 0 deletions.
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,28 @@
public class DynamicContext {

private final StringJoiner sqlBuilder = new StringJoiner(" ");
private Object parameterObject;

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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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);
return sqlSource.getBoundSql(parameterObject);
}
}
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;
}
}
}
89 changes: 89 additions & 0 deletions src/main/java/org/apache/ibatis/scripting/xmltags/TrimSqlNode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.apache.ibatis.scripting.xmltags;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;

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

private final SqlNode contents;
private final String prefix;
private final List<String> prefixesToOverride;

public TrimSqlNode(SqlNode contents, String prefix, String prefixesToOverride) {
this.contents = contents;
this.prefix = prefix;
this.prefixesToOverride = parseOverrides(prefixesToOverride);
}

@Override
public boolean apply(DynamicContext context) {
FilteredDynamicContext filteredDynamicContext = new FilteredDynamicContext(context);
boolean result = contents.apply(filteredDynamicContext);
filteredDynamicContext.applyAll();
return result;
}

private static List<String> parseOverrides(String overrides) {
if (StringUtils.isEmpty(overrides)) {
return Collections.emptyList();
}
return Arrays.stream(overrides.split("\\|")).collect(Collectors.toList());
}

private class FilteredDynamicContext extends DynamicContext {
private final DynamicContext delegate;
private StringBuilder sqlBuffer = new StringBuilder();

public FilteredDynamicContext(DynamicContext delegate) {
this.delegate = delegate;
}

public void applyAll() {
sqlBuffer = new StringBuilder(sqlBuffer.toString().trim());
String trimmedUppercaseSql = sqlBuffer.toString().toUpperCase();
if (trimmedUppercaseSql.length() > 0) {
applyPrefix(sqlBuffer, trimmedUppercaseSql);
}
delegate.appendSql(sqlBuffer.toString());
}

@Override
public void appendSql(String sql) {
sqlBuffer.append(sql);
}

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

@Override
public Object getParameterObject() {
return delegate.getParameterObject();
}

private void applyPrefix(StringBuilder sql, String trimmedUppercaseSql) {
if (Objects.nonNull(prefixesToOverride)) {
for (String remove : prefixesToOverride) {
if (trimmedUppercaseSql.startsWith(remove)) {
sql.delete(0, remove.trim().length());
break;
}
}
}
if (prefix != null) {
sql.insert(0, " ");
sql.insert(0, prefix);
}
}
}

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package org.apache.ibatis.scripting.xmltags;

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

import org.apache.ibatis.builder.BaseBuilder;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.scripting.defaults.RawSqlSource;
Expand All @@ -18,6 +21,8 @@ public class XMLScriptBuilder extends BaseBuilder {

private final XNode context;
private final Class<?> parameterType;
private boolean isDynamic;
private final Map<String, NodeHandler> nodeHandlerMap = new HashMap<>();

public XMLScriptBuilder(Configuration configuration, XNode context) {
this(configuration, context, null);
Expand All @@ -27,10 +32,15 @@ public XMLScriptBuilder(Configuration configuration, XNode context, Class<?> par
super(configuration);
this.context = context;
this.parameterType = parameterType;
nodeHandlerMap.put("if", new IfHandler());
nodeHandlerMap.put("trim", new TrimHandler());
}

public SqlSource parseScriptNode() {
MixedSqlNode rootSqlNode = parseDynamicTags(context);
if (isDynamic) {
return new DynamicSqlSource(configuration, rootSqlNode);
}
return new RawSqlSource(configuration, rootSqlNode, parameterType);
}

Expand All @@ -41,8 +51,49 @@ protected MixedSqlNode parseDynamicTags(XNode node) {
XNode child = node.newXNode(children.item(i));
if (child.getNode().getNodeType() == Node.TEXT_NODE) {
contents.add(new StaticTextSqlNode(child.getBody()));
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) {
String nodeName = child.getNode().getNodeName();
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler == null) {
throw new BuilderException("not support " + nodeName + " NodeHandler");
}
handler.handleNode(child, contents);
isDynamic = true;
}
}
return new MixedSqlNode(contents);
}

private interface NodeHandler {
void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);
}

/**
* 处理 <if test> 标签
*/
private class IfHandler implements NodeHandler {

@Override
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
String test = nodeToHandle.getAttribute("test");
IfSqlNode ifSqlNode = new IfSqlNode(test, mixedSqlNode);
targetContents.add(ifSqlNode);
}
}

/**
* 处理 <trim> 标签
*/
private class TrimHandler implements NodeHandler {

@Override
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
String prefix = nodeToHandle.getAttribute("prefix");
String prefixOverrides = nodeToHandle.getAttribute("prefixOverrides");
TrimSqlNode trim = new TrimSqlNode(mixedSqlNode, prefix, prefixOverrides);
targetContents.add(trim);
}
}
}

0 comments on commit b1af0bc

Please sign in to comment.