Mybatis中执行 findAll() 方法的详细执行步骤

自己实现Dao接口的方式

  • 首先是写一个 UserDao 的实现类:UserDaoImpl

    这一个步骤区别于一般情况,平时我们一般不会自己写Dao的实现类,而是通过Mybatis通过动态代理的方式帮我们实现一个Dao的代理类,然后我们直接使用;

public class UserDaoImpl implements UserDao {
    // 创建SqlSession的工厂对象
    private SqlSessionFactory factory;
	
    // 通过构造的方式将factory传入本实现类
    public UserDaoImpl (SqlSessionFactory factory) {
        this.factory = factory;
    }
    /**
    *  查询所有
    */
    @Override
    public List<User> findAll() {
        // 获取操控数据库的 SqlSession 对象
        SqlSession session = factory.openSession();
        // 调用selectList()方法来查询,dao接口全限定类名.findAll(方法名)
        List<User> users = session.selectList("com.xfzhao.dao.UserDao.findAll");
        // 关闭资源
        session.close();
        // 返回结果
        return users;
    }
}
  • MyBatis的主配置文件和映射配置文件没有什么变化;
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- mybatis的主配置文件 -->
<configuration>
    
    <typeAliases>
        <package name="com.xfzhao.domain"/>
    </typeAliases>
    
    <!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(连接池) -->
            <dataSource type="POOLED">
                <!-- 配置连接数据库的4个基本信息 -->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
    <mappers>
        <mapper resource="com/xfzhao/dao/Userdao.xml"></mapper>
    </mappers>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.xfzhao.dao.UserDao">
    <!--查询所有用户-->
    <select id="findAll" resultType="com.xfzhao.domain.User">
        select * from user;
    </select>
</mapper>
  • 测试方法:
public class mybatis {
    private InputStream in;
    private SqlSessionFactory factory;
    private UserDao userDao;


    @Before
    public void init() throws IOException {
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        factory = builder.build(in);
    }


    @Test
    public void testFindAll() {
       UserDao userDao = new UserDaoImpl(factory);
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
    }
}

流程解析-findAll

  1. 首先我们调用的是 SqlSession 的 selectList() 方法:

    List<User> users = session.selectList("com.xfzhao.dao.UserDao.findAll");
    
    • 因为 SqlSession 是接口,通过断点调试发现,真正的实现类的是 DefaultSqlSession,其中有三个 selectList() 方法,最终都是走的最后一个三个参数的方法;

    • 其中很关键的一步就是 this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); 这里的 executor 是 CachingExecutor

    // DefaultSqlSession 类
    
    public <E> List<E> selectList(String statement) {
      	// 调用下面一个方法
            return this.selectList(statement, (Object)null);
        }
    
        public <E> List<E> selectList(String statement, Object parameter) {
    		// 调用下面一个方法
            return this.selectList(statement, parameter, RowBounds.DEFAULT);
        }
    		
    	 // 真正执行的是这个方法
        public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
            List var5;
            try {
                MappedStatement ms = this.configuration.getMappedStatement(statement);
              // 走的是这里,这里的 executor 是 CachingExecutor  
              var5 = this.executor.query(ms, this.wrapCollection(parameter), 
                                         rowBounds, Executor.NO_RESULT_HANDLER);
            } catch (Exception var9) {
                throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
            } finally {
                ErrorContext.instance().reset();
            }
            return var5;
        }
    
  2. 下面找到 CachingExecutor :

    // CachingExecutor类
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    	// 省略代码...
      // 走的是这里,this.delegate 是 SimpleExecutor 类
      return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
     }
    
  3. 然后我们去 SimpleExecutor 类中:

    我们发现这个类中没有 query() 方法,只有 doQuery() 方法。于是我们就去找它的父类:BaseExecutor

    public class SimpleExecutor extends BaseExecutor {...}
    

    BaseExecutor 类中存在 query() 方法:而且存在两个query() 方法,我们直接看最后被调用的那一个

    public <E> List<E> query(MappedStatement ms, 
                             Object parameter, RowBounds rowBounds, 
                             ResultHandler resultHandler, CacheKey key, BoundSql boundSql) 
      				throws SQLException {
    		//....省略其他
      	// 主要走的就是这一步: 执行 queryFromDatabase() 方法
         list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
        }
    

    继续看 queryFromDatabase() 方法:

    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
      // ....
      // 走的是这一步,执行的是抽象的 doQuery()方法
      // 这里我们就知道了实际上绕了一圈还是走的是 simpleExecutor 的 doQuery() 方法
      list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
      // ....
        }
    

    最后我们再回去看 simpleExecutor 的 doQuery() 方法

      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
            Statement stmt = null;
    
            List var9;
            try {
                Configuration configuration = ms.getConfiguration();
                StatementHandler handler = configuration.newStatementHandler(this.wrapper, 
                                                                             ms, parameter, 
                                                                             rowBounds, 
                                                                             resultHandler, boundSql);
                stmt = this.prepareStatement(handler, ms.getStatementLog());\
                // 最后走的这一步:这里的 handler 是 RoutingStatementHandler  
                var9 = handler.query(stmt, resultHandler);
            } finally {
                this.closeStatement(stmt);
            }
            return var9;
        }
    
  4. 现在来到了 RoutingStatementHandler 类中:查看它的 query() 方法:

    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
      		 // 走的这里:这里的delegate是 PrepareStatementHandler
            return this.delegate.query(statement, resultHandler);
        }
    
  5. 然后继续进入类: PrepareStatementHandler 中,查看 query() 方法:这里就是JDBC 的常规操作

      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        // 到这里就很熟悉了,使用的是 prepareStatement对象
        PreparedStatement ps = (PreparedStatement)statement;
        // 执行的是它的 executor() 方法 
        ps.execute();
    		// 下面的步骤就是封装结果集 
    	  return this.resultSetHandler.handleResultSets(ps);
        }
    

