Mybatis源码分析(五)探究SQL语句的执行过程

一、重温JDBC

Java Database Connectivity,简称JDBC。是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。 随着Java ORM框架的发展,已经很少有机会再在生产系统中写JDBC的代码来访问数据库了,但是基本流程我们还是要熟悉。下面以一个简单的查询为例,温故一下JDBC。

public static void main(String[] args) throws Exception {
   Connection conn = getConnection();  
   String sql = "select * from user where 1=1 and id = ?";
   PreparedStatement stmt = conn.prepareStatement(sql);
   stmt.setString(1, "501440165655347200");
   ResultSet rs = stmt.executeQuery();
   while(rs.next()){
       String username = rs.getString("username");
       System.out.print("姓名: " + username);
   }
}

 

从上面的代码来看,一次简单的数据库查询操作,可以分为几个步骤。

  • 创建Connection连接

  • 传入参数化查询SQL语句构建预编译对象PreparedStatement

  • 设置参数

  • 执行SQL

  • 从结果集中获取数据

那么,咱们的主角Mybatis是怎样完成这一过程的呢?不着急,咱们一个一个来看。

二、sqlSession

在上一章节的内容中,我们已经看到了在Service层通过@Autowired注入的userMapper是个代理类,在执行方法的时候实际上调用的是代理类的invoke通知方法。

public class MapperProxy<T> implements InvocationHandler{
   public Object invoke(Object proxy, Method method, Object[] args)
       final MapperMethod mapperMethod = cachedMapperMethod(method);
       return mapperMethod.execute(sqlSession, args);
   }
}

 

1 、创建MapperMethod对象

MapperMethod对象里面就两个属性,SqlCommand和MethodSignature。

SqlCommand包含了执行方法的名称和方法的类型,比如 UNKNOWN,INSERT,UPDATE,DELETE,SELECT,FLUSH。 MethodSignature可以简单理解为方法的签名信息。里面包含:返回值类型、是否void、是否为集合类型、是否为Cursor等,主要还获取到了方法参数上的@Param注解的名称,方便下一步获取参数值。 比如,如果方法上加了@Param的参数: UsergetUserById(@Param(value="id")Stringid,@Param(value="password")Stringpassword);,参数会被解析成 {0=id,1=password}

2、执行

判断方法的SQL类型和返回值类型 ,调用相应的方法。以方法 UsergetUserById(Stringid,Stringpassword)为例,会调用到selectOne()方法。

public class MapperMethod {

   public Object execute(SqlSession sqlSession, Object[] args) {
       Object result;
       switch (command.getType()) {
       case INSERT: {}
       case UPDATE: {}
       case DELETE: {}
       case SELECT:
       if (method.returnsVoid() && method.hasResultHandler()) {
           //无返回值
       } else if (method.returnsMany()) {
           //返回集合类型
           result = executeForMany(sqlSession, args);
       } else if (method.returnsMap()) {
           //返回Map类型
           result = executeForMap(sqlSession, args);
       } else if (method.returnsCursor()) {
           //返回Cursor
           result = executeForCursor(sqlSession, args);
       } else {
           //将参数args转换为SQL命令的参数
           //默认会添加一个《param+参数索引》的参数名
           //{password=123456, id=501441819331002368, param1=501441819331002368, param2=123456}
           Object param = method.convertArgsToSqlCommandParam(args);
           result = sqlSession.selectOne(command.getName(), param);
       }
       ...
       return result;
   }
}

 

可以看到,sqlSession.selectOne就可以获取到数据库中的值并完成转换工作。这里的sqlSession就是 SqlSessionTemplate实例的对象,所以它会调用到

public class SqlSessionTemplate{
   public <T> T selectOne(String statement, Object parameter) {
       return this.sqlSessionProxy.<T> selectOne(statement, parameter);
   }
}

 

sqlSessionProxy也是个代理对象。关于它的创建咱们上节课也很认真的分析了,总之它实际会调用到 SqlSessionInterceptor.invoke()

3、创建sqlSession对象

sqlSession我们熟悉呀,它作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能。关于它的创建、执行、提交和资源清理都是在SqlSessionInterceptor的通知方法中完成的。

private class SqlSessionInterceptor implements InvocationHandler {
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       //创建SqlSession对象
       SqlSession sqlSession = getSqlSession(
       SqlSessionTemplate.this.sqlSessionFactory,
       SqlSessionTemplate.this.executorType,
       SqlSessionTemplate.this.exceptionTranslator);
       try {
           //调用sqlSession实际方法
           Object result = method.invoke(sqlSession, args);
           return result;
       } catch (Throwable t) {
           ....
       } finally {
           if (sqlSession != null) {
               closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
           }
       }
   }
}

 

上面的重点就是创建了SqlSession并执行它的方法,它是一个DefaultSqlSession实例的对象,里面主要有一个通过configuration创建的执行器,在这里它是SimpleExecutor。

那么,invoke方法实际调用的就是 DefaultSqlSession.selectOne()

三、获取BoundSql对象

DefaultSqlSession中的 selectOne()方法最终也会调用到 selectList()方法。它先从数据大管家configuration中根据请求方法的全名称拿到对应的MappedStatement对象,然后调用执行器的查询方法。

1、获取MappedStatement对象

//statement是调用方法的全名称,parameter为参数的Map
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
   //在mapper.xml中每一个SQL节点都会封装为MappedStatement对象
   //在configuration中就可以通过请求方法的全名称获取对应的MappedStatement对象
   Ma
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值