博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mybatis源码分析: MapperMethod功能讲解(1)
阅读量:4449 次
发布时间:2019-06-07

本文共 5980 字,大约阅读时间需要 19 分钟。

Mybatis源码分析: MapperMethod功能讲解(1)

      MapperMethod主要的功能是执行SQL的相关操作,在初始化时会实例化两个组件Sql命令(SqlCommand)和方法签名(MethodSignature)这两个组件会在后续进行详解,同时MapperMethod必须提供Mapper的接口路径,待执行的方法,配置Configuration作为入参。通过获取SqlCommand中的执行类型,MapperMethod才知道该Mapper接口将要执行什么样的操作。构造方法如下所示:

1 public MapperMethod(Class
mapperInterface, Method method, Configuration config) {2 //初始化SqlCommand3 this.command = new SqlCommand(config, mapperInterface, method);4 //初始化方法签名5 this.method = new MethodSignature(config, mapperInterface, method);6 }

 

Mybatis中定义了5种操作:SELECT,INSERT,UPDATE,DELETE,FLUSH。其中SELECT中包含了5中查询,结果类型处理器,返回NULL值,多条记录查询,单记录查询,游标查询,和map查询。相关操作和方法如下表对应。从表中可以看到,Sql执行方式本质上时通过sqlSession调用的,在SELECT操作中,虽然调用了MapperMethod中的方法,但本质上仍是通过Sqlsession下的select(),selectList(),selectCursor(),selectMap()等方法实现的。

操作  方法名
INSERT  sqlSession.insert()
UPDATE  sqlSession.update()
DELETE  sqlSession.delete()
FLUSH  sqlSession.flushStatements()
SELECT  executeWithResultHandler(sqlSession, args)
executeForMany(sqlSession, args)
method.returnsMap()
executeForCursor()
sqlSession.selectOne(command.getName(), param)

 

1 public Object execute(SqlSession sqlSession, Object[] args) { 2 Object result; 3 //判断命令类型 4 switch (command.getType()) { 5 case INSERT: { 6 //将参数转为Sql参数 7 Object param = method.convertArgsToSqlCommandParam(args); 8 //获取插入结果 9 result = rowCountResult(sqlSession.insert(command.getName(), param));10 break;11 }12 case UPDATE: {13 Object param = method.convertArgsToSqlCommandParam(args);14 result = rowCountResult(sqlSession.update(command.getName(), param));15 break;16 }17 case DELETE: {18 Object param = method.convertArgsToSqlCommandParam(args);19 result = rowCountResult(sqlSession.delete(command.getName(), param));20 break;21 }22 case SELECT:23 if (method.returnsVoid() && method.hasResultHandler()) {24 //带有结果处理器的25 executeWithResultHandler(sqlSession, args);26 result = null;27 } else if (method.returnsMany()) {28 //多条查询29 result = executeForMany(sqlSession, args);30 } else if (method.returnsMap()) {31 //返回map32 result = executeForMap(sqlSession, args);33 } else if (method.returnsCursor()) {34 //游标查询35 result = executeForCursor(sqlSession, args);36 } else {37 //单条查询38 Object param = method.convertArgsToSqlCommandParam(args);39 result = sqlSession.selectOne(command.getName(), param);40 }41 break;42 case FLUSH:43 //清除操作44 result = sqlSession.flushStatements();45 break;46 default:47 throw new BindingException("Unknown execution method for: " + command.getName());48 }49 if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {50 throw new BindingException("Mapper method '" + command.getName() 51 + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");52 }53 return result;54 }

 

参数解析器

     在上述代码中经常能看见这样一句代码 method.convertArgsToSqlCommandParam(args);,该方法主要的功能是获取@Param注解上的参数值,而实现方式便是通过参数解析器(ParamNameResolver),convertArgsToSqlCommandParam(args)方法实际上调用的是ParamNameResolver下的getNamedParams(args)方法。在分析该方法之前,先看看构造器都做了些什么操作。

1 public ParamNameResolver(Configuration config, Method method) { 2 final Class
[] paramTypes = method.getParameterTypes(); //获取所有参数类型 3 final Annotation[][] paramAnnotations = method.getParameterAnnotations();//获取方法中的所有注解 4 final SortedMap
map = new TreeMap
(); 5 //判断注解数组长度 6 int paramCount = paramAnnotations.length; 7 // get names from @Param annotations 8 for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) { 9 if (isSpecialParameter(paramTypes[paramIndex])) {10 // skip special parameters11 continue;12 }13 String name = null;14 for (Annotation annotation : paramAnnotations[paramIndex]) {15 //判断是否是Param标签的子类,也就是说@param中是否存在value值16 if (annotation instanceof Param) {17 hasParamAnnotation = true;18 //获取标签中的value值19 name = ((Param) annotation).value();20 break;21 }22 }23 if (name == null) {24 // @Param was not specified.25 //是否使用了真实值,也就是说没有设置value值26 if (config.isUseActualParamName()) {27 //获取方法中参数的名字28 name = getActualParamName(method, paramIndex);29 }30 if (name == null) {31 // use the parameter index as the name ("0", "1", ...)32 // gcode issue #7133 name = String.valueOf(map.size());34 }35 }36 map.put(paramIndex, name);37 }38 //设置所有参数名为不可修改的集合39 names = Collections.unmodifiableSortedMap(map);40 }

 

      构造器同样需要两个入参,配置类和方法名,ParamNameResolver下包含了两个属性字段GENERIC_NAME_PREFIX属性前缀和参数集合names,构造器会将Method中所有参数级别的注解全部解析出来方法有序参数集中,names中存储形式为<参数下标,参数名>,如果在注解上设置了参数名,则会直接获取注解的value值,如果没有使用@Param注解,则使用真实的参数名,注意,真实参数名其实是arg0,arg1....的形式展现的,在判断真实参数名时,Mybatis会检查JDK版本是否包含java.lang.reflect.Parameter类,不存在该类的化会抛出ClassNotFoundException异常。 完成初始化后,就可以调用getNamedParams(args)方法了,如下代码所示,该方法使用了类中的names属性,从有序集合中取出所有的<参数索引,参数名>键值对>,随后填充到另外一个集合中,以<参数名,参数下标索引,>形式呈现,同时会保留一份<param+下标索引,参数下标>的键值对

1 public Object getNamedParams(Object[] args) { 2 //判断参数个数 3 final int paramCount = names.size(); 4 if (args == null || paramCount == 0) { 5 return null; 6 } 7 //返回首个参数  8 else if (!hasParamAnnotation && paramCount == 1) { 9 return args[names.firstKey()];10 } else {11 final Map
param = new ParamMap
();12 int i = 0;13 for (Map.Entry
entry : names.entrySet()) {14 //设置参数的值和键名15 param.put(entry.getValue(), args[entry.getKey()]);16 // add generic param names (param1, param2, ...)17 //增加参数名18 final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);19 // ensure not to overwrite parameter named with @Param20 if (!names.containsValue(genericParamName)) {21 param.put(genericParamName, args[entry.getKey()]);22 }23 i++;24 }25 return param;26 }27 }

 

需掌握的知识点

  • Method类的使用,常用的方法
方法名  作用
getName()  获取方法名
isVarArgs()  如果该方法声明为采用可变数量的参数,则返回true; 否则返回false
getModifiers()  获取权限修饰符
getReturnType()  获取返回类型
getExceptionTypes()  获取所有抛出的异常类型
getGenericReturnType () 返回Type类型
getParameterTypes()  获取所有参数的类型
getParameterCount()  获取所有参数的个数
getAnnotations()  获取方法级别的注解

 

  • 集合的使用

Collections.unmodifiableSortedMap(map);可修饰为一个不可变的集合

如何实现自定义注解,并对注解进行解析

见实现自定义注解,并对注解进行解析 文章

转载于:https://www.cnblogs.com/zhengzuozhanglina/p/11212200.html

你可能感兴趣的文章
redis中重启和停止服务
查看>>
Django--form表单组件
查看>>
Django--中间件
查看>>
Elasticsearch快速开始
查看>>
使用scrapy框架来进行抓取的原因
查看>>
scrapy中的ImagePipeline下载图片到本地、并提取本地的保存地址
查看>>
自定义配置文件的使用
查看>>
js-20170609-运算符
查看>>
ALV弹出窗口&nbsp;&nbsp;&nbsp;REU…
查看>>
算法笔记_065:分治法求逆序对(Java)
查看>>
CSS中关于字体大小的定义 em px rem pt %
查看>>
MSP430FLASH小结
查看>>
STM32 ADC转换时间
查看>>
kylin cube 构建过程
查看>>
结合实际业务场景聊一聊MVP模式的应用
查看>>
scrapy实例:爬取中国天气网
查看>>
经济学效应
查看>>
深圳常见问题
查看>>
入侵指定网站的一些方法(思路篇)
查看>>
【帅刺猬】用鼠标Wheel中键控制对象的缩放
查看>>