手撸Mybatis(四)——连接数据库进行简单查询

本专栏的源码:https://gitee.com/dhi-chen-xiaoyang/yang-mybatis。

添加数据库操作模板

对于JDBC操作,一般包括以下几个步骤:
1)注册驱动
2)建立连接
3)执行sql语句
4)处理结果
5)释放资源
上面这些步骤,真正和我们处理相关的,是第三步和第四步,其他步骤,都是通用的逻辑,因此,我们可以将这些步骤抽象成一个模板方法类,其内容如下:

package com.yang.mybatis.session;import com.yang.mybatis.config.MybatisDataSource;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public abstract class TransactionInvoke<T> {public T invoke(MybatisDataSource mybatisDataSource) {String username = mybatisDataSource.getUsername();String password = mybatisDataSource.getPassword();String url = mybatisDataSource.getUrl();String driver = mybatisDataSource.getDriver();Connection connection = null;T result = null;try {Class.forName(driver);connection = DriverManager.getConnection(url, username, password);connection.setAutoCommit(false);result = execute(connection);connection.commit();} catch (SQLException e) {try {connection.rollback();} catch (SQLException ex) {throw new RuntimeException(ex);}} catch (ClassNotFoundException e) {throw new RuntimeException(e);} finally {closeResource(connection);}return result;}private void closeResource(Connection connection) {if (connection != null) {try {connection.close();} catch (SQLException e) {throw new RuntimeException(e);}}}public abstract T execute(Connection connection) throws SQLException;
}

因为涉及到数据库操作,所以我们要先引入mysql的依赖:

  <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.27</version></dependency>

查询某个字段

首先,我们修改DefaultMybatisSqlSession中,在该类中,执行我们的sql语句,其中,执行sql和对sql查询结果进行处理的内容,收敛在execute方法中。

package com.yang.mybatis.session;import com.yang.mybatis.config.MybatisConfiguration;
import com.yang.mybatis.config.MybatisEnvironment;
import com.yang.mybatis.config.MybatisSqlStatement;
import com.yang.mybatis.proxy.MapperProxyFactory;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;public class DefaultMybatisSqlSession implements IMybatisSqlSession {private MapperProxyFactory mapperProxyFactory;private MybatisConfiguration mybatisConfiguration;public DefaultMybatisSqlSession(MapperProxyFactory mapperProxyFactory) {this.mapperProxyFactory = mapperProxyFactory;this.mybatisConfiguration = mapperProxyFactory.getMybatisConfiguration();}@Overridepublic <T> T execute(String method, Object parameter) {Map<String, MybatisSqlStatement> mapperMethod2SqlStatementsMap = mapperProxyFactory.getMybatisConfiguration().getMapperMethod2SqlStatementsMap();MybatisSqlStatement mybatisSqlStatement = mapperMethod2SqlStatementsMap.get(method);MybatisEnvironment defaultMybatisEnvironment = this.mybatisConfiguration.getDefaultMybatisEnvironment();return new TransactionInvoke<T>() {@Overridepublic T execute(Connection connection) throws SQLException {String rawSql = mybatisSqlStatement.getSql();List<String> parameterNameList = new ArrayList<>();String sql = extractRawSql(rawSql, parameterNameList);Object[] parameters = (Object[]) parameter;PreparedStatement preparedStatement = connection.prepareStatement(sql);if (parameterNameList.size() != parameters.length) {throw new RuntimeException("SQL语句参数个数不匹配====");}int index = 1;for (Object o : parameters) {preparedStatement.setObject(index ++, o);}ResultSet resultSet = preparedStatement.executeQuery();T result = null;if (resultSet.next()) {result = (T) resultSet.getObject(1);}resultSet.close();return result;}}.invoke(defaultMybatisEnvironment.getMybatisDataSource());}private String extractRawSql(String rawSql, List<String> parameterNameList) {StringBuilder sqlBuilder = new StringBuilder();int start = 0;int end = -1;while ((end = rawSql.indexOf("#", start)) != -1) {sqlBuilder.append(rawSql.substring(start, end - 1)).append(" ? ");int parameterStart = end + 2;int parameterEnd = rawSql.indexOf("}", parameterStart);parameterNameList.add(rawSql.substring(parameterStart, parameterEnd));start = parameterEnd + 1;}sqlBuilder.append(rawSql.substring(start));return sqlBuilder.toString();}@Overridepublic <T> T getMapper(Class<T> type) {return (T) mapperProxyFactory.newInstance(type, this);}
}

最后我们添加测试方法,进行测试:

String configPath = "mybatis-config.xml";IMybatisSqlSessionFactory mybatisSqlSessionFactory = new MybatisSqlSessionFactoryBuilder().setMybatisMapperParser(new XmlMybatisMapperParser()).setMybatisConfigurationParser(new XmlMybatisConfigurationParser()).setConfigPath(configPath).buildSqlSessionFactory();IMybatisSqlSession mybatisSqlSession = mybatisSqlSessionFactory.openSession();IUserMapper userMapper = mybatisSqlSession.getMapper(IUserMapper.class);System.out.println(userMapper.queryUserName(1));

测试结果如下:
image.png

查询结果封装为对象

基于resultType

