Mybatis源码分析:MapperMethod中内部静态类SqlCommand的作用
MapperMethod中内部静态类SqlCommand的作用
在MapperMethod初始化中,会首先初始化两个内部静态类,SqlCommand就是其中之一,SqlCommand的作用主要体现在MapperMethod类的execute()方法里,SqlCommand为其提供了查询类型和方法id两个信息,从而使用Sqlseesion执行不同的方法,那么SqlCommand是如何获取到查询类型和方法id的呢?其中又做了哪些操作呢?
首先看看构造器中的代码执行顺序。构造器需要传入配置类,Mapper接口和Method类.
- 从Method类中获取方法名和该方法所在的类路径
- 根据mapper接口信息和配置类获取到XML对应的MapperStatement对象
- 判断映射语句对象是否为NULL,如果不为NULL,则从中获取到语句的id和待执行的Sql类型,如果为NULL,再次判断待执行方法上是否标注有Flush的注解,如果不满足条件,则会抛出BindingException异常。
流程图如下所示:
值得注意的是,在获取映射语句对象是通过调用resolveMappedStatement()方法实现的。见如下的讲述。
1 public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) { 2 //获取方法名 3 final String methodName = method.getName(); 4 //获取方法所在的类 5 final Class<?> declaringClass = method.getDeclaringClass(); 6 //获取映射语句信息 7 MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass, 8 configuration); 9 //如果映射语句对象是NULL,那么查看该方法上是否标注了FLUSH标签,如果存在,则设置查询类型为FLUSH,否则抛出BindingException异常表示接口找不到定义的方法。 10 if (ms == null) { 11 if (method.getAnnotation(Flush.class) != null) { 12 name = null; 13 type = SqlCommandType.FLUSH; 14 } else { 15 throw new BindingException("Invalid bound statement (not found): " 16 + mapperInterface.getName() + "." + methodName); 17 } 18 } 19 //如果映射语句对象不为空,则设置指定的查询类型,如果为UNKNOWN 类型,则直接抛出BindingException异常 20 else { 21 name = ms.getId(); 22 type = ms.getSqlCommandType(); 23 if (type == SqlCommandType.UNKNOWN) { 24 throw new BindingException("Unknown execution method for: " + name); 25 } 26 } 27 }
resolveMappedStatement()方法
该方法主要是获取映射语句对象,在xml配置中,像select,update,delete,insert 都需要提供id号和sql语句以及入参和出参信息,而MappedStatement就是xml到java对象的映射,一个<select//>标签就会对应一个MappedStatement对象,这个目前了解即可,在后续会专门进行介绍。现简要说明下代码的执行流程。
1. 获取待执行语句的id号,id号形式为类路径.方法名
2. 在配置类中是否能找到该语句的id号,如果能找到,则直接从配置类中返回MappedStatement实例,否则从父类接口中继续查找,如果找不到就返回NULL
3. 如果入参接口路径就是方法所在的类路径,那么直接返回NULL
1 private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName, 2 Class<?> declaringClass, Configuration configuration) { 3 //获取语句id 形式为接口名.方法名 4 String statementId = mapperInterface.getName() + "." + methodName; 5 //判断配置中是否存在该方法id 6 if (configuration.hasStatement(statementId)) { 7 //返回映射语句 8 return configuration.getMappedStatement(statementId); 9 } 10 //如果接口信息就是所在类的话,直接返回NULL 11 else if (mapperInterface.equals(declaringClass)) { 12 return null; 13 } 14 //获取该类下的所有接口信息 15 for (Class<?> superInterface : mapperInterface.getInterfaces()) { 16 //从父接口中查找对应的方法ID,下列语句使用递归的方式进行查找 17 if (declaringClass.isAssignableFrom(superInterface)) { 18 MappedStatement ms = resolveMappedStatement(superInterface, methodName, 19 declaringClass, configuration); 20 if (ms != null) { 21 return ms; 22 } 23 } 24 } 25 return null; 26 } 27 }