执行流程图

自己实现Dao接口的方式

  • 首先是写一个 UserDao 的实现类:UserDaoImpl

    这一个步骤区别于一般情况,平时我们一般不会自己写Dao的实现类,而是通过Mybatis通过动态代理的方式帮我们实现一个Dao的代理类,然后我们直接使用;

public class UserDaoImpl implements UserDao {
    // 创建SqlSession的工厂对象
    private SqlSessionFactory factory;
	
    // 通过构造的方式将factory传入本实现类
    public UserDaoImpl (SqlSessionFactory factory) {
        this.factory = factory;
    }
    /**
    *  查询所有
    */
    @Override
    public List<User> findAll() {
        // 获取操控数据库的 SqlSession 对象
        SqlSession session = factory.openSession();
        // 调用selectList()方法来查询,dao接口全限定类名.findAll(方法名)
        List<User> users = session.selectList("com.xfzhao.dao.UserDao.findAll");
        // 关闭资源
        session.close();
        // 返回结果
        return users;
    }
}
  • MyBatis的主配置文件和映射配置文件没有什么变化;
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- mybatis的主配置文件 -->
<configuration>
    
    <typeAliases>
        <package name="com.xfzhao.domain"/>
    </typeAliases>
    
    <!-- 配置环境 -->
    <environments default="mysql">
        <!-- 配置mysql的环境-->
        <environment id="mysql">
            <!-- 配置事务的类型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置数据源(连接池) -->
            <dataSource type="POOLED">
                <!-- 配置连接数据库的4个基本信息 -->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
    <mappers>
        <mapper resource="com/xfzhao/dao/Userdao.xml"></mapper>
    </mappers>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.xfzhao.dao.UserDao">
    <!--查询所有用户-->
    <select id="findAll" resultType="com.xfzhao.domain.User">
        select * from user;
    </select>
</mapper>
  • 测试方法:
public class mybatis {
    private InputStream in;
    private SqlSessionFactory factory;
    private UserDao userDao;


    @Before
    public void init() throws IOException {
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        factory = builder.build(in);
    }


    @Test
    public void testFindAll() {
       UserDao userDao = new UserDaoImpl(factory);
        List<User> users = userDao.findAll();
        for (User user : users) {
            System.out.println(user);
        }
    }
}

