《手写Mybatis渐进式源码实践》实践笔记(第七章 SQL执行器的创建和利用) ...

打印 上一主题 下一主题

主题 980|帖子 980|积分 2940



  

第七章 SQL执行器的界说和实现


背景

技能背景

模板模式

模板模式(Template Method Pattern)是活动型计划模式之一,它在父类中界说了一个算法的框架,允许子类在不改变算法布局的情况下重写算法的某些特定步骤。这种模式紧张用来办理代码复用的问题,它通过把一些相同步骤提取到父类中,而将不同的步骤耽误到子类中实现,从而避免代码重复。
特点


  • 算法框架:在父类中界说算法的骨架,这些算法通常包罗一系列的步骤。
  • 扩展性:子类可以重新界说算法的某些步骤,而不影响算法的布局。
  • 代码复用:通过提取公共代码到父类,减少代码重复。
  • 控制反转:父类控制算法的流程,子类提供具体的实现。
布局

模板模式通常包罗以下脚色:


  • 抽象类(Abstract Class):界说模板方法和算法框架,其中模板方法是一个调用一系列抽象操作的方法。
  • 具体类(Concrete Class):实现抽象类中的抽象方法,这些方法可以是具体的方法,也可以是其他抽象方法。
  • 钩子方法(Hook Method):在抽象类中界说,可以被子类重写,但不带有抽象方法的声明。
示例代码(Java)

  1. // 抽象类
  2. abstract class Game {
  3.     // 模板方法
  4.     final void play() {
  5.         initialize();
  6.         startPlay();
  7.         endPlay();
  8.     }
  9.     // 抽象方法,由子类实现
  10.     abstract void initialize();
  11.     abstract void startPlay();
  12.     abstract void endPlay();
  13. }
  14. // 具体类
  15. class Cricket extends Game {
  16.     @Override
  17.     void initialize() {
  18.         System.out.println("Cricket game initialized.");
  19.     }
  20.     @Override
  21.     void startPlay() {
  22.         System.out.println("Cricket game started.");
  23.     }
  24.     @Override
  25.     void endPlay() {
  26.         System.out.println("Cricket game finished.");
  27.     }
  28. }
  29. // 具体类
  30. class Football extends Game {
  31.     @Override
  32.     void initialize() {
  33.         System.out.println("Football game initialized.");
  34.     }
  35.     @Override
  36.     void startPlay() {
  37.         System.out.println("Football game started.");
  38.     }
  39.     @Override
  40.     void endPlay() {
  41.         System.out.println("Football game finished.");
  42.     }
  43. }
  44. // 客户端代码
  45. public class TemplatePatternDemo {
  46.     public static void main(String[] args) {
  47.         Game game = new Cricket();
  48.         game.play();
  49.         game = new Football();
  50.         game.play();
  51.     }
  52. }
复制代码
在这个例子中,Game 类界说了游戏的算法框架,而 Cricket 和 Football 类分别实现了具体的游戏初始化、开始和竣事的方法。如许,不同的游戏可以共享相同的游戏流程,同时保持各自的特性。
业务背景

在第6章节我们实现了毗连池/非毗连池的数据源,可以在调用执行SQL的时间,通过我们实现的池化技能完成数据库的操作。
那么关于池化数据源的调用、执行和效果封装,现在只是在 DefaultSqlSession 中进行发起 ,代码层面写死了流程,这种方式不适用于后续扩展利用,因为SqlSession 中新增界说的方法都要处理对池化数据源的调用逻辑。
  1. @Override
  2.     public <T> T selectOne(String statement, Object parameter) {
  3.         try {
  4.             // 映射语句
  5.             MappedStatement mappedStatement = configuration.getMappedStatement(statement);
  6.             // 环境
  7.             Environment environment = configuration.getEnvironment();
  8.             // 连接
  9.             Connection connection = environment.getDataSource().getConnection();
  10.             BoundSql boundSql = mappedStatement.getBoundSql();
  11.             PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSql());
  12.             preparedStatement.setLong(1, Long.parseLong(((Object[]) parameter)[0].toString()));
  13.             // 执行查询
  14.             ResultSet resultSet = preparedStatement.executeQuery();
  15.             // 结果封装
  16.             List<T> objectList = resultSet2Obj(resultSet, Class.forName(boundSql.getResultType()));
  17.             return objectList.get(0);
  18.         } catch (Exception e) {
  19.             e.printStackTrace();
  20.             return null;
  21.         }
  22.     }
