diff --git a/README.md b/README.md
index b54f5c6..e4236fa 100644
--- a/README.md
+++ b/README.md
@@ -3,4 +3,5 @@
# 功能模块
[1.注册Mapper接口](https://github.com/FuriousPws002/mini-mybatis/wiki/1.%E6%B3%A8%E5%86%8CMapper%E6%8E%A5%E5%8F%A3 "Markdown")
[2.解析xml中静态sql的mapper](https://github.com/FuriousPws002/mini-mybatis/wiki/2.%E8%A7%A3%E6%9E%90xml%E4%B8%AD%E9%9D%99%E6%80%81sql%E7%9A%84mapper "Markdown")
-[3.执行静态sql](https://github.com/FuriousPws002/mini-mybatis/wiki/3.%E6%89%A7%E8%A1%8C%E9%9D%99%E6%80%81sql "Markdown")
\ No newline at end of file
+[3.执行静态sql](https://github.com/FuriousPws002/mini-mybatis/wiki/3.%E6%89%A7%E8%A1%8C%E9%9D%99%E6%80%81sql "Markdown")
+[4.参数绑定](https://github.com/FuriousPws002/mini-mybatis/wiki/4.%E5%8F%82%E6%95%B0%E7%BB%91%E5%AE%9A "Markdown")
\ No newline at end of file
diff --git a/src/main/java/org/apache/ibatis/annotations/Param.java b/src/main/java/org/apache/ibatis/annotations/Param.java
new file mode 100644
index 0000000..6ffccd9
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/annotations/Param.java
@@ -0,0 +1,18 @@
+package org.apache.ibatis.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author furious 2024/4/10
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface Param {
+
+ String value();
+}
diff --git a/src/main/java/org/apache/ibatis/binding/MapperMethod.java b/src/main/java/org/apache/ibatis/binding/MapperMethod.java
index 8bd3488..ffaf2d4 100644
--- a/src/main/java/org/apache/ibatis/binding/MapperMethod.java
+++ b/src/main/java/org/apache/ibatis/binding/MapperMethod.java
@@ -5,6 +5,7 @@
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
+import org.apache.ibatis.reflection.ParamNameResolver;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession;
@@ -14,16 +15,18 @@
public class MapperMethod {
private final SqlCommand command;
+ private final MethodSignature method;
public MapperMethod(Class> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
+ this.method = new MethodSignature(config, mapperInterface, method);
}
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
- result = sqlSession.insert(command.getName(), args);
+ result = sqlSession.insert(command.getName(), method.convertArgsToSqlCommandParam(args));
break;
}
default:
@@ -57,4 +60,16 @@ public SqlCommandType getType() {
}
}
+ public static class MethodSignature {
+
+ private final ParamNameResolver paramNameResolver;
+
+ public MethodSignature(Configuration configuration, Class> mapperInterface, Method method) {
+ this.paramNameResolver = new ParamNameResolver(method);
+ }
+
+ public Object convertArgsToSqlCommandParam(Object[] args) {
+ return paramNameResolver.getNamedParams(args);
+ }
+ }
}
diff --git a/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java b/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java
new file mode 100644
index 0000000..bbbe8ea
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/builder/SqlSourceBuilder.java
@@ -0,0 +1,49 @@
+package org.apache.ibatis.builder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.ibatis.mapping.ParameterMapping;
+import org.apache.ibatis.mapping.SqlSource;
+import org.apache.ibatis.parsing.GenericTokenParser;
+import org.apache.ibatis.parsing.TokenHandler;
+import org.apache.ibatis.session.Configuration;
+
+/**
+ * @author furious 2024/4/10
+ */
+public class SqlSourceBuilder extends BaseBuilder {
+ public SqlSourceBuilder(Configuration configuration) {
+ super(configuration);
+ }
+
+ public SqlSource parse(String originalSql, Class> parameterType) {
+ ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration);
+ GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
+ String sql = parser.parse(originalSql);
+ return new StaticSqlSource(sql, handler.getParameterMappings());
+ }
+
+ private static class ParameterMappingTokenHandler extends BaseBuilder implements TokenHandler {
+
+ private List parameterMappings = new ArrayList<>();
+
+ public ParameterMappingTokenHandler(Configuration configuration) {
+ super(configuration);
+ }
+
+ public List getParameterMappings() {
+ return parameterMappings;
+ }
+
+ @Override
+ public String handleToken(String content) {
+ parameterMappings.add(buildParameterMapping(content));
+ return "?";
+ }
+
+ private ParameterMapping buildParameterMapping(String content) {
+ return new ParameterMapping(configuration, content);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/ibatis/builder/StaticSqlSource.java b/src/main/java/org/apache/ibatis/builder/StaticSqlSource.java
index dedbd87..8fca613 100644
--- a/src/main/java/org/apache/ibatis/builder/StaticSqlSource.java
+++ b/src/main/java/org/apache/ibatis/builder/StaticSqlSource.java
@@ -1,6 +1,9 @@
package org.apache.ibatis.builder;
+import java.util.List;
+
import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlSource;
/**
@@ -9,13 +12,19 @@
public class StaticSqlSource implements SqlSource {
private final String sql;
+ private final List parameterMappings;
public StaticSqlSource(String sql) {
+ this(sql, null);
+ }
+
+ public StaticSqlSource(String sql, List parameterMappings) {
this.sql = sql;
+ this.parameterMappings = parameterMappings;
}
@Override
public BoundSql getBoundSql(Object parameterObject) {
- return new BoundSql(this.sql);
+ return new BoundSql(sql, parameterMappings, parameterObject);
}
}
diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java b/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java
index a0b2fe0..d8d0ba5 100644
--- a/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java
+++ b/src/main/java/org/apache/ibatis/builder/xml/XMLStatementBuilder.java
@@ -35,7 +35,7 @@ public void parseStatementNode() {
String id = context.getAttribute("id");
String nodeName = context.getName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
- SqlSource sqlSource = new StaticSqlSource(context.getBody());
+ SqlSource sqlSource = configuration.getLanguageDriver().createSqlSource(configuration, context, null);
builderAssistant.addMappedStatement(id, sqlCommandType, sqlSource);
}
}
diff --git a/src/main/java/org/apache/ibatis/executor/SimpleExecutor.java b/src/main/java/org/apache/ibatis/executor/SimpleExecutor.java
index 0af4dbf..46af77b 100644
--- a/src/main/java/org/apache/ibatis/executor/SimpleExecutor.java
+++ b/src/main/java/org/apache/ibatis/executor/SimpleExecutor.java
@@ -41,6 +41,7 @@ private Statement prepareStatement(StatementHandler handler) throws SQLException
Connection connection = configuration.getDataSource().getConnection();
connection.setAutoCommit(true);
stmt = handler.prepare(connection, null);
+ handler.parameterize(stmt);
return stmt;
}
diff --git a/src/main/java/org/apache/ibatis/executor/parameter/ParameterHandler.java b/src/main/java/org/apache/ibatis/executor/parameter/ParameterHandler.java
new file mode 100644
index 0000000..94ba67b
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/executor/parameter/ParameterHandler.java
@@ -0,0 +1,14 @@
+package org.apache.ibatis.executor.parameter;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * @author furious 2024/4/10
+ */
+public interface ParameterHandler {
+
+ Object getParameterObject();
+
+ void setParameters(PreparedStatement ps) throws SQLException;
+}
diff --git a/src/main/java/org/apache/ibatis/executor/statement/PrepareStatementHandler.java b/src/main/java/org/apache/ibatis/executor/statement/PrepareStatementHandler.java
index e00c449..e9be854 100644
--- a/src/main/java/org/apache/ibatis/executor/statement/PrepareStatementHandler.java
+++ b/src/main/java/org/apache/ibatis/executor/statement/PrepareStatementHandler.java
@@ -7,6 +7,7 @@
import java.util.Objects;
import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
@@ -20,6 +21,7 @@ public class PrepareStatementHandler implements StatementHandler {
protected final Executor executor;
protected final MappedStatement mappedStatement;
protected BoundSql boundSql;
+ protected final ParameterHandler parameterHandler;
public PrepareStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
@@ -29,6 +31,7 @@ public PrepareStatementHandler(Executor executor, MappedStatement mappedStatemen
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
+ this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
}
@Override
@@ -42,4 +45,9 @@ public int update(Statement statement) throws SQLException {
ps.execute();
return ps.getUpdateCount();
}
+
+ @Override
+ public void parameterize(Statement statement) throws SQLException {
+ parameterHandler.setParameters((PreparedStatement) statement);
+ }
}
diff --git a/src/main/java/org/apache/ibatis/executor/statement/StatementHandler.java b/src/main/java/org/apache/ibatis/executor/statement/StatementHandler.java
index ac02e3d..7395ac5 100644
--- a/src/main/java/org/apache/ibatis/executor/statement/StatementHandler.java
+++ b/src/main/java/org/apache/ibatis/executor/statement/StatementHandler.java
@@ -12,4 +12,6 @@ public interface StatementHandler {
Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;
int update(Statement statement) throws SQLException;
+
+ void parameterize(Statement statement) throws SQLException;
}
diff --git a/src/main/java/org/apache/ibatis/mapping/BoundSql.java b/src/main/java/org/apache/ibatis/mapping/BoundSql.java
index b9a55a7..83ac02c 100644
--- a/src/main/java/org/apache/ibatis/mapping/BoundSql.java
+++ b/src/main/java/org/apache/ibatis/mapping/BoundSql.java
@@ -1,14 +1,32 @@
package org.apache.ibatis.mapping;
+import java.util.List;
+
/**
* @author furious 2024/4/7
*/
public class BoundSql {
private final String sql;
+ private final List parameterMappings;
+ private final Object parameterObject;
public BoundSql(String sql) {
+ this(sql, null, null);
+ }
+
+ public BoundSql(String sql, List parameterMappings, Object parameterObject) {
this.sql = sql;
+ this.parameterMappings = parameterMappings;
+ this.parameterObject = parameterObject;
+ }
+
+ public List getParameterMappings() {
+ return parameterMappings;
+ }
+
+ public Object getParameterObject() {
+ return parameterObject;
}
public String getSql() {
diff --git a/src/main/java/org/apache/ibatis/mapping/MappedStatement.java b/src/main/java/org/apache/ibatis/mapping/MappedStatement.java
index d1bb08b..8f1d452 100644
--- a/src/main/java/org/apache/ibatis/mapping/MappedStatement.java
+++ b/src/main/java/org/apache/ibatis/mapping/MappedStatement.java
@@ -27,7 +27,7 @@ public Configuration getConfiguration() {
public BoundSql getBoundSql(Object parameterObject) {
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
- return new BoundSql(boundSql.getSql());
+ return new BoundSql(boundSql.getSql(),boundSql.getParameterMappings(),parameterObject);
}
public static class Builder {
diff --git a/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java b/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java
new file mode 100644
index 0000000..cc5f519
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/mapping/ParameterMapping.java
@@ -0,0 +1,56 @@
+package org.apache.ibatis.mapping;
+
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.type.TypeHandler;
+
+/**
+ * @author furious 2024/4/10
+ */
+public class ParameterMapping {
+
+ private Configuration configuration;
+ private String property;
+ private Class> javaType = Object.class;
+ private TypeHandler typeHandler;
+
+ public ParameterMapping() {
+ }
+
+ public ParameterMapping(Configuration configuration, String property) {
+ this.configuration = configuration;
+ this.property = property;
+ this.typeHandler = configuration.getTypeHandlerRegistry().getUnknownTypeHandler();
+ }
+
+ public Configuration getConfiguration() {
+ return configuration;
+ }
+
+ public void setConfiguration(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ public String getProperty() {
+ return property;
+ }
+
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ public Class> getJavaType() {
+ return javaType;
+ }
+
+ public void setJavaType(Class> javaType) {
+ this.javaType = javaType;
+ }
+
+ public TypeHandler getTypeHandler() {
+ return typeHandler;
+ }
+
+ public void setTypeHandler(TypeHandler typeHandler) {
+ this.typeHandler = typeHandler;
+ }
+}
diff --git a/src/main/java/org/apache/ibatis/parsing/GenericTokenParser.java b/src/main/java/org/apache/ibatis/parsing/GenericTokenParser.java
new file mode 100644
index 0000000..b0abbf0
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/parsing/GenericTokenParser.java
@@ -0,0 +1,31 @@
+package org.apache.ibatis.parsing;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * @author furious 2024/4/10
+ */
+public class GenericTokenParser {
+
+ private final String openToken;
+ private final String closeToken;
+ private final TokenHandler handler;
+
+ public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
+ this.openToken = openToken;
+ this.closeToken = closeToken;
+ this.handler = handler;
+ }
+
+ public String parse(String text) {
+ String[] tokens = StringUtils.substringsBetween(text, openToken, closeToken);
+ if (ArrayUtils.isEmpty(tokens)) {
+ return text;
+ }
+ for (String token : tokens) {
+ text = text.replace(openToken + token + closeToken, handler.handleToken(token));
+ }
+ return text;
+ }
+}
diff --git a/src/main/java/org/apache/ibatis/parsing/TokenHandler.java b/src/main/java/org/apache/ibatis/parsing/TokenHandler.java
new file mode 100644
index 0000000..f1cf802
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/parsing/TokenHandler.java
@@ -0,0 +1,9 @@
+package org.apache.ibatis.parsing;
+
+/**
+ * @author furious 2024/4/10
+ */
+public interface TokenHandler {
+
+ String handleToken(String content);
+}
diff --git a/src/main/java/org/apache/ibatis/parsing/XNode.java b/src/main/java/org/apache/ibatis/parsing/XNode.java
index fdafaaa..e664858 100644
--- a/src/main/java/org/apache/ibatis/parsing/XNode.java
+++ b/src/main/java/org/apache/ibatis/parsing/XNode.java
@@ -30,6 +30,10 @@ public XNode(XPathParser xpathParser, Node node) {
this.body = parseBody(node);
}
+ public XNode newXNode(Node node) {
+ return new XNode(xpathParser, node);
+ }
+
public T getAttribute(String name) {
return (T) attributes.getProperty(name);
}
@@ -46,6 +50,10 @@ public String getName() {
return name;
}
+ public Node getNode() {
+ return node;
+ }
+
private Properties parseAttributes(Node n) {
Properties attributes = new Properties();
NamedNodeMap attributeNodes = n.getAttributes();
diff --git a/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java b/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java
new file mode 100644
index 0000000..d60e2a2
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/reflection/ParamNameResolver.java
@@ -0,0 +1,63 @@
+package org.apache.ibatis.reflection;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * @author furious 2024/4/10
+ */
+public class ParamNameResolver {
+
+ private boolean hasParamAnnotation;
+
+ /**
+ * map中key为参数序号,value为参数名称
+ * method(@Param("M") int a, @Param("N") int b) -> {{0, "M"}, {1, "N"}}
+ */
+ private final SortedMap names;
+
+ public ParamNameResolver(Method method) {
+ final Annotation[][] paramAnnotations = method.getParameterAnnotations();
+ final SortedMap map = new TreeMap<>();
+ int paramCount = paramAnnotations.length;
+ for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
+ String name = null;
+ for (Annotation annotation : paramAnnotations[paramIndex]) {
+ if (annotation instanceof Param) {
+ name = ((Param) annotation).value();
+ hasParamAnnotation = true;
+ break;
+ }
+ }
+ if (Objects.isNull(name)) {
+ name = method.getParameters()[paramIndex].getName();
+ }
+ map.put(paramIndex, name);
+ }
+ names = Collections.unmodifiableSortedMap(map);
+ }
+
+ public Object getNamedParams(Object[] args) {
+ final int paramCount = names.size();
+ if (Objects.isNull(args) || paramCount == 0) {
+ return null;
+ } else if (!hasParamAnnotation && paramCount == 1) {
+ //没有Param注解,同时只有一个参数,直接返回这个参数值
+ return args[0];
+ } else {
+ final Map param = new HashMap<>();
+ for (Map.Entry entry : names.entrySet()) {
+ param.put(entry.getValue(), args[entry.getKey()]);
+ }
+ return param;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/ibatis/scripting/LanguageDriver.java b/src/main/java/org/apache/ibatis/scripting/LanguageDriver.java
new file mode 100644
index 0000000..3f1d21a
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/scripting/LanguageDriver.java
@@ -0,0 +1,18 @@
+package org.apache.ibatis.scripting;
+
+import org.apache.ibatis.executor.parameter.ParameterHandler;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlSource;
+import org.apache.ibatis.parsing.XNode;
+import org.apache.ibatis.session.Configuration;
+
+/**
+ * @author furious 2024/4/10
+ */
+public interface LanguageDriver {
+
+ ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
+
+ SqlSource createSqlSource(Configuration configuration, XNode script, Class> parameterType);
+}
diff --git a/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java b/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java
new file mode 100644
index 0000000..952dade
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/scripting/defaults/DefaultParameterHandler.java
@@ -0,0 +1,68 @@
+package org.apache.ibatis.scripting.defaults;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.ibatis.executor.parameter.ParameterHandler;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.ParameterMapping;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.type.TypeException;
+
+/**
+ * @author furious 2024/4/10
+ */
+public class DefaultParameterHandler implements ParameterHandler {
+
+ private final MappedStatement mappedStatement;
+ private final Object parameterObject;
+ private final BoundSql boundSql;
+ private final Configuration configuration;
+
+ public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
+ this.mappedStatement = mappedStatement;
+ this.configuration = mappedStatement.getConfiguration();
+ this.parameterObject = parameterObject;
+ this.boundSql = boundSql;
+ }
+
+ @Override
+ public Object getParameterObject() {
+ return parameterObject;
+ }
+
+ @Override
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public void setParameters(PreparedStatement ps) throws SQLException {
+ List parameterMappings = boundSql.getParameterMappings();
+ if (Objects.isNull(parameterMappings)) {
+ return;
+ }
+ for (int i = 0; i < parameterMappings.size(); i++) {
+ ParameterMapping parameterMapping = parameterMappings.get(i);
+ Object value;
+ String propertyName = parameterMapping.getProperty();
+ if (Objects.isNull(parameterObject)) {
+ value = null;
+ } else if (configuration.getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass())) {
+ value = parameterObject;
+ } else if (parameterObject instanceof Map) {
+ //parameterObject为map,org.apache.ibatis.reflection.ParamNameResolver.getNamedParams
+ value = ((Map) parameterObject).get(propertyName);
+ } else {
+ //pojo对象,通过反射获取对象值
+ try {
+ value = FieldUtils.readField(parameterObject, propertyName, true);
+ } catch (Exception e) {
+ throw new TypeException(e);
+ }
+ }
+ parameterMapping.getTypeHandler().setParameter(ps, i + 1, value);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/ibatis/scripting/defaults/RawSqlSource.java b/src/main/java/org/apache/ibatis/scripting/defaults/RawSqlSource.java
new file mode 100644
index 0000000..63bf0b4
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/scripting/defaults/RawSqlSource.java
@@ -0,0 +1,37 @@
+package org.apache.ibatis.scripting.defaults;
+
+import org.apache.ibatis.builder.SqlSourceBuilder;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.SqlSource;
+import org.apache.ibatis.scripting.xmltags.DynamicContext;
+import org.apache.ibatis.scripting.xmltags.SqlNode;
+import org.apache.ibatis.session.Configuration;
+
+/**
+ * @author furious 2024/4/10
+ */
+public class RawSqlSource implements SqlSource {
+
+ private final SqlSource sqlSource;
+
+ public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class> parameterType) {
+ this(configuration, getSql(rootSqlNode), parameterType);
+ }
+
+ public RawSqlSource(Configuration configuration, String sql, Class> parameterType) {
+ SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
+ Class> clazz = parameterType == null ? Object.class : parameterType;
+ sqlSource = sqlSourceParser.parse(sql, clazz);
+ }
+
+ private static String getSql(SqlNode rootSqlNode) {
+ DynamicContext context = new DynamicContext();
+ rootSqlNode.apply(context);
+ return context.getSql();
+ }
+
+ @Override
+ public BoundSql getBoundSql(Object parameterObject) {
+ return sqlSource.getBoundSql(parameterObject);
+ }
+}
diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java
new file mode 100644
index 0000000..15822b8
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/scripting/xmltags/DynamicContext.java
@@ -0,0 +1,22 @@
+package org.apache.ibatis.scripting.xmltags;
+
+import java.util.StringJoiner;
+
+/**
+ * @author furious 2024/4/10
+ */
+public class DynamicContext {
+
+ private final StringJoiner sqlBuilder = new StringJoiner(" ");
+
+ public DynamicContext() {
+ }
+
+ public void appendSql(String sql) {
+ sqlBuilder.add(sql);
+ }
+
+ public String getSql() {
+ return sqlBuilder.toString().trim();
+ }
+}
diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/MixedSqlNode.java b/src/main/java/org/apache/ibatis/scripting/xmltags/MixedSqlNode.java
new file mode 100644
index 0000000..ac9189a
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/scripting/xmltags/MixedSqlNode.java
@@ -0,0 +1,21 @@
+package org.apache.ibatis.scripting.xmltags;
+
+import java.util.List;
+
+/**
+ * @author furious 2024/4/10
+ */
+public class MixedSqlNode implements SqlNode {
+
+ private final List contents;
+
+ public MixedSqlNode(List contents) {
+ this.contents = contents;
+ }
+
+ @Override
+ public boolean apply(DynamicContext context) {
+ contents.forEach(node -> node.apply(context));
+ return true;
+ }
+}
diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/SqlNode.java b/src/main/java/org/apache/ibatis/scripting/xmltags/SqlNode.java
new file mode 100644
index 0000000..de0bdfe
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/scripting/xmltags/SqlNode.java
@@ -0,0 +1,9 @@
+package org.apache.ibatis.scripting.xmltags;
+
+/**
+ * @author furious 2024/4/10
+ */
+public interface SqlNode {
+
+ boolean apply(DynamicContext context);
+}
diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNode.java b/src/main/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNode.java
new file mode 100644
index 0000000..5d95d09
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/scripting/xmltags/StaticTextSqlNode.java
@@ -0,0 +1,19 @@
+package org.apache.ibatis.scripting.xmltags;
+
+/**
+ * @author furious 2024/4/10
+ */
+public class StaticTextSqlNode implements SqlNode {
+
+ private final String text;
+
+ public StaticTextSqlNode(String text) {
+ this.text = text;
+ }
+
+ @Override
+ public boolean apply(DynamicContext context) {
+ context.appendSql(text);
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/XMLLanguageDriver.java b/src/main/java/org/apache/ibatis/scripting/xmltags/XMLLanguageDriver.java
new file mode 100644
index 0000000..4c12d8e
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/scripting/xmltags/XMLLanguageDriver.java
@@ -0,0 +1,26 @@
+package org.apache.ibatis.scripting.xmltags;
+
+import org.apache.ibatis.executor.parameter.ParameterHandler;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlSource;
+import org.apache.ibatis.parsing.XNode;
+import org.apache.ibatis.scripting.LanguageDriver;
+import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
+import org.apache.ibatis.session.Configuration;
+
+/**
+ * @author furious 2024/4/10
+ */
+public class XMLLanguageDriver implements LanguageDriver {
+
+ @Override
+ public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
+ return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
+ }
+
+ @Override
+ public SqlSource createSqlSource(Configuration configuration, XNode script, Class> parameterType) {
+ return new XMLScriptBuilder(configuration, script, parameterType).parseScriptNode();
+ }
+}
diff --git a/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java b/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java
new file mode 100644
index 0000000..b0a82c5
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/scripting/xmltags/XMLScriptBuilder.java
@@ -0,0 +1,48 @@
+package org.apache.ibatis.scripting.xmltags;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.ibatis.builder.BaseBuilder;
+import org.apache.ibatis.mapping.SqlSource;
+import org.apache.ibatis.parsing.XNode;
+import org.apache.ibatis.scripting.defaults.RawSqlSource;
+import org.apache.ibatis.session.Configuration;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * @author furious 2024/4/10
+ */
+public class XMLScriptBuilder extends BaseBuilder {
+
+ private final XNode context;
+ private final Class> parameterType;
+
+ public XMLScriptBuilder(Configuration configuration, XNode context) {
+ this(configuration, context, null);
+ }
+
+ public XMLScriptBuilder(Configuration configuration, XNode context, Class> parameterType) {
+ super(configuration);
+ this.context = context;
+ this.parameterType = parameterType;
+ }
+
+ public SqlSource parseScriptNode() {
+ MixedSqlNode rootSqlNode = parseDynamicTags(context);
+ return new RawSqlSource(configuration, rootSqlNode, parameterType);
+ }
+
+ protected MixedSqlNode parseDynamicTags(XNode node) {
+ List contents = new ArrayList<>();
+ NodeList children = node.getNode().getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ XNode child = node.newXNode(children.item(i));
+ if (child.getNode().getNodeType() == Node.TEXT_NODE) {
+ contents.add(new StaticTextSqlNode(child.getBody()));
+ }
+ }
+ return new MixedSqlNode(contents);
+ }
+}
diff --git a/src/main/java/org/apache/ibatis/session/Configuration.java b/src/main/java/org/apache/ibatis/session/Configuration.java
index 7417149..a75f281 100644
--- a/src/main/java/org/apache/ibatis/session/Configuration.java
+++ b/src/main/java/org/apache/ibatis/session/Configuration.java
@@ -10,10 +10,13 @@
import org.apache.ibatis.binding.MapperRegistry;
import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.PrepareStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.scripting.LanguageDriver;
+import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.type.TypeHandlerRegistry;
/**
@@ -29,6 +32,7 @@ public class Configuration {
private final Set loadedResources = new HashSet<>();
private final Map mappedStatements = new HashMap<>();
private final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
+ private final LanguageDriver languageDriver = new XMLLanguageDriver();
public Configuration() {
}
@@ -77,7 +81,15 @@ public StatementHandler newStatementHandler(Executor executor, MappedStatement m
return new PrepareStatementHandler(executor, mappedStatement, parameterObject, boundSql);
}
+ public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
+ return languageDriver.createParameterHandler(mappedStatement, parameterObject, boundSql);
+ }
+
public TypeHandlerRegistry getTypeHandlerRegistry() {
return typeHandlerRegistry;
}
+
+ public LanguageDriver getLanguageDriver() {
+ return languageDriver;
+ }
}
diff --git a/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java b/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java
index 000e5ad..0fb003d 100644
--- a/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java
+++ b/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java
@@ -48,7 +48,7 @@ public int insert(String statement, Object parameter) {
public int update(String statement, Object parameter) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
- return executor.update(ms, null);
+ return executor.update(ms, parameter);
} catch (Exception e) {
throw new RuntimeException(e);
}
diff --git a/src/main/java/org/apache/ibatis/type/TypeException.java b/src/main/java/org/apache/ibatis/type/TypeException.java
new file mode 100644
index 0000000..c8e3ee6
--- /dev/null
+++ b/src/main/java/org/apache/ibatis/type/TypeException.java
@@ -0,0 +1,27 @@
+package org.apache.ibatis.type;
+
+/**
+ * @author furious 2024/4/10
+ */
+public class TypeException extends RuntimeException{
+
+ public TypeException() {
+ super();
+ }
+
+ public TypeException(String message) {
+ super(message);
+ }
+
+ public TypeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public TypeException(Throwable cause) {
+ super(cause);
+ }
+
+ protected TypeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+}
diff --git a/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java b/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java
index 8f640fe..0fd398d 100644
--- a/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java
+++ b/src/main/java/org/apache/ibatis/type/TypeHandlerRegistry.java
@@ -2,6 +2,7 @@
import java.util.HashMap;
import java.util.Map;
+import java.util.Objects;
/**
* @author furious 2024/4/9
@@ -32,4 +33,8 @@ public TypeHandler getTypeHandler(Class type) {
public TypeHandler