首页 > 代码库 > MyBatis源码解读(4)——SqlSession(上)
MyBatis源码解读(4)——SqlSession(上)
在上一篇博客中提到MyBatis是如何实现代理类MapperProxy,并抛出了一个问题——是怎么执行一个具体的sql语句的,在文末中提到了MapperMethod的execute采用命令模式来判断是何种sql语句,并将具体语句的执行交由SqlSession处理。所以此篇博客正是要讲到SqlSession。
在SqlSession接口中包含了所有可能执行的sql语句在这里不一一列举,请参考org.apache.ibatis.session.SqlSession源码。DefaultSqlSession是SqlSession的实现类,所以我们重点关注DefaultSqlSession类。在上一篇博客中提到了我们将选择MapperProxy的executeForMany方法。在此方法中调用了SqlSession.<E>selectList方法。所以我们来看看org.apache.ibatis.session.default.DefaultSqlSession类的selectList方法。
1 //org.apache.ibatis.session.defaults.DefaultSqlSession 2 …… 3 @Override 4 public <E> List<E> selectList(String statement) { 5 return this.selectList(statement, null); 6 } 7 8 @Override 9 public <E> List<E> selectList(String statement, Object parameter) { 10 return this.selectList(statement, parameter, RowBounds.DEFAULT); 11 } 12 13 @Override 14 public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { 15 try { 16 MappedStatement ms = configuration.getMappedStatement(statement); 17 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); 18 } catch (Exception e) { 19 throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); 20 } finally { 21 ErrorContext.instance().reset(); 22 } 23 } 24 ……
我们看到关于selectList有三个重载方法,最后调用的都是public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds)。在此方法中第一个参数为String类型且命名为statement,第二个参数为Object命名为parameter,回到MapperMethod。
1 //org.apache.ibatis.binding.MapperMethod 2 private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { 3 List<E> result; 4 Object param = method.convertArgsToSqlCommandParam(args); 5 if (method.hasRowBounds()) { 6 RowBounds rowBounds = method.extractRowBounds(args); 7 result = sqlSession.<E>selectList(command.getName(), param, rowBounds); //注意传递了什么参数进来 8 } else { 9 result = sqlSession.<E>selectList(command.getName(), param); 10 } 11 // issue #510 Collections & arrays support 12 if (!method.getReturnType().isAssignableFrom(result.getClass())) { 13 if (method.getReturnType().isArray()) { 14 return convertToArray(result); 15 } else { 16 return convertToDeclaredCollection(sqlSession.getConfiguration(), result); 17 } 18 } 19 return result; 20 }
第7行代码可以看到传递了什么参数进来,跟踪command.getName()是怎么来的。
private final SqlCommand command;
看来是一个叫做SqlCommand的变量,进而我们可以发现这个SqlCommand是MapperMethod的静态内部类。
1 //org.apache.ibatis.binding.MapperMethod 2 public static class SqlCommand { 3 4 private final String name; 5 private final SqlCommandType type; 6 7 public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) { 8 String statementName = mapperInterface.getName() + "." + method.getName(); //获取接口+方法名 9 MappedStatement ms = null; //定义一个MappedStatement,这个MapperedStatement稍后介绍 10 if (configuration.hasStatement(statementName)) { //从Configuration对象查找是否有这个方法的全限定名称 11 ms = configuration.getMappedStatement(statementName); //有,则根据方法的全限定名称获取MappedStatement实例 12 } else if (!mapperInterface.equals(method.getDeclaringClass())) { // issue #35//如果没有在Configuration对象中找到这个方法,则向上父类中获取全限定方法名 13 String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName(); 14 if (configuration.hasStatement(parentStatementName)) { //在向上的父类查找是否有这个全限定方法名 15 ms = configuration.getMappedStatement(parentStatementName); //有,则根据方法的全限定名称获取MappedStatement实例 16 } 17 } 18 if (ms == null) { 19 if(method.getAnnotation(Flush.class) != null){ 20 name = null; 21 type = SqlCommandType.FLUSH; 22 } else { 23 throw new BindingException("Invalid bound statement (not found): " + statementName); 24 } 25 } else { 26 name = ms.getId(); //这个ms.getId,其实就是我们在mapper.xml配置文件中配置一条sql语句时开头的<select id="……"……,即是接口的该方法名全限定名称 27 type = ms.getSqlCommandType(); //显然这是将sql是何种类型(insert、update、delete、select)赋给type 28 if (type == SqlCommandType.UNKNOWN) { 29 throw new BindingException("Unknown execution method for: " + name); 30 } 31 } 32 } 33 34 public String getName() { 35 return name; 36 } 37 38 public SqlCommandType getType() { 39 return type; 40 } 41 }
大致对MapperMethod的解读到此,再次回到DefaultSqlSession中,到这里可能稍微有点混乱。画个图来理解这一段,希望能帮助理解。
这次又一次回到了DefaultSqlSession.selectList。此处不再贴出selectList所有方法,而是只贴出一句,根据接口方法的全限定名称获取MappedStatement。
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
在获取到MappedStatement后,碰到了第一个SqlSession下的四大对象之一:Executor执行器。看来还是没有讲到SqlSession,再放到下一节吧。可以思考下这个MappedStatement是什么意思,望文生义就能猜出个大概。
MyBatis源码解读(4)——SqlSession(上)