复制代码


  • 解耦 DefaultSqlSession#selectOne 方法中关于对数据源的调用、执行和效果封装,提供新的功能模块替换这部门硬编码的逻辑处理。
  • 只有提供了单独的执行方法入口,我们才能更好的扩展和应对这部门内容里的需求变化,包罗了各类入参、效果封装、执行器范例、批处理等,来满足不同样式的用户需求,也就是配置到 Mapper.xml 中的具体信息。
目标

基于当前框架实现,解耦 DefaultSqlSession#selectOne 方法中关于对数据源的调用、执行和效果封装,提供SQL执行器功能模块替换这部门硬编码的逻辑处理。
计划

ORM 框架渐进式的开辟过程上,渐渐实现的执举措作:解析配置、代理对象、映射方法等,直至我们前面章节对数据源的包装和利用,只不过我们把数据源的操作硬编码到了 DefaultSqlSession 的执行方法上了。
为了解耦这块的功能处理,必要引入SQL执行器的服务功能,将SQL执行器随着 DefaultSqlSession 创建时传入,之后具体的方法调用就可以通过执行器来处理。
团体计划如图 :



  • 提取执行器的接口,界说执行方法、事件获取和相应提交、回滚、关闭的方法界说;
  • 执行器是一种标准的执行过程,由抽象类进行实现,可以对过程内容进行模板模式的过程包装。在包装过程中界说抽象类,由具体的子类来实现。这一部门在下文的代码中会体现到 SimpleExecutor 简单执行器实现中;
  • 对 SQL 的处理,利用 JDBC 执行 SQL 的时间,分为了简单处理和预处理,预处理中包罗准备语句、参数化通报、执行查询,以及最后的效果封装和返回。以是我们这里也必要把 JDBC 这部门的步骤,分为布局化的类过程来实现,便于功能的拓展。具体代码紧张体如今语句处理器 StatementHandler 的接口实现中。
实现

工程代码

类图



  • Executor 接口界说为执行器入口,确定事件操作和 SQL 执行的同一标准。用BaseExecutor抽象类处理同一共用的事件和执行SQL的标准流程,并界说执行 SQL 的抽象接口#doQuery(),后续由子类实现。
  • SimpleExecutor 简单SQL 执行器实现类中,处理 doQuery 方法的具体操作过程。这个过程中则会引入 SQL 语句处理器的创建,创建过程仍由 configuration 配置项提供。你会发现许多如许的生成处理,都来自于配置项
  • 执行器开辟完成后,由DefaultSqlSessionFactory 开启 openSession 的时间,随着构造函数参数通报给 DefaultSqlSession 中,如许在执行 DefaultSqlSession#selectOne 的时间就可以调用执行器进行处理了。也就由此完成解耦操作了。
实现步骤

1.执行器的界说和实现

执行器分为接口、抽象类、简单执行器实现类三部门,通常在框架的源码中对于一些标准流程的处理,都会有抽象类的存在。它负责提供共性功能逻辑,以及对接口方法的执行过程进行界说和处理,并提供抽象接口交由子类实现。这种计划模式也被界说为模板模式。
1-1. Executor界说执行器接口



  • 在执行器中界说的接口包罗事件相关的处理方法和执行SQL查询的操作,随着后续功能的迭代还会继承补充其他的方法。
  1. public interface Executor {
  2.    
  3.     ResultHandler NO_RESULT_HANDLER = null;
  4.     <E> List<E> query(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql);
  5.     Transaction getTransaction();
  6.     void commit(boolean required) throws Exception;
  7.     void rollback(boolean required) throws Exception;
  8.     void close(boolean forceRollback);
  9.    
  10. }