流程解析-findAll

  1. 首先我们调用的是 SqlSession 的 selectList() 方法:

    List<User> users = session.selectList("com.xfzhao.dao.UserDao.findAll");
    
    • 因为 SqlSession 是接口,通过断点调试发现,真正的实现类的是 DefaultSqlSession,其中有三个 selectList() 方法,最终都是走的最后一个三个参数的方法;

    • 其中很关键的一步就是 this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); 这里的 executor 是 CachingExecutor

    // DefaultSqlSession 类
    
    public <E> List<E> selectList(String statement) {
      	// 调用下面一个方法
            return this.selectList(statement, (Object)null);
        }
    
        public <E> List<E> selectList(String statement, Object parameter) {
    		// 调用下面一个方法
            return this.selectList(statement, parameter, RowBounds.DEFAULT);
        }
    		
    	 // 真正执行的是这个方法
        public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
            List var5;
            try {
                MappedStatement ms = this.configuration.getMappedStatement(statement);
              // 走的是这里,这里的 executor 是 CachingExecutor  
              var5 = this.executor.query(ms, this.wrapCollection(parameter), 
                                         rowBounds, Executor.NO_RESULT_HANDLER);
            } catch (Exception var9) {
                throw ExceptionFactory.wrapException("Error querying database.  Cause: " + var9, var9);
            } finally {
                ErrorContext.instance().reset();
            }
            return var5;
        }
    
  2. 下面找到 CachingExecutor :

    // CachingExecutor类
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    	// 省略代码...
      // 走的是这里,this.delegate 是 SimpleExecutor 类
      return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
     }
    
  3. 然后我们去 SimpleExecutor 类中:

    我们发现这个类中没有 query() 方法,只有 doQuery() 方法。于是我们就去找它的父类:BaseExecutor

    public class SimpleExecutor extends BaseExecutor {...}
    

    BaseExecutor 类中存在 query() 方法:而且存在两个query() 方法,我们直接看最后被调用的那一个

    public <E> List<E> query(MappedStatement ms, 
                             Object parameter, RowBounds rowBounds, 
                             ResultHandler resultHandler, CacheKey key, BoundSql boundSql) 
      				throws SQLException {
    		//....省略其他
      	// 主要走的就是这一步: 执行 queryFromDatabase() 方法
         list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
        }
    

    继续看 queryFromDatabase() 方法:

    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
      // ....
      // 走的是这一步,执行的是抽象的 doQuery()方法
      // 这里我们就知道了实际上绕了一圈还是走的是 simpleExecutor 的 doQuery() 方法
      list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
      // ....
        }
    

    最后我们再回去看 simpleExecutor 的 doQuery() 方法

      public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
            Statement stmt = null;
    
            List var9;
            try {
                Configuration configuration = ms.getConfiguration();
                StatementHandler handler = configuration.newStatementHandler(this.wrapper, 
                                                                             ms, parameter, 
                                                                             rowBounds, 
                                                                             resultHandler, boundSql);
                stmt = this.prepareStatement(handler, ms.getStatementLog());\
                // 最后走的这一步:这里的 handler 是 RoutingStatementHandler  
                var9 = handler.query(stmt, resultHandler);
            } finally {
                this.closeStatement(stmt);
            }
            return var9;
        }
    
  4. 现在来到了 RoutingStatementHandler 类中:查看它的 query() 方法:

    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
      		 // 走的这里:这里的delegate是 PrepareStatementHandler
            return this.delegate.query(statement, resultHandler);
        }
    
  5. 然后继续进入类: PrepareStatementHandler 中,查看 query() 方法:这里就是JDBC 的常规操作

      public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        // 到这里就很熟悉了,使用的是 prepareStatement对象
        PreparedStatement ps = (PreparedStatement)statement;
        // 执行的是它的 executor() 方法 
        ps.execute();
    		// 下面的步骤就是封装结果集 
    	  return this.resultSetHandler.handleResultSets(ps);
        }
    

执行流程图

Mybatis中执行 findAll() 方法的详细执行步骤

上一篇:一个可悲的现实情况:中小型企业的规模无法使其免受网络***


下一篇:stm32 HardFault_Handler调试及问题查找方法——飞思卡尔