From 144c93f24b179819dec9976a04f601e6f43291b6 Mon Sep 17 00:00:00 2001 From: FuriousPws002 <1938485828@qq.com> Date: Fri, 12 Apr 2024 21:32:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=9F=A5=E8=AF=A2=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E9=9B=86resultType=E7=9A=84=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- .../apache/ibatis/binding/MapperMethod.java | 15 +- .../ibatis/builder/xml/XMLMapperBuilder.java | 2 +- .../org/apache/ibatis/executor/Executor.java | 3 + .../ibatis/executor/SimpleExecutor.java | 13 ++ .../resultset/DefaultResultSetHandler.java | 141 ++++++++++++++++++ .../executor/resultset/ResultSetWrapper.java | 10 +- .../statement/PrepareStatementHandler.java | 11 ++ .../executor/statement/StatementHandler.java | 3 + .../ibatis/mapping/MappedStatement.java | 4 + .../apache/ibatis/session/Configuration.java | 6 + .../org/apache/ibatis/session/SqlSession.java | 3 + .../session/defaults/DefaultSqlSession.java | 17 ++- .../ibatis/type/IntegerTypeHandler.java | 3 +- .../org/apache/ibatis/dao/UserMapper.java | 4 + .../java/org/apache/ibatis/entity/UserDO.java | 9 ++ .../apache/ibatis/session/SqlSessionTest.java | 18 +++ src/test/resources/mapper/UserMapper.xml | 4 + 18 files changed, 261 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java diff --git a/README.md b/README.md index e4236fa..6c3c1b2 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,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")
-[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 +[4.参数绑定](https://github.com/FuriousPws002/mini-mybatis/wiki/4.%E5%8F%82%E6%95%B0%E7%BB%91%E5%AE%9A "Markdown")
+[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")
\ No newline at end of file diff --git a/src/main/java/org/apache/ibatis/binding/MapperMethod.java b/src/main/java/org/apache/ibatis/binding/MapperMethod.java index ffaf2d4..196b320 100644 --- a/src/main/java/org/apache/ibatis/binding/MapperMethod.java +++ b/src/main/java/org/apache/ibatis/binding/MapperMethod.java @@ -1,6 +1,7 @@ package org.apache.ibatis.binding; import java.lang.reflect.Method; +import java.util.Collection; import java.util.Objects; import org.apache.ibatis.mapping.MappedStatement; @@ -23,18 +24,28 @@ public MapperMethod(Class mapperInterface, Method method, Configuration confi } public Object execute(SqlSession sqlSession, Object[] args) { - Object result; + Object result = null; switch (command.getType()) { case INSERT: { result = sqlSession.insert(command.getName(), method.convertArgsToSqlCommandParam(args)); break; } + case SELECT: { + if (method.returnsMany) { + result = executeForMany(sqlSession, args); + } + break; + } default: throw new BindingException(command.getType() + " not support"); } return result; } + private Object executeForMany(SqlSession sqlSession, Object[] args) { + return sqlSession.selectList(command.getName(), method.convertArgsToSqlCommandParam(args)); + } + public static class SqlCommand { private final String name; @@ -63,9 +74,11 @@ public SqlCommandType getType() { public static class MethodSignature { private final ParamNameResolver paramNameResolver; + private final boolean returnsMany; public MethodSignature(Configuration configuration, Class mapperInterface, Method method) { this.paramNameResolver = new ParamNameResolver(method); + returnsMany = Collection.class.isAssignableFrom(method.getReturnType()); } public Object convertArgsToSqlCommandParam(Object[] args) { diff --git a/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java b/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java index 42b88c3..8439100 100644 --- a/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java +++ b/src/main/java/org/apache/ibatis/builder/xml/XMLMapperBuilder.java @@ -43,7 +43,7 @@ private void configurationElement(XNode context) { if (Objects.isNull(namespace) || namespace.isEmpty()) { throw new BuilderException("mapper namespace can not be empty"); } - buildStatementFromContext(context.evalNodes("insert")); + buildStatementFromContext(context.evalNodes("insert|select")); } catch (Exception e) { throw new BuilderException("parse mapper xml error", e); } diff --git a/src/main/java/org/apache/ibatis/executor/Executor.java b/src/main/java/org/apache/ibatis/executor/Executor.java index bd6e71d..4295a1e 100644 --- a/src/main/java/org/apache/ibatis/executor/Executor.java +++ b/src/main/java/org/apache/ibatis/executor/Executor.java @@ -1,6 +1,7 @@ package org.apache.ibatis.executor; import java.sql.SQLException; +import java.util.List; import org.apache.ibatis.mapping.MappedStatement; @@ -10,4 +11,6 @@ public interface Executor { int update(MappedStatement ms, Object parameter) throws SQLException; + + List query(MappedStatement ms, Object parameter) throws SQLException; } diff --git a/src/main/java/org/apache/ibatis/executor/SimpleExecutor.java b/src/main/java/org/apache/ibatis/executor/SimpleExecutor.java index 46af77b..544b19b 100644 --- a/src/main/java/org/apache/ibatis/executor/SimpleExecutor.java +++ b/src/main/java/org/apache/ibatis/executor/SimpleExecutor.java @@ -3,6 +3,7 @@ import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; +import java.util.List; import java.util.Objects; import org.apache.ibatis.executor.statement.StatementHandler; @@ -36,6 +37,18 @@ public int update(MappedStatement ms, Object parameter) throws SQLException { } } + @Override + public List query(MappedStatement ms, Object parameter) throws SQLException { + Statement stmt = null; + try { + StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, null); + stmt = prepareStatement(handler); + return handler.query(stmt); + } finally { + StatementUtil.closeStatement(stmt); + } + } + private Statement prepareStatement(StatementHandler handler) throws SQLException { Statement stmt; Connection connection = configuration.getDataSource().getConnection(); diff --git a/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java b/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java new file mode 100644 index 0000000..b5aca6c --- /dev/null +++ b/src/main/java/org/apache/ibatis/executor/resultset/DefaultResultSetHandler.java @@ -0,0 +1,141 @@ +package org.apache.ibatis.executor.resultset; + +import java.lang.reflect.Field; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.ResultMap; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.type.TypeHandler; +import org.apache.ibatis.type.TypeHandlerRegistry; + +/** + * @author furious 2024/4/12 + */ +public class DefaultResultSetHandler implements ResultSetHandler { + + private final Configuration configuration; + private final MappedStatement mappedStatement; + private final TypeHandlerRegistry typeHandlerRegistry; + + public DefaultResultSetHandler(MappedStatement mappedStatement) { + this.configuration = mappedStatement.getConfiguration(); + this.mappedStatement = mappedStatement; + this.typeHandlerRegistry = configuration.getTypeHandlerRegistry(); + } + + @Override + public List handleResultSets(Statement stmt) throws SQLException { + final List multipleResults = new ArrayList<>(); + ResultSet rs = stmt.getResultSet(); + if (Objects.isNull(rs)) { + return Collections.emptyList(); + } + ResultSetWrapper rsw = new ResultSetWrapper(rs, configuration); + ResultMap resultMap = mappedStatement.getResultMap(); + handleResultSet(rsw, resultMap, multipleResults); + return (List) multipleResults; + } + + private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List multipleResults) throws SQLException { + try { + handleRowValues(rsw, resultMap, multipleResults); + } finally { + closeResultSet(rsw.getResultSet()); + } + } + + private void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, List multipleResults) throws SQLException { + handleRowValuesForSimpleResultMap(rsw, resultMap, multipleResults); + } + + private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, List multipleResults) throws SQLException { + ResultSet resultSet = rsw.getResultSet(); + while (!resultSet.isClosed() && resultSet.next()) { + Object rowValue = getRowValue(rsw, resultMap); + multipleResults.add(rowValue); + } + } + + private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException { + Object rowValue = createResultObject(resultMap); + applyAutomaticMappings(rsw, rowValue); + return rowValue; + } + + private boolean applyAutomaticMappings(ResultSetWrapper rsw, Object rowValue) throws SQLException { + List autoMapping = createAutomaticMappings(rsw, rowValue); + boolean foundValues = false; + if (!autoMapping.isEmpty()) { + for (UnMappedColumnAutoMapping mapping : autoMapping) { + final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column); + if (value != null) { + foundValues = true; + } + try { + FieldUtils.writeField(rowValue, mapping.property, value, true); + } catch (IllegalAccessException e) { + ExceptionUtils.rethrow(e); + } + } + } + return foundValues; + } + + private List createAutomaticMappings(ResultSetWrapper rsw, Object rowValue) { + List autoMapping = new ArrayList<>(); + List columnLabels = rsw.getColumnLabels(); + for (String columnLabel : columnLabels) { + Field field = FieldUtils.getDeclaredField(rowValue.getClass(), columnLabel, true); + String property = Objects.isNull(field) ? null : columnLabel; + if (Objects.nonNull(property)) { + Class propertyType = field.getType(); + if (typeHandlerRegistry.hasTypeHandler(propertyType)) { + final TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(propertyType); + autoMapping.add(new UnMappedColumnAutoMapping(columnLabel, property, typeHandler)); + } + } + } + return autoMapping; + } + + private Object createResultObject(ResultMap resultMap) throws SQLException { + try { + return resultMap.getType().newInstance(); + } catch (Exception e) { + throw new SQLException("create return type fail " + e); + } + } + + private void closeResultSet(ResultSet rs) { + try { + if (rs != null) { + rs.close(); + } + } catch (SQLException ignored) { + } + } + + /** + * 数据库中列和对象中属性映射 + */ + private static class UnMappedColumnAutoMapping { + private final String column; + private final String property; + private final TypeHandler typeHandler; + + public UnMappedColumnAutoMapping(String column, String property, TypeHandler typeHandler) { + this.column = column; + this.property = property; + this.typeHandler = typeHandler; + } + } +} diff --git a/src/main/java/org/apache/ibatis/executor/resultset/ResultSetWrapper.java b/src/main/java/org/apache/ibatis/executor/resultset/ResultSetWrapper.java index 63ea26f..12c5e44 100644 --- a/src/main/java/org/apache/ibatis/executor/resultset/ResultSetWrapper.java +++ b/src/main/java/org/apache/ibatis/executor/resultset/ResultSetWrapper.java @@ -4,12 +4,9 @@ import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.apache.ibatis.session.Configuration; -import org.apache.ibatis.type.TypeHandler; import org.apache.ibatis.type.TypeHandlerRegistry; /** @@ -35,4 +32,11 @@ public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLExc } } + public ResultSet getResultSet() { + return resultSet; + } + + public List getColumnLabels() { + return columnLabels; + } } 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 e9be854..129b1aa 100644 --- a/src/main/java/org/apache/ibatis/executor/statement/PrepareStatementHandler.java +++ b/src/main/java/org/apache/ibatis/executor/statement/PrepareStatementHandler.java @@ -4,10 +4,12 @@ import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Statement; +import java.util.List; import java.util.Objects; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.parameter.ParameterHandler; +import org.apache.ibatis.executor.resultset.ResultSetHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.session.Configuration; @@ -22,6 +24,7 @@ public class PrepareStatementHandler implements StatementHandler { protected final MappedStatement mappedStatement; protected BoundSql boundSql; protected final ParameterHandler parameterHandler; + protected final ResultSetHandler resultSetHandler; public PrepareStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { this.configuration = mappedStatement.getConfiguration(); @@ -32,6 +35,7 @@ public PrepareStatementHandler(Executor executor, MappedStatement mappedStatemen } this.boundSql = boundSql; this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql); + this.resultSetHandler = configuration.newResultSetHandler(mappedStatement); } @Override @@ -50,4 +54,11 @@ public int update(Statement statement) throws SQLException { public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); } + + @Override + public List query(Statement statement) throws SQLException { + PreparedStatement ps = (PreparedStatement) statement; + ps.execute(); + return resultSetHandler.handleResultSets(ps); + } } 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 7395ac5..f50fefb 100644 --- a/src/main/java/org/apache/ibatis/executor/statement/StatementHandler.java +++ b/src/main/java/org/apache/ibatis/executor/statement/StatementHandler.java @@ -3,6 +3,7 @@ import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; +import java.util.List; /** * @author furious 2024/4/8 @@ -14,4 +15,6 @@ public interface StatementHandler { int update(Statement statement) throws SQLException; void parameterize(Statement statement) throws SQLException; + + List query(Statement statement) throws SQLException; } diff --git a/src/main/java/org/apache/ibatis/mapping/MappedStatement.java b/src/main/java/org/apache/ibatis/mapping/MappedStatement.java index 4b45d8c..ff92d84 100644 --- a/src/main/java/org/apache/ibatis/mapping/MappedStatement.java +++ b/src/main/java/org/apache/ibatis/mapping/MappedStatement.java @@ -31,6 +31,10 @@ public BoundSql getBoundSql(Object parameterObject) { return new BoundSql(boundSql.getSql(),boundSql.getParameterMappings(),parameterObject); } + public ResultMap getResultMap() { + return resultMap; + } + public static class Builder { private MappedStatement mappedStatement = new MappedStatement(); diff --git a/src/main/java/org/apache/ibatis/session/Configuration.java b/src/main/java/org/apache/ibatis/session/Configuration.java index a75f281..1974c2f 100644 --- a/src/main/java/org/apache/ibatis/session/Configuration.java +++ b/src/main/java/org/apache/ibatis/session/Configuration.java @@ -11,6 +11,8 @@ import org.apache.ibatis.binding.MapperRegistry; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.parameter.ParameterHandler; +import org.apache.ibatis.executor.resultset.DefaultResultSetHandler; +import org.apache.ibatis.executor.resultset.ResultSetHandler; import org.apache.ibatis.executor.statement.PrepareStatementHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; @@ -85,6 +87,10 @@ public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Obj return languageDriver.createParameterHandler(mappedStatement, parameterObject, boundSql); } + public ResultSetHandler newResultSetHandler(MappedStatement mappedStatement) { + return new DefaultResultSetHandler(mappedStatement); + } + public TypeHandlerRegistry getTypeHandlerRegistry() { return typeHandlerRegistry; } diff --git a/src/main/java/org/apache/ibatis/session/SqlSession.java b/src/main/java/org/apache/ibatis/session/SqlSession.java index c1f0073..76b4855 100644 --- a/src/main/java/org/apache/ibatis/session/SqlSession.java +++ b/src/main/java/org/apache/ibatis/session/SqlSession.java @@ -1,6 +1,7 @@ package org.apache.ibatis.session; import java.io.Closeable; +import java.util.List; /** * @author furious 2024/3/29 @@ -14,4 +15,6 @@ public interface SqlSession extends Closeable { int insert(String statement, Object parameter); int update(String statement, Object parameter); + + List selectList(String statement, Object parameter); } 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 0fb003d..3e306ab 100644 --- a/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java +++ b/src/main/java/org/apache/ibatis/session/defaults/DefaultSqlSession.java @@ -1,8 +1,11 @@ package org.apache.ibatis.session.defaults; import java.io.IOException; +import java.util.Collections; +import java.util.List; import java.util.Objects; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.SimpleExecutor; import org.apache.ibatis.mapping.MappedStatement; @@ -50,8 +53,20 @@ public int update(String statement, Object parameter) { MappedStatement ms = configuration.getMappedStatement(statement); return executor.update(ms, parameter); } catch (Exception e) { - throw new RuntimeException(e); + ExceptionUtils.rethrow(e); } + return -1; + } + + @Override + public List selectList(String statement, Object parameter) { + try { + MappedStatement ms = configuration.getMappedStatement(statement); + return executor.query(ms, parameter); + } catch (Exception e) { + ExceptionUtils.rethrow(e); + } + return Collections.emptyList(); } @Override diff --git a/src/main/java/org/apache/ibatis/type/IntegerTypeHandler.java b/src/main/java/org/apache/ibatis/type/IntegerTypeHandler.java index f923af1..ab709e4 100644 --- a/src/main/java/org/apache/ibatis/type/IntegerTypeHandler.java +++ b/src/main/java/org/apache/ibatis/type/IntegerTypeHandler.java @@ -15,6 +15,7 @@ protected void setNonNullParameter(PreparedStatement ps, int i, Integer paramete @Override protected Integer getNullableResult(ResultSet rs, String columnLabel) throws SQLException { - return rs.getInt(columnLabel); + int result = rs.getInt(columnLabel); + return result == 0 && rs.wasNull() ? null : result; } } diff --git a/src/test/java/org/apache/ibatis/dao/UserMapper.java b/src/test/java/org/apache/ibatis/dao/UserMapper.java index 5ab66ef..6b57765 100644 --- a/src/test/java/org/apache/ibatis/dao/UserMapper.java +++ b/src/test/java/org/apache/ibatis/dao/UserMapper.java @@ -1,5 +1,7 @@ package org.apache.ibatis.dao; +import java.util.List; + import org.apache.ibatis.annotations.Param; import org.apache.ibatis.entity.UserDO; import org.apache.ibatis.reflection.ParamNameResolver; @@ -26,4 +28,6 @@ public interface UserMapper { void insertWithoutParam(String namex); void insertWithPOJO(UserDO user); + + List select(); } diff --git a/src/test/java/org/apache/ibatis/entity/UserDO.java b/src/test/java/org/apache/ibatis/entity/UserDO.java index 1b1667f..dc58dc7 100644 --- a/src/test/java/org/apache/ibatis/entity/UserDO.java +++ b/src/test/java/org/apache/ibatis/entity/UserDO.java @@ -6,6 +6,7 @@ public class UserDO { private String name; + private String namex; private Integer age; public String getName() { @@ -23,4 +24,12 @@ public Integer getAge() { public void setAge(Integer age) { this.age = age; } + + public String getNamex() { + return namex; + } + + public void setNamex(String namex) { + this.namex = namex; + } } diff --git a/src/test/java/org/apache/ibatis/session/SqlSessionTest.java b/src/test/java/org/apache/ibatis/session/SqlSessionTest.java index ca08435..295ec34 100644 --- a/src/test/java/org/apache/ibatis/session/SqlSessionTest.java +++ b/src/test/java/org/apache/ibatis/session/SqlSessionTest.java @@ -1,9 +1,12 @@ package org.apache.ibatis.session; +import java.util.List; + import org.apache.ibatis.DataSourceBuilderTest; import org.apache.ibatis.dao.UserMapper; import org.apache.ibatis.entity.UserDO; import org.apache.ibatis.session.defaults.DefaultSqlSession; +import org.junit.Assert; import org.junit.Test; /** @@ -76,4 +79,19 @@ public void insertWithPOJO() { user.setAge(20); userMapper.insertWithPOJO(user); } + + @Test + public void queryPOJOHandleTheResult() { + Configuration configuration = new Configuration(); + configuration.setDataSource(DataSourceBuilderTest.build()); + configuration.addMapper(UserMapper.class); + SqlSession sqlSession = new DefaultSqlSession(configuration); + UserMapper userMapper = sqlSession.getMapper(UserMapper.class); + List list = userMapper.select(); + Assert.assertNotNull(list); + UserDO userDO = list.get(0); + Assert.assertNotNull(userDO.getName()); + Assert.assertNotNull(userDO.getNamex()); + } + } diff --git a/src/test/resources/mapper/UserMapper.xml b/src/test/resources/mapper/UserMapper.xml index 1e04d87..a986b05 100644 --- a/src/test/resources/mapper/UserMapper.xml +++ b/src/test/resources/mapper/UserMapper.xml @@ -16,4 +16,8 @@ INSERT INTO user (name,age,birth) VALUES (#{name},#{age},'2024-03-29 22:09:07') + + \ No newline at end of file