复制代码
1-2.BaseExecutor执行器抽象基类



  • 封装了执行器的全部接口,如许具体的子类继承抽象类后,就可以直接利用这些共性的方法。在 query 查询方法中,封装一些必要的流程处理,如果检测关闭等,在 Mybatis 源码中另有一些缓存的操作,这里暂时剔撤除,以核心流程为主。读者伙伴在学习的过程中可以与源码进行对照学习。
  1. public abstract class BaseExecutor implements Executor {
  2.     Logger logger = LoggerFactory.getLogger(BaseExecutor.class);
  3.     protected Configuration configuration;
  4.     protected Transaction transaction;
  5.     protected Executor wrapper;
  6.     private boolean closed;
  7.     protected BaseExecutor(Configuration configuration, Transaction transaction) {
  8.         this.configuration = configuration;
  9.         this.transaction = transaction;
  10.         this.wrapper = this;
  11.     }
  12.     // 连接关闭处理统一逻辑
  13.     @Override
  14.     public <E> List<E> query(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql) {
  15.         if (closed) {
  16.             throw new RuntimeException("Executor was closed.");
  17.         }
  18.         return doQuery(ms, parameter, resultHandler, boundSql);
  19.     }
  20.     protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql);
  21.     @Override
  22.     public Transaction getTransaction() {
  23.         if (closed) {
  24.             throw new RuntimeException("Executor was closed.");
  25.         }
  26.         return transaction;
  27.     }
  28.     @Override
  29.     public void commit(boolean required) throws Exception {
  30.         if (closed) {
  31.             throw new RuntimeException("Cannot commit, transaction is already closed");
  32.         }
  33.         if (required) {
  34.             transaction.commit();
  35.         }
  36.     }
  37.     @Override
  38.     public void rollback(boolean required) throws SQLException {
  39.         if (closed) {
  40.             throw new RuntimeException("Cannot rollback, transaction is already closed");
  41.         }
  42.         if (required) {
  43.             transaction.rollback();
  44.         }
  45.     }
  46.     @Override
  47.     public void close(boolean forceRollback) {
  48.         try {
  49.             try {
  50.                 rollback(forceRollback);
  51.             } finally {
  52.                 transaction.close();
  53.             }
  54.         } catch (SQLException e) {
  55.             logger.warn("Unexpected exception on closing transaction.  Cause: " + e);
  56.         } finally {
  57.             transaction = null;
  58.             closed = true;
  59.         }
  60.     }
  61. }
复制代码
1-3. SimpleExecutor 简单执行器实现



  • 简单执行器 SimpleExecutor 继承抽象基类,实现抽象方法 doQuery,在这个方法中包装数据源的获取、语句处理器的创建,以及对 Statement 的实例化和相关参数设置。最后执行 SQL 的处理和效果的返回操作。
  1. public class SimpleExecutor extends BaseExecutor {
  2.     public SimpleExecutor(Configuration configuration, Transaction transaction) {
  3.         super(configuration, transaction);
  4.     }
  5.     @Override
  6.     protected <E> List<E> doQuery(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql) {
  7.         //处理查询逻辑.
  8.         try {
  9.             //获取配置信息
  10.             Configuration configuration = ms.getConfiguration();
  11.             //获取StatementHandler
  12.             StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, resultHandler, boundSql);
  13.             //获取连接
  14.             Connection connection = transaction.getConnection();
  15.             Statement stmt = handler.prepare(connection);
  16.             handler.parameterize(stmt);
  17.             return handler.query(stmt, resultHandler);
  18.         } catch (SQLException e) {
  19.             e.printStackTrace();
  20.             return null;
  21.         }
  22.     }
  23. }
复制代码
2.语句处理器

语句处理器是 SQL 执行器中依靠的部门,SQL 执行器封装事件、毗连和检测情况等,而语句处理器则是准备语句、参数化通报、执行SQL、封装效果的处理。
2-1.StatementHandler界说语句处理接口



  • 语句处理器的核心: 准备语句、参数化通报参数、执行查询的操作,这里对应的 Mybatis 源码中还包罗了 update、批处理、获取参数处理器等。
  1. public interface StatementHandler {
  2.     /**
  3.      * 准备语句
  4.      */
  5.     Statement prepare(Connection connection) throws SQLException;
  6.     /**
  7.      * 参数化
  8.      */
  9.     void parameterize(Statement statement) throws SQLException;
  10.     /**
  11.      * 执行查询
  12.      */
  13.     <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException;
  14. }