上述的操作,只对查询某个字段有效,假设我们要获取的是一个对象,比如我们在UserMapper添加如下方法:

    User queryUserById(Integer id);

其中,User类内容如下:


public class User implements Serializable {private Integer id;private String userName;private String password;private Integer age;private LocalDateTime createTime;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public LocalDateTime getCreateTime() {return createTime;}public void setCreateTime(LocalDateTime createTime) {this.createTime = createTime;}
}

该方法对于的mapperxml查询块如下:

  <select id="queryUserById" resultType="com.yang.mybatis.test.User">select * from userwhere id = #{id}</select>

此时,当我们使用JDBC执行sql,获取ResultSet后,我们可以根据resultType的类型,通过反射的方式,来创建对应的结果,并将属性填充到对象中。
首先,我们修改MybatisSqlStatement,添加resultType字段

package com.yang.mybatis.config;import java.io.Serializable;public class MybatisSqlStatement implements Serializable {private String namespace;private String id;private String sql;private String resultType;public String getNamespace() {return namespace;}public void setNamespace(String namespace) {this.namespace = namespace;}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getSql() {return sql;}public void setSql(String sql) {this.sql = sql;}public String getResultType() {return resultType;}public void setResultType(String resultType) {this.resultType = resultType;}
}

然后修改XmlMybatisMapperParser的parseStatement方法,设置对应的resultType值

private void parseStatement(List<MybatisSqlStatement> mybatisSqlStatements, List<Element> elements, Element root) {if (elements == null || elements.isEmpty()) {return;}String namespace = root.attributeValue("namespace");for (Element element : elements) {String id = element.attributeValue("id");String resultType = element.attributeValue("resultType");String sql = element.getText().trim();MybatisSqlStatement mybatisSqlStatement = new MybatisSqlStatement();mybatisSqlStatement.setNamespace(namespace);mybatisSqlStatement.setId(id);mybatisSqlStatement.setSql(sql);mybatisSqlStatement.setResultType(resultType);mybatisSqlStatements.add(mybatisSqlStatement);}}

最后修改DefaultMybatisSqlSession:

