手写Mybatis:第7章-SQL执行器的定义和实现
文章目录
- 一、目标:SQL执行的定义和实现
- 二、设计:SQL执行的定义和实现
- 三、实现:SQL执行的定义和实现
- 3.1 工程结构
- 3.2 SQL执行实现的关系图
- 3.3 执行器的定义和实现
- 3.3.1 Executor 接口
- 3.3.2 BaseExecutor 抽象基类
- 3.3.3 SimpleExecutor 简单执行器实现
- 3.4 语句处理器
- 3.4.1 StatementHandler 语句处理器接口
- 3.4.2 BaseStatementHandler 抽象基类
- 3.4.3 SimpleStatementHandler 简单语句处理器
- 3.4.4 PreparedStatementHandler 预处理语句处理器
- 3.5 结果处理器
- 3.5.1 结果处理器接口
- 3.5.2 结果集处理器接口
- 3.5.3 默认Map结果集处理器实现类
- 3.6 在配置项中添加执行器、语句处理器、结果处理器
- 3.7 执行器创建和使用
- 3.7.1 在 DefaultSqlSession 中开启执行器
- 3.7.2 在 DefaultSqlSession 使用执行器
- 四、测试:SQL执行的定义和实现
- 五、总结:SQL执行的定义和实现
一、目标:SQL执行的定义和实现
💡 上一章节中关于池化数据源的调用、执行和结果封装,都是在 DefaultSqlSession 中进行发起。这样写死不利于扩展,也不利于 SqlSession 中每一个新增定义的方法对池化数据源的调用。怎么利于扩展呢?
- 解耦
DefaultSqlSession#selectOne
方法中关于对数据源的调用、执行和结果封装,提供新的功能模块替代这部分编码的逻辑处理。 - 只有提供单独的执行方法入口,才能更好的扩展和应对这部分内容的需求变化,包括:各类入参、结果封装、执行器类型、批处理等。
二、设计:SQL执行的定义和实现
💡 解耦:数据源的操作硬捆绑到 DefaultSqlSession 的执行方法上。
- 单独提出一块执行器的服务功能,之后将执行器的功能随着 DefaultSqlSession 创建时传入执行器功能。
- 之后具体的方法调用就可以调用执行器来处理,从而解耦这部分功能模块。
- 首先提取出执行器的接口 Executor,定义出执行方法、事务获取和相应提交、回滚、关闭的定义。
- 同时由于执行器是一种标准的执行过程,所以可以由抽象类进行实现,对过程内容进行 模板模式 的过程包装。
- 在包装过程中定义抽象类 BaseExecutor,由具体的子类 SimpleExecutor 来实现。
- 之后就是对 SQL 的处理,JDBC 在处理 SQL 时,分为简单处理和预处理。
- 预处理:包括准备语句、参数化传递、执行查询,以及最后的结果封装和返回。
- 所以这里把 JDBC 的这部分步骤,分为结构化的类过程实现,便于功能的扩展。
- 结构化类:语句处理器 StatementHandler。
三、实现:SQL执行的定义和实现
3.1 工程结构
mybatis-step-06
|-src|-main| |-java| |-com.lino.mybatis| |-binding| | |-MapperMethod.java| | |-MapperProxy.java| | |-MapperProxyFactory.java| | |-MapperRegistry.java| |-builder| | |-xml| | | |-XMLConfigBuilder.java| | |-BaseBuilder.java| |-datasource| | |-druid| | | |-DruidDataSourceFacroty.java| | |-pooled| | | |-PooledConnection.java| | | |-PooledDataSource.java| | | |-PooledDataSourceFacroty.java| | | |-PoolState.java| | |-unpooled| | | |-UnpooledDataSource.java| | | |-UnpooledDataSourceFacroty.java| | |-DataSourceFactory.java| |-executor| | |-resultset| | | |-DefaultResultSetHandler.java| | | |-ResultSetHandler.java| | |-statement| | | |-BaseStatementHandler.java| | | |-PreparedStatementHandler.java| | | |-SimpleStatementHandler.java| | | |-StatementHandler.java| | |-BaseExecutor.java| | |-Executor.java| | |-SimpleExecutor.java| |-io| | |-Resources.java| |-mapping| | |-BoundSql.java| | |-Environment.java| | |-MappedStatement.java| | |-ParameterMapping.java| | |-SqlCommandType.java| |-session| | |-defaults| | | |-DefaultSqlSession.java| | | |-DefaultSqlSessionFactory.java| | |-Configuration.java| | |-ResultHandler.java| | |-SqlSession.java| | |-SqlSessionFactory.java| | |-SqlSessionFactoryBuilder.java| | |-TransactionIsolationLevel.java| |-transaction| | |-jdbc| | | |-JdbcTransaction.java| | | |-JdbcTransactionFactory.java| | |-Transaction.java| | |-TransactionFactory.java| |-type| | |-JdbcType.java| | |-TypeAliasRegistry.java|-test|-java| |-com.lino.mybatis.test| |-dao| | |-IUserDao.java| |-po| | |-User.java| |-ApiTest.java|-resources|-mapper| |-User_Mapper.xml|-mybatis-config-datasource.xml
3.2 SQL执行实现的关系图
- 以
Executor
接口定义为执行器入口,确定出事务和操作和 SQL 执行的统一标准接口。- 并以执行器接口定义实现抽象类
BaseExecutor
,也就是用抽象类处理统一共用的事务和执行 SQL 的标准流程。 - 也就是这里定义的执行 SQL 的抽象接口由子类
SimpleExecutor
实现。
- 并以执行器接口定义实现抽象类
- 在具体的简单 SQL 执行器实现类中,处理
doQuery
方法的具体操作过程。- 这个过程中则会引入进来 SQL 语句处理器的创建,创建过程仍由 Configuration 配置项提供。
- 当执行器开发完成之后,接下来就交给
DefaultSqlSessionFactory
开启openSession
的时候随着构造函数参数传递给DefaultSqlSession
中,这样在执行DefaultSqlSession#selectOne
的时候就可以调用执行器进行处理。这也就完成 解耦 操作。
3.3 执行器的定义和实现
- 执行器分为接口、抽象类、简单执行器实现类三部分。这就是 模板模式。
- 模板模式:通常在框架的源码中对于一些标准流程的处理,都会有抽象类的存在。它负责提供共性功能逻辑,以及对接口方法的执行过程进行定义和处理,并提取抽象接口交由子类实现。
3.3.1 Executor 接口
Executor.java
package com.lino.mybatis.executor;import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.transaction.Transaction;
import java.sql.SQLException;
import java.util.List;/*** @description: 执行器*/
public interface Executor {/*** 结果处理器*/ResultHandler NO_RESULT_HANDLER = null;/*** 查询** @param ms 映射器语句* @param parameter 参数* @param resultHandler 结果处理器* @param boundSql SQL对象* @param <E> 返回的类型* @return List<E>*/<E> List<E> query(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql);/*** 获取事务** @return 事务对象*/Transaction getTransaction();/*** 提交** @param required 是否请求执行* @throws SQLException SQL异常*/void commit(boolean required) throws SQLException;/*** 回滚** @param required 是否请求执行* @throws SQLException SQL异常*/void rollback(boolean required) throws SQLException;/*** 关闭** @param forceRollback 是否强制回滚*/void close(boolean forceRollback);
}
- 在执行器中的定义的接口包括:事务相关的处理方法、执行 SQL 查询的操作。
3.3.2 BaseExecutor 抽象基类
BaseExecutor.java
package com.lino.mybatis.executor;import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.transaction.Transaction;
import org.slf4j.LoggerFactory;
import java.sql.SQLException;
import java.util.List;/*** @description: 执行器抽象基类*/
public abstract class BaseExecutor implements Executor {private org.slf4j.Logger logger = LoggerFactory.getLogger(BaseExecutor.class);protected Configuration configuration;protected Transaction transaction;protected Executor wrapper;private boolean closed;public BaseExecutor(Configuration configuration, Transaction transaction) {this.configuration = configuration;this.transaction = transaction;this.wrapper = this;}@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql) {if (closed) {throw new RuntimeException("Executor was closed.");}return doQuery(ms, parameter, resultHandler, boundSql);}protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql);@Overridepublic Transaction getTransaction() {if (closed) {throw new RuntimeException("Executor was closed.");}return transaction;}@Overridepublic void commit(boolean required) throws SQLException {if (closed) {throw new RuntimeException("Cannot commit, transaction is already closed.");}if (required) {transaction.commit();}}@Overridepublic void rollback(boolean required) throws SQLException {if (!closed) {if (required) {transaction.rollback();}}}@Overridepublic void close(boolean forceRollback) {try {try {rollback(forceRollback);} finally {transaction.close();}} catch (SQLException e) {logger.warn("Unexpected exception on closing transaction. Cause: " + e);} finally {transaction = null;closed = true;}}
}
- 在抽象基类中封装了执行器的全部接口,这样具体的子类继承抽象类后,就不用处理这些共性的方法。
- 与此同时在 query 查询方法中,封装了一些必要的流程处理,如检测关闭等。
3.3.3 SimpleExecutor 简单执行器实现
SimpleExecutor.java
package com.lino.mybatis.executor;import com.lino.mybatis.executor.statement.StatementHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.transaction.Transaction;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;/*** @description: 简单执行器*/
public class SimpleExecutor extends BaseExecutor {public SimpleExecutor(Configuration configuration, Transaction transaction) {super(configuration, transaction);}@Overrideprotected <E> List<E> doQuery(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql) {try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, resultHandler, boundSql);Connection connection = transaction.getConnection();Statement stmt = handler.prepare(connection);handler.parameterize(stmt);return handler.query(stmt, resultHandler);} catch (SQLException e) {e.printStackTrace();return null;}}
}
- 简单执行器 SimpleExecutor 继承抽象基类,实现抽象方法
doQuery
。- 这个方法包装:数据源的获取、语句处理器的创建,以及对 Statement 的实例化和相关参数设置。最后执行 SQL 的处理和结果的返回操作。
3.4 语句处理器
- 语句处理器是 SQL 执行器中依赖的部分,SQL 执行器封装事务、连接和检测环境等。
- 而语句处理器则是准备语句、参数化传递、执行 SQL、封装结果的处理。
3.4.1 StatementHandler 语句处理器接口
StatementHandler.java
package com.lino.mybatis.executor.statement;import com.lino.mybatis.session.ResultHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;/*** @description: 语句处理器*/
public interface StatementHandler {/*** 准备语句** @param connection 链接* @return Statement语句*/Statement prepare(Connection connection);/*** 参数化** @param statement 语句*/void parameterize(Statement statement) throws SQLException;/*** 执行查询** @param statement 语句* @param resultHandler 结果处理器* @param <E> 泛型类型* @return 泛型集合*/<E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
}
- 语句处理器的核心包括:准备语句、参数化传递参数、执行查询的操作。
- Mybatis 源码中还包括 update、批处理、获取参数处理器等。
3.4.2 BaseStatementHandler 抽象基类
BaseStatementHandler.java
package com.lino.mybatis.executor.statement;import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;/*** @description: 语句处理器抽象基类*/
public abstract class BaseStatementHandler implements StatementHandler {protected final Configuration configuration;protected final Executor executor;protected final MappedStatement mappedStatement;protected final Object parameterObject;protected final ResultSetHandler resultSetHandler;protected BoundSql boundSql;public BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, ResultHandler resultHandler, BoundSql boundSql) {this.configuration = mappedStatement.getConfiguration();this.executor = executor;this.mappedStatement = mappedStatement;this.parameterObject = parameterObject;this.boundSql = boundSql;this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, boundSql);}@Overridepublic Statement prepare(Connection connection) {Statement statement = null;try {// 实例化 Statementstatement = instantiateStatement(connection);// 参数设置,可以被抽取,提供配置statement.setQueryTimeout(350);statement.setFetchSize(10000);return statement;} catch (Exception e) {throw new RuntimeException("Error prepare statement. Cause: " + e, e);}}protected abstract Statement instantiateStatement(Connection connection) throws SQLException;}
- 在语句处理器基类中,将参数信息、结果信息进行封装处理。
- 之后是对
BaseStatementHandler#prepare
方法的处理,包括定义实例化抽象方法,这个方法交由各个具体的子类进行处理。- SimpleStatementHandler 简单语句处理器:只是对 SQL 的最基本执行,没有参数的设置。
- PreparedStatementHandler 预处理语句处理器:是我们在 JDBC 中使用的最多的操作方式,PreparedStatement 设置 SQL,传递参数的设置过程。
3.4.3 SimpleStatementHandler 简单语句处理器
SimpleStatementHandler.java
package com.lino.mybatis.executor.statement;import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.ResultHandler;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;/*** @description: 简单语句处理器(STATEMENT)*/
public class SimpleStatementHandler extends BaseStatementHandler {public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, ResultHandler resultHandler, BoundSql boundSql) {super(executor, mappedStatement, parameterObject, resultHandler, boundSql);}@Overrideprotected Statement instantiateStatement(Connection connection) throws SQLException {return connection.createStatement();}@Overridepublic void parameterize(Statement statement) {}@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {String sql = boundSql.getSql();statement.execute(sql);return resultSetHandler.handleResultSets(statement);}
}
- 简单语句处理器:只是对 SQL 的最基本执行,没有参数的设置。
3.4.4 PreparedStatementHandler 预处理语句处理器
PreparedStatementHandler.java
package com.lino.mybatis.executor.statement;import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.ResultHandler;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;/*** @description: 预处理语句处理器(PREPARED)* @author: lingjian* @createDate: 2022/11/8 14:13*/
public class PreparedStatementHandler extends BaseStatementHandler {public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, ResultHandler resultSetHandler, BoundSql boundSql) {super(executor, mappedStatement, parameterObject, resultSetHandler, boundSql);}@Overrideprotected Statement instantiateStatement(Connection connection) throws SQLException {String sql = boundSql.getSql();return connection.prepareStatement(sql);}@Overridepublic void parameterize(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.setLong(1, Long.parseLong(((Object[]) parameterObject)[0].toString()));}@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();return resultSetHandler.handleResultSets(ps);}
}
- 在预处理语句处理器中包括
instantiateStatement
预处理 SQL、parameterize
设置参数,以及query
查询的执行操作。 parameterize
暂时是写死的处理,后续再完善。query
是执行查询和对结果的封装,结果的封装,后续再完善。
3.5 结果处理器
💡 定义结果处理器接口,结果集处理器接口,默认Map结果集处理器实现类
3.5.1 结果处理器接口
ResultHandler.java
package com.lino.mybatis.session;/*** @description: 结果处理器*/
public interface ResultHandler {/*** 处理结果*/void handleResult();
}
3.5.2 结果集处理器接口
ResultSetHandler.java
package com.lino.mybatis.executor.resultset;import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;/*** @description: 结果集处理器*/
public interface ResultSetHandler {/*** 处理结果集** @param stmt 语句* @param <E> 泛型* @return 泛型集合* @throws SQLException SQL异常*/<E> List<E> handleResultSets(Statement stmt) throws SQLException;
}
3.5.3 默认Map结果集处理器实现类
DefaultResultSetHandler.java
package com.lino.mybatis.executor.resultset;import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import java.lang.reflect.Method;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;/*** @description: 默认Map结果处理器*/
public class DefaultResultSetHandler implements ResultSetHandler {private final BoundSql boundSql;public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, BoundSql boundSql) {this.boundSql = boundSql;}@Overridepublic <E> List<E> handleResultSets(Statement stmt) throws SQLException {ResultSet resultSet = stmt.getResultSet();try {return resultSet2Obj(resultSet, Class.forName(boundSql.getResultType()));} catch (ClassNotFoundException e) {e.printStackTrace();return null;}}private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) {List<T> list = new ArrayList<>();try {ResultSetMetaData metaData = resultSet.getMetaData();int columnCount = metaData.getColumnCount();// 每次遍历值while (resultSet.next()) {T obj = (T) clazz.newInstance();for (int i = 1; i <= columnCount; i++) {Object value = resultSet.getObject(i);String columnName = metaData.getColumnName(i);String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);Method method;if (value instanceof Timestamp) {method = clazz.getMethod(setMethod, LocalDateTime.class);} else {method = clazz.getMethod(setMethod, value.getClass());}method.invoke(obj, value);}list.add(obj);}} catch (Exception e) {e.printStackTrace();}return list;}
}
3.6 在配置项中添加执行器、语句处理器、结果处理器
Configuration.java
package com.lino.mybatis.session;import com.lino.mybatis.binding.MapperRegistry;
import com.lino.mybatis.datasource.druid.DruidDataSourceFactory;
import com.lino.mybatis.datasource.pooled.PooledDataSourceFactory;
import com.lino.mybatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.SimpleExecutor;
import com.lino.mybatis.executor.resultset.DefaultResultSetHandler;
import com.lino.mybatis.executor.resultset.ResultSetHandler;
import com.lino.mybatis.executor.statement.PreparedStatementHandler;
import com.lino.mybatis.executor.statement.StatementHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.jdbc.JdbcTransactionFactory;
import com.lino.mybatis.type.TypeAliasRegistry;
import java.util.HashMap;
import java.util.Map;/*** @description: 配置项* @author: lingjian* @createDate: 2022/11/7 21:32*/
public class Configuration {//省略.../*** 生产执行器** @param transaction 事务* @return 执行器*/public Executor newExecutor(Transaction transaction) {return new SimpleExecutor(this, transaction);}/*** 创建语句处理器** @param executor 执行器* @param mappedStatement 映射器语句类* @param parameter 参数* @param resultHandler 结果处理器* @param boundSql SQL语句* @return StatementHandler 语句处理器*/public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, ResultHandler resultHandler, BoundSql boundSql) {return new PreparedStatementHandler(executor, mappedStatement, parameter, resultHandler, boundSql);}/*** 创建结果集处理器** @param executor 执行器* @param mappedStatement 映射器语句类* @param boundSql SQL语句* @return ResultSetHandler 结果集处理器*/public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, BoundSql boundSql) {return new DefaultResultSetHandler(executor, mappedStatement, boundSql);}
}
3.7 执行器创建和使用
- 执行器开发完成以后,则需要串联到 DefaultSqlSession 中进行使用。
- 串联过程:在创建 DefaultSqlSession 的时候,构建出执行器并作为参数传递进去。这里涉及到
DefaultSqlSessionFactory#openSession
的处理。
3.7.1 在 DefaultSqlSession 中开启执行器
DefaultSqlSession.java
package com.lino.mybatis.session.defaults;import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.SqlSession;
import com.lino.mybatis.session.SqlSessionFactory;
import com.lino.mybatis.session.TransactionIsolationLevel;
import com.lino.mybatis.transaction.Transaction;
import com.lino.mybatis.transaction.TransactionFactory;
import java.sql.SQLException;/*** @description: 默认的SqlSessionFactory实现类*/
public class DefaultSqlSessionFactory implements SqlSessionFactory {private final Configuration configuration;public DefaultSqlSessionFactory(Configuration configuration) {this.configuration = configuration;}@Overridepublic SqlSession openSession() {Transaction tx = null;try {final Environment environment = configuration.getEnvironment();TransactionFactory transactionFactory = environment.getTransactionFactory();tx = transactionFactory.newTransaction(configuration.getEnvironment().getDataSource(), TransactionIsolationLevel.READ_COMMITTED, false);// 创建执行器final Executor executor = configuration.newExecutor(tx);// 创建 DefaultSqlSessionreturn new DefaultSqlSession(configuration, executor);} catch (Exception e) {try {assert tx != null;tx.close();} catch (SQLException ignore) {}throw new RuntimeException("Error opening session. Cause: " + e);}}
}
- 在
openSession
中开启事务传递给执行器的创建。并在执行器创建完毕后,作为参数传递给 DefaultSqlSession。
3.7.2 在 DefaultSqlSession 使用执行器
DefaultSqlSession.java
package com.lino.mybatis.session.defaults;import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.Environment;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.SqlSession;
import java.lang.reflect.Method;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;/*** @description: 默认sqlSession实现类*/
public class DefaultSqlSession implements SqlSession {private Configuration configuration;private Executor executor;public DefaultSqlSession(Configuration configuration, Executor executor) {this.configuration = configuration;this.executor = executor;}@Overridepublic <T> T selectOne(String statement) {return (T) ("你被代理了!" + statement);}@Overridepublic <T> T selectOne(String statement, Object parameter) {MappedStatement ms = configuration.getMappedStatement(statement);List<T> list = executor.query(ms, parameter, Executor.NO_RESULT_HANDLER, ms.getBoundSql());return list.get(0);}@Overridepublic <T> T getMapper(Class<T> type) {return configuration.getMapper(type, this);}@Overridepublic Configuration getConfiguration() {return configuration;}
}
- 在
DefaultSqlSession#selectOne
中获取MappedStatement
映射语句后,则传递给执行器进行处理。 - 那么现在这些类经过设计思想的解耦后,就变得更加干净整洁了,也易于维护和扩展了。
四、测试:SQL执行的定义和实现
ApiTest.java
@Test
public void test_SqlSessionFactoryExecutor() throws IOException {// 1.从SqlSessionFactory中获取SqlSessionSqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));SqlSession sqlSession = sqlSessionFactory.openSession();// 2.获取映射器对象IUserDao userDao = sqlSession.getMapper(IUserDao.class);// 3.测试验证User user = userDao.queryUserInfoById(1L);logger.info("测试结果:{}", JSON.toJSONString(user));
}
测试结果
16:40:58.484 [main] INFO c.l.m.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
16:40:59.195 [main] INFO c.l.m.d.pooled.PooledDataSource - Created connention 1436664465.
16:40:59.262 [main] INFO com.lino.mybatis.test.ApiTest - 测试结果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小灵哥"}
- 从测试结果看我们已经把
DefaultSqlSession#selectOne
中的调用,换成执行器完成整个过程的处理,解耦了部分的逻辑操作,方便后续的扩展。
五、总结:SQL执行的定义和实现
- 整个实现都是处理解耦这件事情,从
DefaultSqlSession#selectOne
对数据源的处理解耦到执行器中进行操作。而执行器中又包括了对 JDBC 处理的拆解,链接、准备语句、封装参数、处理结果,所有的这些过程经过解耦后的类和方法,在后续方便进行扩展。
相关文章:

手写Mybatis:第7章-SQL执行器的定义和实现
文章目录 一、目标:SQL执行的定义和实现二、设计:SQL执行的定义和实现三、实现:SQL执行的定义和实现3.1 工程结构3.2 SQL执行实现的关系图3.3 执行器的定义和实现3.3.1 Executor 接口3.3.2 BaseExecutor 抽象基类3.3.3 SimpleExecutor 简单执…...

C语言基础知识理论版(很详细)
文章目录 前述一、数据1.1 数据类型1.2 数据第一种数据:常量第二种数据:变量第三种数据:表达式1、算术运算符及算术表达式2、赋值运算符及赋值表达式3、自增、自减运算符4、逗号运算符及其表达式(‘顺序求值’表达式)5…...

CG MAGIC分享3d Max中的Corona渲染器材质如何成转换VRay材质?
大家无论是使用Corona渲染器还是Vray渲染器时,进行材质问题时,都会遇到转化材质问题。 如何将CR转换成VR或者将VR转换CR材质呢? 对于这两者之间转换最好最好的方法只能是材质转换器。 CG MAGIC小编,梳理了两种方法,大…...
电脑入门:路由器常见问题排错步骤
HiPER系列路由器使用中Ping LAN口不通的诊断步骤 准备工作: 在可以ping通的时候记录下路由器LAN口的MAC地址: 命令hiper% show interface ethernet/1 mac Mac : 0022aa419d1e 以下步骤在ping不通路由器的时候依次操作,并记下结果: 步骤一:观察设备各端口…...
mac电脑识别不出来u盘?mac识别不了u盘怎么办
有些用户反馈说本来想要拷贝文件,但是将U盘插入mac系统后竟然不能识别,这时候我们需要用到NTFS For Mac软件。 其实mac系统只提供了它自身磁盘格式(mac os 扩展)等的读写权限,只提供了读的权限给NTFS、FAT32给硬盘和U盘,我们99%使…...

【系统编程】线程池以及API接口简介
(꒪ꇴ꒪ ),Hello我是祐言QAQ我的博客主页:C/C语言,数据结构,Linux基础,ARM开发板,网络编程等领域UP🌍快上🚘,一起学习,让我们成为一个强大的攻城狮࿰…...

Verilog零基础入门(边看边练与测试仿真)-笔记
文章目录 第一讲第二讲第三讲第四讲 第一讲 1、testbench 没有端口,所以没括号 2、testbench 输入端 之后要变动 所以定义为reg 3、#10 :过10个时间单位 ;’timescale 1ns/10ps 即 1ns 的时间单位 10ps的时间精度 4、reg 型变量赋值的时候 用…...

LLMs之Code:Code Llama的简介、安装、使用方法之详细攻略
LLMs之Code:Code Llama的简介、安装、使用方法之详细攻略 导读:2023年08月25日(北京时间),Meta发布了Code Llama,一个可以使用文本提示生成代码的大型语言模型(LLM)。Code Llama是最先进的公开可用的LLM代码任务,并有潜…...

[国产MCU]-W801开发实例-MQTT客户端通信
MQTT客户端通信 文章目录 MQTT客户端通信1、MQTT介绍2、W801的MQTT客户端相关API介绍3、代码实现本文将详细介绍如何在W801中使用MQTT协议通信。 1、MQTT介绍 MQTT 被称为消息队列遥测传输协议。它是一种轻量级消息传递协议,可通过简单的通信机制帮助资源受限的网络客户端。 …...

搭建个人hMailServer 邮件服务实现远程发送邮件
文章目录 1. 安装hMailServer2. 设置hMailServer3. 客户端安装添加账号4. 测试发送邮件5. 安装cpolar6. 创建公网地址7. 测试远程发送邮件8. 固定连接公网地址9. 测试固定远程地址发送邮件 hMailServer 是一个邮件服务器,通过它我们可以搭建自己的邮件服务,通过cpolar内网映射工…...
React的 虚拟DOM创建
React是一个流行的JavaScript库,用于构建用户界面。它通过使用虚拟DOM来提高性能和渲染速度。本文将详细介绍React的虚拟DOM的创建方式、用法和案例,以及相关代码和解释。 虚拟DOM是什么? 虚拟DOM是React的一个重要概念,它是一个…...

供热管网安全运行监测,提升供热管网安全性能
城市管网是城市的“生命线”之一,是城市赖以生存和发展的基础,在城市基础设施高质量发展中发挥着重要作用。供热管网作为城市生命线中连接供热管线与热用户的桥梁,担负着向企业和居民用户直接供热的重要职责。随着城市热力需求的急剧增加&…...

手写Mybatis:第14章-解析和使用ResultMap映射参数配置
文章目录 一、目标:ResultMap映射参数二、设计:ResultMap映射参数三、实现:ResultMap映射参数3.1 工程结构3.2 ResultMap映射参数类图3.3 添加类型处理器3.3.1 日期类型处理器3.3.2 类型处理器注册机 3.4 存放映射对象3.4.1 结果标志3.4.2 结…...

GE VME-7807RC-410001350-93007807-410001 K数字输入模块
通道数目: VME-7807RC-410001350-93007807-410001K 数字输入模块通常具有多个数字输入通道,可以同时监测多个数字信号。 输入类型: 这种模块通常用于监测数字信号,例如开关状态(ON/OFF)或计数器脉冲。 采…...

C++插入加密,替代加密
void 插入加密() {//缘由https://bbs.csdn.net/topics/396047473int n 1, j 0;char aa[60]{}, aaa[] "abcde";cin >> aa;while (j < 60 && (aa[j] - \0))cout << aa[j] << aaa[j % 5]; } void 插入加密() {//缘由https://bbs.csdn.n…...

Web前端开发概述
Web(World Wide Web,全球广域网)是指一种基于互联网的信息系统,通过超文本链接将全球各地的文档、图像、视频等资源相互关联起来,并通过Web浏览器进行交互浏览和访问。Web的发展使得人们可以方便地获取和共享各种类型的…...

Web自动化 —— Selenium元素定位与防踩坑
1. 基本元素定位一 from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By # selenium Service("../../chromedriver.exe") # driver webdriver.Chrome(serviceService) # driver.…...

【数据结构】树和二叉树的概念及结构(一)
目录 一,树的概念及结构 1,树的定义 2,树结点的分类及关系 3,树的表示 二,二叉树的概念及结构 1,二叉树的定义 2,特殊的二叉树 3,二叉树的性质 4,二叉树的存储结构 1&…...

第三章 USB应用笔记之USB鼠标(以STM32 hal库为例)
第三章 USB应用笔记之USB鼠标(以STM32 hal库为例) 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 第三章 USB应用笔记之USB鼠标(以STM32 hal库为例)前言一、STM32 U…...

微服务01-基本介绍+注册中心EureKa
基本介绍 服务集群:一个请求由多个服务完成,服务接口暴露,以便于相互调用; 注册中心:每个服务的状态,需要进行维护,我们可以在注册中心进行监控维护服务; 配置中心:这些…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...

AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践
在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...

macOS 终端智能代理检测
🧠 终端智能代理检测:自动判断是否需要设置代理访问 GitHub 在开发中,使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新,例如: fatal: unable to access https://github.com/ohmyzsh/oh…...