复制代码
2-2. BaseStatementHandler 语句处理器抽象基类



  • 语句处理器基类中,封装参数信息、效果信息。不过这里我们暂时还不会做过多的参数处理,比如jdbc的字段范例转换等。这部门内容随着我们整个执行器的布局创建完毕后,再进行迭代开辟。
  • 对 BaseStatementHandler#prepare 方法的处理,包罗界说实例化抽象方法,这个方法交由各个具体的实现子类进行处理。
  1. public abstract class BaseStatementHandler implements StatementHandler {
  2.     protected final Configuration configuration;
  3.     protected final Executor executor;
  4.     protected final MappedStatement mapperStatement;
  5.     protected final Object parameter;
  6.     protected final ResultSetHandler resultSetHandler;
  7.     protected BoundSql boundSql;
  8.     protected BaseStatementHandler(Executor executor, MappedStatement mapperStatement, Object parameter,
  9.                                    ResultHandler resultHandler,
  10.                                    BoundSql boundSql) {
  11.         this.executor = executor;
  12.         this.configuration = mapperStatement.getConfiguration();
  13.         this.mapperStatement = mapperStatement;
  14.         this.parameter = parameter;
  15.         this.boundSql = boundSql;
  16.         this.resultSetHandler = configuration.newResultSetHandler(executor, mapperStatement, boundSql);
  17.     }
  18.     @Override
  19.     public Statement prepare(Connection connection) throws SQLException {
  20.         Statement statement = null;
  21.         try {
  22.             statement = instantiateStatement(connection);
  23.             statement.setQueryTimeout(350);
  24.             statement.setFetchSize(10000);
  25.             return statement;
  26.         } catch (Exception e) {
  27.             throw new RuntimeException("Error preparing statement.  Cause: " + e, e);
  28.         }
  29.     }
  30.     protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
  31. }
复制代码
2-3. SimpleStatementHandler 简单语句处理器类



  • SimpleStatementHandler 简单语句处理器,只是对 SQL 的最基本执行,没有参数的设置.
  1. public class SimpleStatementHandler extends BaseStatementHandler {
  2.     public SimpleStatementHandler(Executor executor, MappedStatement mapperStatement, Object parameter,
  3.                                   ResultHandler resultHandler,
  4.                                   BoundSql boundSql) {
  5.         super(executor, mapperStatement, parameter, resultHandler, boundSql);
  6.     }
  7.     @Override
  8.     protected Statement instantiateStatement(Connection connection) throws SQLException {
  9.         return connection.createStatement();
  10.     }
  11.     @Override
  12.     public void parameterize(Statement statement) throws SQLException {
  13.         // N/A
  14.     }
  15.     @Override
  16.     public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  17.         String sql = boundSql.getSql();
  18.         statement.execute(sql);
  19.         return resultSetHandler.handleResultSets(statement);
  20.     }
  21. }
复制代码
2-4. PrepareStatementHandler 预处理语句处理器类



  • 预处理语句处理器中包罗 instantiateStatement 预处理 SQL、parameterize 设置参数,以及 query 查询的执行的操作。
  • 这里必要注意,当前 parameterize 设置参数中还是写死的,后续这部门再进行完善。
  • query 方法则是执行查询和对效果的封装,效果的封装现在也是比较简单的处理,只是把我们前面章节中对象的内容摘取出来进行封装,这部门暂时没有改变。都放在后续进行完善处理。
  1. public class PreparedStatementHandler extends BaseStatementHandler {
  2.     public PreparedStatementHandler(Executor executor, MappedStatement mapperStatement, Object parameter,
  3.                                     ResultHandler resultHandler,
  4.                                     BoundSql boundSql) {
  5.         super(executor, mapperStatement, parameter, resultHandler, boundSql);
  6.     }
  7.     @Override
  8.     protected Statement instantiateStatement(Connection connection) throws SQLException {
  9.         String sql = boundSql.getSql();
  10.         return connection.prepareStatement(sql);
  11.     }
  12.     @Override
  13.     public void parameterize(Statement statement) throws SQLException {
  14.         PreparedStatement ps = (PreparedStatement) statement;
  15.         ps.setLong(1, Long.parseLong(((Object[]) parameter)[0].toString()));
  16.     }
  17.     @Override
  18.     public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  19.         PreparedStatement ps = (PreparedStatement) statement;
  20.         ps.execute();
  21.         return resultSetHandler.<E>handleResultSets(ps);
  22.     }
  23. }
复制代码
3.执行器创建和利用