package com.yang.mybatis.session;import com.google.common.base.CaseFormat;
import com.yang.mybatis.config.MybatisConfiguration;
import com.yang.mybatis.config.MybatisEnvironment;
import com.yang.mybatis.config.MybatisSqlStatement;
import com.yang.mybatis.proxy.MapperProxyFactory;import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class DefaultMybatisSqlSession implements IMybatisSqlSession {private MapperProxyFactory mapperProxyFactory;private MybatisConfiguration mybatisConfiguration;public DefaultMybatisSqlSession(MapperProxyFactory mapperProxyFactory) {this.mapperProxyFactory = mapperProxyFactory;this.mybatisConfiguration = mapperProxyFactory.getMybatisConfiguration();}@Overridepublic <T> T execute(String method, Object parameter) {Map<String, MybatisSqlStatement> mapperMethod2SqlStatementsMap = mapperProxyFactory.getMybatisConfiguration().getMapperMethod2SqlStatementsMap();MybatisSqlStatement mybatisSqlStatement = mapperMethod2SqlStatementsMap.get(method);MybatisEnvironment defaultMybatisEnvironment = this.mybatisConfiguration.getDefaultMybatisEnvironment();return new TransactionInvoke<T>() {@Overridepublic T execute(Connection connection) throws SQLException {String rawSql = mybatisSqlStatement.getSql();List<String> parameterNameList = new ArrayList<>();String sql = extractRawSql(rawSql, parameterNameList);Object[] parameters = (Object[]) parameter;PreparedStatement preparedStatement = connection.prepareStatement(sql);if (parameterNameList.size() != parameters.length) {throw new RuntimeException("SQL语句参数个数不匹配====");}int index = 1;for (Object o : parameters) {preparedStatement.setObject(index ++, o);}ResultSet resultSet = preparedStatement.executeQuery();T result = parseResult(resultSet, mybatisSqlStatement);resultSet.close();return result;}}.invoke(defaultMybatisEnvironment.getMybatisDataSource());}private <T> T parseResult(ResultSet resultSet, MybatisSqlStatement mybatisSqlStatement) throws SQLException {String resultType = mybatisSqlStatement.getResultType();if (resultType == null || resultType.isEmpty()) {return (T) resultSet.getObject(1);}try {Class<?> aClass = Class.forName(resultType);Field[] fields = aClass.getDeclaredFields();Map<String, String> fieldName2ColumnNameMap = new HashMap<>();Map<String, Field> fieldName2FieldMap = new HashMap<>();for (Field field : fields) {// 驼峰命名转下划线String columnName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName());fieldName2ColumnNameMap.put(field.getName(), columnName);fieldName2FieldMap.put(field.getName(), field);}Object result = aClass.newInstance();while (resultSet.next()) {for (Map.Entry<String, String> entry : fieldName2ColumnNameMap.entrySet()) {String fieldName = entry.getKey();String columnName = entry.getValue();Object columnValue = resultSet.getObject(columnName);Field field = fieldName2FieldMap.get(fieldName);field.setAccessible(true);field.set(result, columnValue);}}return (T)result;} catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (InstantiationException | IllegalAccessException e) {throw new RuntimeException(e);}}private String extractRawSql(String rawSql, List<String> parameterNameList) {StringBuilder sqlBuilder = new StringBuilder();int start = 0;int end = -1;while ((end = rawSql.indexOf("#", start)) != -1) {sqlBuilder.append(rawSql.substring(start, end - 1)).append(" ? ");int parameterStart = end + 2;int parameterEnd = rawSql.indexOf("}", parameterStart);parameterNameList.add(rawSql.substring(parameterStart, parameterEnd));start = parameterEnd + 1;}sqlBuilder.append(rawSql.substring(start));return sqlBuilder.toString();}@Overridepublic <T> T getMapper(Class<T> type) {return (T) mapperProxyFactory.newInstance(type, this);}
}

这里将和结果相关的解析,抽取到parseResult方法中,此外,因为数据库是字段是下划线格式,类的属性是驼峰格式,因此,这里引入了Guava依赖,方便使用它的CaseFormat类进行格式转化。

  <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.1-jre</version></dependency>

最后,我们添加测试方法,进行测试:

 public static void main(String[] args) {String configPath = "mybatis-config.xml";IMybatisSqlSessionFactory mybatisSqlSessionFactory = new MybatisSqlSessionFactoryBuilder().setMybatisMapperParser(new XmlMybatisMapperParser()).setMybatisConfigurationParser(new XmlMybatisConfigurationParser()).setConfigPath(configPath).buildSqlSessionFactory();IMybatisSqlSession mybatisSqlSession = mybatisSqlSessionFactory.openSession();IUserMapper userMapper = mybatisSqlSession.getMapper(IUserMapper.class);User user = userMapper.queryUserById(1);System.out.println(user);System.out.println(user.getId());System.out.println(user.getUserName());System.out.println(user.getPassword());System.out.println(user.getAge());System.out.println(user.getCreateTime());}

测试结果如下:
image.png

基于resultMap

上面的方式,是基于resultType来进行解析的,但是在mybatis中,还有另外一种将sql字段和类对象属性映射的方式,就是resultMap。
首先,我们创建一个IdUserNameVO类

package com.yang.mybatis.test;import java.io.Serializable;public class IdUserNameVO implements Serializable {private Integer id;private String userName;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}
}

在IUserMapper中,我们添加下列方法:

    IdUserNameVO queryIdUserNameVOById(Integer id);

修改UserMapper.xml,加上queryIdUserNameVOById的sql语句和对应的resultMap

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.yang.mybatis.test.IUserMapper"><resultMap id="idUserName" type="com.yang.mybatis.test.IdUserNameVO"><id property="id" column="id" javaType="Integer" jdbcType="int"/><result property="userName" column="user_name" javaType="String" jdbcType="VARCHAR"/></resultMap><select id="queryUserName">select user_name from user where id = #{id}</select><select id="queryUserAge">select age from user where id = #{id}</select><select id="queryUserById" resultType="com.yang.mybatis.test.User">select * from userwhere id = #{id}</select><select id="queryIdUserNameVOById" resultMap="idUserName">select id, user_namefrom userwhere id = #{id}</select>
</mapper>

之前,我们在解析每一个mapper.xml文件时,解析出来的结果,是一个MybatisSqlStatement列表,但是这种方式还不能更好的表达一个mapper.xml中包含的信息,因此,我们修改代码,现在对于每一个mapper.xml文件,解析出来的结果位MybatisMapperXmlConfiguration类,该类定义如下:

package com.yang.mybatis.config;import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class MybatisMapperXmlConfiguration implements Serializable {private String mapperName;private List<MybatisSqlStatement> mybatisSqlStatements = new ArrayList<>();private Map<String, MybatisResultMap> mybatisResultMaps = new HashMap<>();public List<MybatisSqlStatement> getMybatisSqlStatements() {return mybatisSqlStatements;}public void setMybatisSqlStatements(List<MybatisSqlStatement> mybatisSqlStatements) {this.mybatisSqlStatements = mybatisSqlStatements;}public List<MybatisResultMap> getMybatisResultMaps() {return new ArrayList<>(mybatisResultMaps.values());}public void addMybatisSqlStatement(MybatisSqlStatement mybatisSqlStatement) {this.mybatisSqlStatements.add(mybatisSqlStatement);}public void addMybatisResultMap(MybatisResultMap mybatisResultMap) {this.mybatisResultMaps.put(mybatisResultMap.getId(), mybatisResultMap);}public MybatisResultMap getMybatisResultMap(String id) {return this.mybatisResultMaps.get(id);}public String getMapperName() {return mapperName;}public void setMapperName(String mapperName) {this.mapperName = mapperName;}
}

MybatisResultMap的定义如下:

package com.yang.mybatis.config;import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;public class MybatisResultMap implements Serializable {private String id;private String type;private MybatisResultMapProperty idProperty;private List<MybatisResultMapProperty> properties = new ArrayList<>();public void addMybatisResultMapProperty(MybatisResultMapProperty mybatisResultMapProperty) {this.properties.add(mybatisResultMapProperty);}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getType() {return type;}public void setType(String type) {this.type = type;}public MybatisResultMapProperty getIdProperty() {return idProperty;}public void setIdProperty(MybatisResultMapProperty idProperty) {this.idProperty = idProperty;}public List<MybatisResultMapProperty> getProperties() {return this.properties;}
}

MybatisResultMapProperty类定义如下:

package com.yang.mybatis.config;import java.io.Serializable;public class MybatisResultMapProperty implements Serializable {private String property;private String column;private String javaType;private String jdbcType;... 省略getter和setter
}

我们修改IMybatisMapperParser接口:

package com.yang.mybatis.config.parser;import com.yang.mybatis.config.MybatisMapperXmlConfiguration;public interface IMybatisMapperParser {MybatisMapperXmlConfiguration parseMapper(String path);
}

修改MybatisStatement,加上resultMap属性:

package com.yang.mybatis.config;import java.io.Serializable;public class MybatisSqlStatement implements Serializable {private String namespace;private String id;private String sql;private String resultType;private String resultMap;public String getNamespace() {return namespace;}public void setNamespace(String namespace) {this.namespace = namespace;}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getSql() {return sql;}public void setSql(String sql) {this.sql = sql;}public String getResultType() {return resultType;}public void setResultType(String resultType) {this.resultType = resultType;}public String getResultMap() {return resultMap;}public void setResultMap(String resultMap) {this.resultMap = resultMap;}
}

修改其IMybatisMapperParser具体实现:

package com.yang.mybatis.config.parser;import com.yang.mybatis.config.MybatisMapperXmlConfiguration;
import com.yang.mybatis.config.MybatisResultMap;
import com.yang.mybatis.config.MybatisResultMapProperty;
import com.yang.mybatis.config.MybatisSqlStatement;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;public class XmlMybatisMapperParser implements IMybatisMapperParser {private final static Set<String> tagSet = new HashSet<>();private final static Set<String> resultMapTagSet = new HashSet<>();static {tagSet.add("select");tagSet.add("insert");tagSet.add("update");tagSet.add("delete");tagSet.add("SELECT");tagSet.add("INSERT");tagSet.add("UPDATE");tagSet.add("DELETE");resultMapTagSet.add("resultMap");resultMapTagSet.add("ResultMap");}@Overridepublic MybatisMapperXmlConfiguration parseMapper(String path) {MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration = new MybatisMapperXmlConfiguration();try {InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(path);SAXReader saxReader = new SAXReader();Document document = saxReader.read(inputStream);Element root = document.getRootElement();parseMapperName(mybatisMapperXmlConfiguration, root);for (String tag : tagSet) {List<Element> elements = root.elements(tag);parseStatement(mybatisMapperXmlConfiguration, elements);}for (String tag: resultMapTagSet) {List<Element> elements = root.elements(tag);parseResultMap(mybatisMapperXmlConfiguration, elements);}} catch (DocumentException e) {e.printStackTrace();}return mybatisMapperXmlConfiguration;}private void parseMapperName(MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration, Element root) {String mapperName = root.attributeValue("namespace");mybatisMapperXmlConfiguration.setMapperName(mapperName);}private void parseResultMap(MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration, List<Element> elements) {if (elements == null || elements.isEmpty()) {return;}for (Element element : elements) {String id = element.attributeValue("id");String type = element.attributeValue("type");MybatisResultMap mybatisResultMap = new MybatisResultMap();mybatisResultMap.setId(id);mybatisResultMap.setType(type);Element idElement = element.element("id");if (idElement != null) {MybatisResultMapProperty mybatisResultMapProperty = buildMybatisResultMapProperty(idElement);mybatisResultMap.addMybatisResultMapProperty(mybatisResultMapProperty);mybatisResultMap.setIdProperty(mybatisResultMapProperty);}List<Element> resultList = element.elements("result");for (Element resultElement : resultList) {MybatisResultMapProperty mybatisResultMapProperty = buildMybatisResultMapProperty(resultElement);mybatisResultMap.addMybatisResultMapProperty(mybatisResultMapProperty);}mybatisMapperXmlConfiguration.addMybatisResultMap(mybatisResultMap);}}private MybatisResultMapProperty buildMybatisResultMapProperty(Element element) {MybatisResultMapProperty mybatisResultMapProperty = new MybatisResultMapProperty();String property = element.attributeValue("property");String column = element.attributeValue("column");String javaType = element.attributeValue("javaType");String jdbcType = element.attributeValue("jdbcType");mybatisResultMapProperty.setProperty(property);mybatisResultMapProperty.setColumn(column);mybatisResultMapProperty.setJavaType(javaType);mybatisResultMapProperty.setJdbcType(jdbcType);return mybatisResultMapProperty;}private void parseStatement(MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration, List<Element> elements) {if (elements == null || elements.isEmpty()) {return;}String namespace = mybatisMapperXmlConfiguration.getMapperName();for (Element element : elements) {String id = element.attributeValue("id");String resultType = element.attributeValue("resultType");String resultMap = element.attributeValue("resultMap");String sql = element.getText().trim();MybatisSqlStatement mybatisSqlStatement = new MybatisSqlStatement();mybatisSqlStatement.setNamespace(namespace);mybatisSqlStatement.setId(id);mybatisSqlStatement.setSql(sql);mybatisSqlStatement.setResultType(resultType);mybatisSqlStatement.setResultMap(resultMap);mybatisMapperXmlConfiguration.addMybatisSqlStatement(mybatisSqlStatement);}}
}

修改MybatisSqlSessionFactoryBuilder类:

package com.yang.mybatis.session;import com.yang.mybatis.config.MybatisConfiguration;
import com.yang.mybatis.config.MybatisMapperXmlConfiguration;
import com.yang.mybatis.config.MybatisSqlStatement;
import com.yang.mybatis.config.parser.IMybatisConfigurationParser;
import com.yang.mybatis.config.parser.IMybatisMapperParser;
import com.yang.mybatis.mapper.MapperProxyFactory;import java.util.List;public class MybatisSqlSessionFactoryBuilder {private IMybatisConfigurationParser mybatisConfigurationParser;private IMybatisMapperParser mybatisMapperParser;private String configPath;public MybatisSqlSessionFactory buildSqlSessionFactory() {if (configPath == null || configPath.isEmpty()) {throw new RuntimeException("配置文件路径不合法==========");}if (this.mybatisMapperParser == null || this.mybatisConfigurationParser == null) {throw new RuntimeException("缺少解析器=======");}MybatisConfiguration mybatisConfiguration = mybatisConfigurationParser.parser(configPath);List<String> mapperPaths = mybatisConfiguration.getMapperPaths();for (String mapperPath : mapperPaths) {MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration = this.mybatisMapperParser.parseMapper(mapperPath);List<MybatisSqlStatement> mybatisSqlStatements = mybatisMapperXmlConfiguration.getMybatisSqlStatements();for (MybatisSqlStatement mybatisSqlStatement : mybatisSqlStatements) {String mapperMethod = mybatisSqlStatement.getNamespace() + "." + mybatisSqlStatement.getId();mybatisConfiguration.putMapperMethod2MybatisSqlStatement(mapperMethod, mybatisSqlStatement);}mybatisConfiguration.putMapperXmlConfiguration(mybatisMapperXmlConfiguration.getMapperName(), mybatisMapperXmlConfiguration);}MapperProxyFactory mapperProxyFactory = new MapperProxyFactory(mybatisConfiguration);return new DefaultMybatisSqlSessionFactory(mapperProxyFactory);}public MybatisSqlSessionFactoryBuilder setConfigPath(String configPath) {this.configPath = configPath;return this;}public MybatisSqlSessionFactoryBuilder setMybatisConfigurationParser(IMybatisConfigurationParser iMybatisConfigurationParser) {this.mybatisConfigurationParser = iMybatisConfigurationParser;return this;}public MybatisSqlSessionFactoryBuilder setMybatisMapperParser(IMybatisMapperParser iMybatisMapperParser) {this.mybatisMapperParser = iMybatisMapperParser;return this;}
}

之前对于结果的解析,我们都是在DefaultMybatisSqlSession类中进行的,但是现在我们发现,结果的类型逐渐变得多样性了,如果都放在DefaultMybatisSqlSession类中,会使这个类十分庞大,因此,我们将解析结果的职责, 提取到IMybatisResultParser类中,首先定义该接口:

package com.yang.mybatis.execute;import com.yang.mybatis.execute.request.MybatisResultParserRequest;import java.sql.SQLException;public interface IMybatisResultParser {final static int ONE_COLUMNE = 0;final static int RESULT_TYPE = 1;final static int RESULT_MAP = 2;<T> T parseResult(MybatisResultParserRequest mybatisResultParserRequest) throws SQLException;
}

MybatisResultParserRequest:

package com.yang.mybatis.execute.request;import com.yang.mybatis.config.MybatisMapperXmlConfiguration;
import com.yang.mybatis.config.MybatisSqlStatement;import java.io.Serializable;
import java.sql.ResultSet;public class MybatisResultParserRequest implements Serializable {private ResultSet resultSet;private MybatisSqlStatement mybatisSqlStatement;private MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration;public MybatisResultParserRequest() {}public ResultSet getResultSet() {return resultSet;}public void setResultSet(ResultSet resultSet) {this.resultSet = resultSet;}public MybatisSqlStatement getMybatisSqlStatement() {return mybatisSqlStatement;}public void setMybatisSqlStatement(MybatisSqlStatement mybatisSqlStatement) {this.mybatisSqlStatement = mybatisSqlStatement;}public MybatisMapperXmlConfiguration getMybatisMapperXmlConfiguration() {return mybatisMapperXmlConfiguration;}public void setMybatisMapperXmlConfiguration(MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration) {this.mybatisMapperXmlConfiguration = mybatisMapperXmlConfiguration;}
}

然后定义其具体实现:

package com.yang.mybatis.execute;import com.google.common.base.CaseFormat;
import com.yang.mybatis.config.MybatisMapperXmlConfiguration;
import com.yang.mybatis.config.MybatisResultMap;
import com.yang.mybatis.config.MybatisResultMapProperty;
import com.yang.mybatis.config.MybatisSqlStatement;
import com.yang.mybatis.execute.request.MybatisResultParserRequest;
import org.apache.commons.lang3.StringUtils;import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;public class DefaultMybatisResultParser implements IMybatisResultParser {@Overridepublic <T> T parseResult(MybatisResultParserRequest mybatisResultParserRequest) throws SQLException {ResultSet resultSet = mybatisResultParserRequest.getResultSet();if (resultSet == null) {return null;}MybatisSqlStatement mybatisSqlStatement = mybatisResultParserRequest.getMybatisSqlStatement();MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration = mybatisResultParserRequest.getMybatisMapperXmlConfiguration();int resultTypeCode = parseResultTypeCode(mybatisSqlStatement);switch (resultTypeCode) {case ONE_COLUMNE:return parseResultOfOneColumn(resultSet, mybatisSqlStatement);case RESULT_TYPE:return parseResultOfResultType(resultSet, mybatisSqlStatement);}return parseResultOfResultMap(resultSet, mybatisSqlStatement, mybatisMapperXmlConfiguration);}private <T> T parseResultOfResultMap(ResultSet resultSet, MybatisSqlStatement mybatisSqlStatement,MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration) throws SQLException {String resultMap = mybatisSqlStatement.getResultMap();MybatisResultMap mybatisResultMap = mybatisMapperXmlConfiguration.getMybatisResultMap(resultMap);String classType = mybatisResultMap.getType();Map<String, String> fieldName2ColumnNameMap = mybatisResultMap.getProperties().stream().collect(Collectors.toMap(MybatisResultMapProperty::getProperty, MybatisResultMapProperty::getColumn));return parseResultOfClassAndFields(resultSet, classType, fieldName2ColumnNameMap);}private <T> T parseResultOfResultType(ResultSet resultSet, MybatisSqlStatement mybatisSqlStatement) throws SQLException {String resultType = mybatisSqlStatement.getResultType();try {Class<?> aClass = Class.forName(resultType);Field[] fields = aClass.getDeclaredFields();Map<String, String> fieldName2ColumnNameMap = new HashMap<>();for (Field field : fields) {// 驼峰命名转下划线String columnName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, field.getName());fieldName2ColumnNameMap.put(field.getName(), columnName);}return parseResultOfClassAndFields(resultSet, resultType, fieldName2ColumnNameMap);} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}private <T> T parseResultOfClassAndFields(ResultSet resultSet, String classType, Map<String, String> fieldName2ColumnNameMap ) throws SQLException {try {Class<?> aClass = Class.forName(classType);Field[] fields = aClass.getDeclaredFields();Map<String, Field> fieldName2FieldMap = new HashMap<>();for (Field field : fields) {fieldName2FieldMap.put(field.getName(), field);}Object result = aClass.newInstance();while (resultSet.next()) {for (Map.Entry<String, String> entry : fieldName2ColumnNameMap.entrySet()) {String fieldName = entry.getKey();String columnName = entry.getValue();Object columnValue = resultSet.getObject(columnName);Field field = fieldName2FieldMap.get(fieldName);field.setAccessible(true);field.set(result, columnValue);}}return (T)result;} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {throw new RuntimeException(e);}}private <T> T parseResultOfOneColumn(ResultSet resultSet, MybatisSqlStatement mybatisSqlStatement) throws SQLException {return (T) resultSet.getObject(1);}private int parseResultTypeCode(MybatisSqlStatement mybatisSqlStatement) {String resultType = mybatisSqlStatement.getResultType();String resultMap = mybatisSqlStatement.getResultMap();if (StringUtils.isEmpty(resultType) && StringUtils.isEmpty(resultMap)) {return ONE_COLUMNE;}if (StringUtils.isNotEmpty(resultType) && StringUtils.isNotEmpty(resultMap)) {throw new RuntimeException("resultType和resultMap不能同时存在");}if (StringUtils.isNotEmpty(resultType)) {return RESULT_TYPE;}return RESULT_MAP;}
}

最后,我们修改DefaultMybatisSqlSession类:

package com.yang.mybatis.session;import com.yang.mybatis.config.MybatisConfiguration;
import com.yang.mybatis.config.MybatisEnvironment;
import com.yang.mybatis.config.MybatisMapperXmlConfiguration;
import com.yang.mybatis.config.MybatisSqlStatement;
import com.yang.mybatis.execute.DefaultMybatisResultParser;
import com.yang.mybatis.execute.IMybatisResultParser;
import com.yang.mybatis.execute.request.MybatisResultParserRequest;
import com.yang.mybatis.proxy.MapperProxyFactory;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;public class DefaultMybatisSqlSession implements IMybatisSqlSession {private MapperProxyFactory mapperProxyFactory;private MybatisConfiguration mybatisConfiguration;private IMybatisResultParser iMybatisResultParser = new DefaultMybatisResultParser();public DefaultMybatisSqlSession(MapperProxyFactory mapperProxyFactory) {this.mapperProxyFactory = mapperProxyFactory;this.mybatisConfiguration = mapperProxyFactory.getMybatisConfiguration();}@Overridepublic <T> T execute(String method, Object parameter) {Map<String, MybatisSqlStatement> mapperMethod2SqlStatementsMap = mapperProxyFactory.getMybatisConfiguration().getMapperMethod2SqlStatementsMap();MybatisSqlStatement mybatisSqlStatement = mapperMethod2SqlStatementsMap.get(method);MybatisEnvironment defaultMybatisEnvironment = this.mybatisConfiguration.getDefaultMybatisEnvironment();return new TransactionInvoke<T>() {@Overridepublic T execute(Connection connection) throws SQLException {String rawSql = mybatisSqlStatement.getSql();List<String> parameterNameList = new ArrayList<>();String sql = extractRawSql(rawSql, parameterNameList);Object[] parameters = (Object[]) parameter;PreparedStatement preparedStatement = connection.prepareStatement(sql);if (parameterNameList.size() != parameters.length) {throw new RuntimeException("SQL语句参数个数不匹配====");}int index = 1;for (Object o : parameters) {preparedStatement.setObject(index ++, o);}ResultSet resultSet = preparedStatement.executeQuery();String mapperName = mybatisSqlStatement.getNamespace();MybatisMapperXmlConfiguration mybatisMapperXmlConfiguration = mybatisConfiguration.getMybatisMapperXmlConfiguration(mapperName);MybatisResultParserRequest mybatisResultParserRequest = new MybatisResultParserRequest();mybatisResultParserRequest.setResultSet(resultSet);mybatisResultParserRequest.setMybatisSqlStatement(mybatisSqlStatement);mybatisResultParserRequest.setMybatisMapperXmlConfiguration(mybatisMapperXmlConfiguration);T result = iMybatisResultParser.parseResult(mybatisResultParserRequest);resultSet.close();return result;}}.invoke(defaultMybatisEnvironment.getMybatisDataSource());}private String extractRawSql(String rawSql, List<String> parameterNameList) {StringBuilder sqlBuilder = new StringBuilder();int start = 0;int end = -1;while ((end = rawSql.indexOf("#", start)) != -1) {sqlBuilder.append(rawSql.substring(start, end - 1)).append(" ? ");int parameterStart = end + 2;int parameterEnd = rawSql.indexOf("}", parameterStart);parameterNameList.add(rawSql.substring(parameterStart, parameterEnd));start = parameterEnd + 1;}sqlBuilder.append(rawSql.substring(start));return sqlBuilder.toString();}@Overridepublic <T> T getMapper(Class<T> type) {return (T) mapperProxyFactory.newInstance(type, this);}
}

添加测试代码,进行测试:

public static void main(String[] args) {String configPath = "mybatis-config.xml";IMybatisSqlSessionFactory mybatisSqlSessionFactory = new MybatisSqlSessionFactoryBuilder().setMybatisConfigurationParser(new XmlMybatisConfigurationParser()).setMybatisMapperParser(new XmlMybatisMapperParser()).setConfigPath(configPath).buildSqlSessionFactory();IMybatisSqlSession mybatisSqlSession = mybatisSqlSessionFactory.openSession();IUserMapper userMapper = mybatisSqlSession.getMapper(IUserMapper.class);IdUserNameVO idUserNameVO = userMapper.queryIdUserNameVOById(1);System.out.println(idUserNameVO.getId());System.out.println(idUserNameVO.getUserName());}

测试结果如下:
image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1411630.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【Python基础】进程

文章目录 [toc]程序与进程的区别与联系同步任务示例 并行任务示例进程调度的“随机性” 进程属性与方法process_object.start()方法process_object.join()方法process_object.daemon属性没有设置守护进程的情况设置守护进程的情况 process_object.current_process()方法 进程通…

(三)Appdesigner-界面转换及数据导入和保存

提示&#xff1a;文章为系列文章&#xff0c;可以在对应学习专栏里面进行学习。对应资源已上传 目录 前言 一、Appdesigner是什么&#xff1f; 二、界面切换 三、数据导入及保存 &#xff08;一&#xff09;数据导入 &#xff08;二&#xff09;数据保存 总结 前言 Appd…

ZooKeeper数据模型你懂吗?

ZooKeeper数据节点你知道吗&#xff1f;那数据节点有什么类型&#xff1f;数据节点的版本呢&#xff1f;听说ZooKeeper还有事务ID&#xff0c;你知不知道啊&#xff1f;还有Watcher机制呢&#xff1f;ZooKeeper作为一个典型的分布式数据一致性的解决方案&#xff0c;ZooKeeper的…

【Python项目】基于DJANGO的【医院体检预约系统】

技术简介&#xff1a;使用Python技术、DJANGO框架、MYSQL数据库等实现。 系统简介&#xff1a;系统采用了在线预约和挂号的方式&#xff0c;用户可以通过网站进行预约和挂号操作。同时&#xff0c;系统还提供了医生的详细介绍和评价&#xff0c;方便用户选择医生。 研究背景&a…

Django之单文件上传(以图片为例)

一&#xff0c;创建项目 初始化&#xff0c;数据迁移&#xff0c;创建superuser&#xff0c;创建app等 二&#xff0c;配置settings.py 1&#xff0c;配置数据库&#xff08;本作者使用的mysql&#xff09;&#xff0c;以前文章有提到 2&#xff0c;配置静态文件存放路径 STAT…

关于YOLO8学习(五)安卓部署ncnn模型--视频检测

前文 关于YOLO8学习(一)环境搭建,官方检测模型部署到手机 关于YOLO8学习(二)数据集收集,处理 关于YOLO8学习(三)训练自定义的数据集 关于YOLO8学习(四)模型转换为ncnn 简介 本文将会讲解: (1)使用前文生成的ncnn模型,部署到安卓端,并且实现视频中,人脸的检测…

CSS精灵图、字体图标、HTML5新增属性、界面样式和网站 favicon 图标

精灵图 为什么要使用精灵图 一个网页中往往会应用很多小的背景图像作为修饰&#xff0c;当网页中的图像过多时&#xff0c;服务器就会频繁地接收和发送请求图片&#xff0c;造成服务器请求压力过大&#xff0c;这将大大降低页面的加载速度,因此&#xff0c;为了有效地减少服务…

农作物害虫检测数据集VOC+YOLO格式18975张97类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;18975 标注数量(xml文件个数)&#xff1a;18975 标注数量(txt文件个数)&#xff1a;18975 标…

ARL资产侦察灯塔系统安装和使用(含实用配置说明)

★★免责声明★★ 文章中涉及的程序(方法)可能带有攻击性&#xff0c;仅供安全研究与学习之用&#xff0c;读者将信息做其他用途&#xff0c;由Ta承担全部法律及连带责任&#xff0c;文章作者不承担任何法律及连带责任。 1、官方介绍 ARL全称&#xff1a;Asset Reconnaissance…

C#图像:1.图像区域分割与提取

&#xff08;1&#xff09;创建一个名为SplitImage的窗体的应用程序&#xff0c;将窗体改名为FormSplitImage。 &#xff08;2&#xff09;创建一个名为ImageProcessingLibrary的类库程序&#xff0c;为该工程添加名为ImageProcessing的静态类 &#xff08;3&#xff09;为Imag…

智慧校园云平台源码,SaaS运营云平台(支持多学校、多校园使用)

智慧班牌系统&#xff0c;又称电子班牌系统&#xff0c;是一种基于互联网技术的综合管理工具。通过在教室内安装显示屏&#xff0c;并连接到学校管理系统&#xff0c;实现教学资源展示、信息发布、学生管理等多种功能的集成。该系统旨在加强学校班级文化建设和班级风采展示&…

【自留】运行一个开源项目

运行一个开源项目 首先是运行起来 1. 拿到地址 拿到你想要的项目的地址 2. 克隆 打开编辑器 VSCode &#xff0c;创建一个放项目的文件夹&#xff0c;控制台输入以下代码克隆项目 git clone 克隆地址gitee克隆地址在这看&#xff1a; github上项目的话&#xff0c;在这…

python数据分析中数据可视化简单入门

1.折线图表 首先引入相关包pyecharts&#xff0c;如果没下载可以先下载 pip install pyecharts from pyecharts.charts import Lineline Line() # 添加x轴 line.add_xaxis([呱了个呱,羊村,牟多,蜂地,喵帕斯]) # 添加y轴 line.add_yaxis("GDP",[50,30,40,34,63,22])…

第Y9周:重要模块解读

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制&#x1f680; 文章来源&#xff1a;K同学的学习圈子 目录 以con.py为例&#xff1a; 一、autopad 二、Conv 三、Focus 四、C2f 文件…

记某APP登录逆向解密过程

最近在学习APP逆向相关的知识&#xff0c;刚好拿到了一个APP目标&#xff0c;该APP登录过程存在加密&#xff0c;所以记录下逆向破解的过程。流程 先介绍下拿到该APP后续所做的一些工作流程 选择相应版本安装到测试机当中进行抓包&#xff0c;查看数据包分析登录请求包&#x…

分布式与一致性协议之一致哈希算法(二)

一致哈希算法 使用哈希算法有什么问题 通过哈希算法&#xff0c;每个key都可以寻址到对应的服务器&#xff0c;比如&#xff0c;查询key是key-01,计算公式为hash(key-01)%3,警告过计算寻址到了编号为1的服务器节点A&#xff0c;如图所示。 但如果服务器数量发生变化&#x…

二叉树的实现(详解,数据结构)

目录 一&#xff0c;二叉树需要实现的功能 二&#xff0c;下面是各功能详解 0.思想&#xff1a; 1.创建二叉树结点&#xff1a; 2.通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树 3.二叉树销毁&#xff1a; 4.前序遍历&#xff1a; 5.中序遍历&#xff1a;…

基于OpenCv的图像Harris角点检测

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

【游戏行业】2024年电子游戏分类,国内游戏产业报告,发展趋势

文章目录 一、电子游戏分类1、传统游戏分类2、混合手游分类3、二次元、开放设计、调查问卷 二、游戏产业报告1、游戏产业数据2、游戏公司名单&#xff08;独角兽&#xff09;3、营收与利润&#xff08;对比互联网、国企&#xff09; 三、发展趋势1、游戏行业上下游2、游戏行业趋…

Docker镜像仓库-在私有镜像仓库推送或拉取镜像

推送镜像到私有仓库&#xff0c;要先让镜像打包 前缀为私有仓库地址的名字&#xff1a; 这里也是打包成功了:docker images 可以查看到 push推送镜像到镜像仓库: docker push 192.168.221.129:8080/nginx:1.0推送成功后在主机访问镜像仓库可以看到 这里已经有个镜像了。而且可…