执行器开辟完成以后,则必要在串联到 DefaultSqlSession 中进行利用。串联过程:在 创建 DefaultSqlSession 的时间,构建出执行器,并作为参数通报到defaultSqlSession。这块涉及到 DefaultSqlSessionFactory#openSession 的处理。
3-1. 开启执行器



  • openSession 中开启事件tx, 用于执行器的创建,具体实现可以看 configuration.newExecutor 代码,这部门没有太多复杂的逻辑。读者可以参考源码进行学习。
  • 在执行器创建完毕后,以参数方式通报给 DefaultSqlSession,如许就把整个过程串联起来了。
  1. public class DefaultSqlSessionFactory implements SqlSessionFactory {
  2.     private final Configuration configuration;
  3.     public DefaultSqlSessionFactory(Configuration configuration) {
  4.         this.configuration = configuration;
  5.     }
  6.     @Override
  7.     public SqlSession openSession() {
  8.         Transaction tx = null;
  9.         try {
  10.             final Environment environment = configuration.getEnvironment();
  11.             TransactionFactory transactionFactory = environment.getTransactionFactory();
  12.             tx = transactionFactory.newTransaction(configuration.getEnvironment().getDataSource(),
  13.                     TransactionIsolationLevel.READ_COMMITTED,
  14.                     false);
  15.             // 创建执行器
  16.             final Executor executor = configuration.newExecutor(tx);
  17.             // 创建DefaultSqlSession
  18.             return new DefaultSqlSession(configuration, executor);
  19.         } catch (Exception e) {
  20.             try {
  21.                 assert tx != null;
  22.                 tx.close();
  23.             } catch (SQLException ignore) {
  24.             }
  25.             throw new RuntimeException("Error opening session.  Cause: " + e);
  26.         }
  27.     }
  28. }
复制代码
3-2. 利用执行器



  • 完成上面执行器的全部实现后,就可以调用执行器进行功能解耦了。在 DefaultSqlSession#selectOne 中获取 MappedStatement 映射语句类后,则通报给执行器进行处理,那么如今这个类经过计划思想的解耦后,就变得更加干净整洁了,更易于维护和扩展了。
  1. public class DefaultSqlSession implements SqlSession {
  2.     /**
  3.      * 配置项.
  4.      */
  5.     private Configuration configuration;
  6.     private Executor executor;
  7.     public DefaultSqlSession(Configuration configuration, Executor executor) {
  8.         this.configuration = configuration;
  9.         this.executor = executor;
  10.     }
  11.     public Configuration getConfiguration() {
  12.         return configuration;
  13.     }
  14.     /**
  15.      * 根据给定的执行SQL获取一条记录的封装对象.
  16.      *
  17.      * @param statement
  18.      * @param <T>
  19.      * @return
  20.      */
  21.     @Override
  22.     public <T> T selectOne(String statement) {
  23.         return this.selectOne(statement, null);
  24.     }
  25.     @Override
  26.     public <T> T selectOne(String statement, Object parameter) {
  27.         //映射语句
  28.         MappedStatement mappedStatement = configuration.getMappedStatement(statement);
  29.         //使用执行器.
  30.         List<T> list = executor.query(mappedStatement, parameter, Executor.NO_RESULT_HANDLER, mappedStatement.getBoundSql());
  31.         return list.get(0);
  32.     }
  33.    
  34.     @Override
  35.     public <T> T getMapper(Class<T> type) {
  36.         return configuration.getMapper(type, this);
  37.     }
  38. }
复制代码
测试

事先准备

创建库表
  1. -- 建表
  2. CREATE TABLE `my_user` (
  3.   `id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增ID',
  4.   `user_id` varchar(9) DEFAULT NULL COMMENT '用户ID',
  5.   `user_head` varchar(16) DEFAULT NULL COMMENT '用户头像',
  6.   `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  7.   `update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  8.   `user_name` varchar(64) DEFAULT NULL COMMENT '用户名',
  9.   `user_password` varchar(64) DEFAULT NULL COMMENT '用户密码',
  10.   PRIMARY KEY (`id`)
  11. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ;
  12. -- 插入数据
  13. INSERT INTO my_user (user_id, user_head, create_time, update_time, user_name, user_password) VALUES('1', '头像', '2024-12-13 18:00:12', '2024-12-13 18:00:12', '小苏', 's123asd');
复制代码
界说一个数据库接口 IUserDao
IUserDao
  1. public interface IUserDao {
  2.     String queryUserInfoById(String uid);
  3. }
复制代码
配置数据源


  • 通过 mybatis-config-datasource.xml 配置数据源信息,包罗:driver、url、username、password
  • 这里DataSource 配置的是 DRUID,因为我们实现的是这个数据源的处理方式。
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  3.         "http://mybatis.org/dtd/mybatis-3-config.dtd">
  4. <configuration>
  5.     <environments default="development">
  6.         <environment id="development">
  7.             <transactionManager type="JDBC"/>
  8.             <dataSource type="UNPOOLED">  #无池化时配置成这个类型值
  9.             <dataSource type="POOLED">  #池化时配置城这个类型值
  10.                 <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
  11.                 <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true"/>
  12.                 <property name="username" value="root"/>
  13.                 <property name="password" value="123456"/>
  14.             </dataSource>
  15.         </environment>
  16.     </environments>
  17.     <mappers>
  18.         <mapper resource="mapper/User_Mapper.xml"/>
  19.     </mappers>
  20. </configuration>
复制代码
界说对应的mapper xml文件
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3. <mapper namespace="cn.suwg.mybatis.test.dao.IUserDao">
  4.     <select id="queryUserInfoById" parameterType="java.lang.Long" resultType="cn.suwg.mybatis.test.po.User">
  5.         SELECT id, user_id, user_head, create_time
  6.         FROM user
  7.         where id = #{id}
  8.     </select>
  9. </mapper>
复制代码
测试用例



  • 单位测试,通报一个 1L 的 long 范例参数,进行方法的调用处理。通过单位测试验证执行器的处理过程,读者在学习的过程中可以进行断点测试,学习每个过程的处理内容。
  • mybatis-config-datasource.xml 中 dataSource 数据源范例的调整 dataSource type="DRUID/POOLED/UNPOOLED",按需配置验证.
单位测试

  1. public class ApiTest {
  2.     private Logger logger = LoggerFactory.getLogger(ApiTest.class);
  3.     // 测试SqlSessionFactory
  4.     @Test
  5.     public void testSqlSessionFactory() throws IOException {
  6.         // 1.从xml文件读取mybatis配置项, 从SqlSessionFactory获取SqlSession.
  7.         Reader reader = Resources.getResourceAsReader("mybatis-config-datasource.xml");
  8.         SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  9.         SqlSession sqlSession = sqlSessionFactory.openSession();
  10.         // 2.获取映射器对象
  11.         IUserDao userDao = sqlSession.getMapper(IUserDao.class);
  12.         // 4.测试验证
  13.         User user = userDao.queryUserInfoById(1L);
  14.         logger.info("测试结果:{}", JSON.toJSONString(user));
  15.     }
  16. }
复制代码
测试效果





  • 从输出的效果来看,我们已经顺遂完成,将DefaultSqlSession#selectOne中的调用,替换成了执行器来完成整个过程,通过解耦这部门的逻辑操作,后续我们扩展就更方便了,这块的功能测试通过后,后续就可以聚焦在新的功能扩展点开辟了。
总结



  • 整个章节的实现都是在处明白耦这件事故,从DefaultSqlSession#selectOne 对数据源的处明白耦到执行器中进行操作。而执行器中又包罗了对 JDBC 处理的拆解,链接、准备语句、封装参数、处理效果,全部的这些过程经过解耦后的类和方法,就都可以在以后的功能迭代中非常方便的完成扩展了。
  • 本章节也为我们后续扩展参数的处理、效果集的封装预留出了扩展点,以及对于不同的语句处理器选择的问题,都必要在后续进行完善和补充。现在我们串联出来的是最核心的骨架布局,随着后续的渐进式开辟连续迭代完善。
  • 对于源码的学习,读者要经历看、写、思考、应用等几个步骤的过程,才能更好的吸收这里面的思想,每一次的阅读源码,都能有新的认知和领会。不要只是Ctrl+C,Ctrl+V一遍就完事了,否则也就失去了跟着学习源码的意义。
   参考书籍:《手写Mybatis渐进式源码实践》
  书籍源代码:https://github.com/fuzhengwei/book-small-mybatis

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

风雨同行

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表