Mybatis源码(七):SQL执行流程 您所在的位置:网站首页 mybatis中的if判断 Mybatis源码(七):SQL执行流程

Mybatis源码(七):SQL执行流程

2023-05-28 12:10| 来源: 网络整理| 查看: 265

1 // Mapper接口方法 2 public class MapperMethod { 3 4 // 记录了SQL语句的名称和类型 5 private final SqlCommand command; 6 // mapper接口中对应方法的相关信息 7 private final MethodSignature method; 8 9 public MapperMethod(Class mapperInterface, Method method, Configuration config) { 10 this.command = new SqlCommand(config, mapperInterface, method); // 获取唯一标识(类限定名+方法名称)、获取sql命令类型 11 this.method = new MethodSignature(config, mapperInterface, method); 12 } 13 // ... 14 } 1、SqlCommand

  SqlCommand为MapperMethod的静态内部类,主要包含了目标执行方法的全限定名、目标执行方法对应sql的类型(增/删/改/查),持有两个关键属性,name和type。name为 全限定接口 + 方法名;type为 当前SQL语句类型,用于辨别增删改查语句。

// 方法的全限定名 -> 全限定类名 + 方法名 private final String name; // SQL语句类型 insert|update|delete|select private final SqlCommandType type;

  下面来看看SqlCommand的构造函数,这两个属性是在哪获取的,构造函数详情:

1 public SqlCommand(Configuration configuration, Class mapperInterface, Method method) { 2 // 获取执行方法名称 3 final String methodName = method.getName(); 4 // 获取接口的Class 5 final Class declaringClass = method.getDeclaringClass(); 6 // 获取MappedStatement对象,包含xml配置文件、全限定名+方法名、返回结果类型、sqlCommand 7 MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass, 8 configuration); 9 // ... 10 // 初始化name和type 11 name = ms.getId(); 12 // sql命令类型, SqlCommandType 13 type = ms.getSqlCommandType(); 14 if (type == SqlCommandType.UNKNOWN) { 15 throw new BindingException("Unknown execution method for: " + name); 16 } 17 // ... 18 }

  解析接口、方法名称获取MappedStatement,MapperMethod#resolveMappedStatement() 核心伪代码:

1 // 解析接口信息获取MappedStatement对象 2 private MappedStatement resolveMappedStatement(Class mapperInterface, String methodName, 3 Class declaringClass, Configuration configuration) { 4 // sql标识id, SQL语句的名称是由Mapper接口的全限定名 + 目标方法名称 5 String statementId = mapperInterface.getName() + "." + methodName; 6 // 全局配置类是否包含该sql标识di 7 if (configuration.hasStatement(statementId)) { 8 // 从configuration.mappedStatements集合中查找对应的MappedStatement对象,MappedStatement对象中封装了SQL语句相关的信息,在mybatis初始化时创建 9 return configuration.getMappedStatement(statementId); 10 } 11 // ... 12 }

  组装sql标识id,sql标识id为 Mapper接口的全限定名 + 目标方法名称,判断此sql标识id在全局配置对象configuration中mappedStatements属性中是否存在,存在则从configuration中获取在解析阶段创建好的mappedStatement对象。sqlCommand中的name对应mappedStatement对象的id,sqlCommand中的type对应mappedStatement中的getSqlCommandType。

  有关sql标识id与mappedStatement的映射关系添加至mappedStatements属性,详见:Mybatis 源码(四):Mapper的解析工作。

  sqlCommand中包含了目标执行方法的全限定名、目标执行方法对应的SQL类型。详情如下:

 

2、MethodSignature

  MethodSignature为MapperMethod的静态内部类,主要包含了方法返回值信息、参数名称解析器。构造函数详情如下:

1 // 返回值类型是否为Collection类型或者数组类型 2 private final boolean returnsMany; 3 // 返回值类型是否为map类型 4 private final boolean returnsMap; 5 // 返回值类型是否为void 6 private final boolean returnsVoid; 7 // 返回值是否为Cursor类型 8 private final boolean returnsCursor; 9 private final boolean returnsOptional; 10 // 返回值类型 11 private final Class returnType; 12 // 如果返回值类型是map,则该字段记录了作为key的列名 13 private final String mapKey; 14 // 用来标记该方法参数列表中ResultHandler类型参数的位置 15 private final Integer resultHandlerIndex; 16 // 用来标记该方法参数列表中RowBounds类型参数的位置 17 private final Integer rowBoundsIndex; 18 // 该方法对应的ParamNameResolver对象 19 private final ParamNameResolver paramNameResolver; 20 21 // 构造函数 22 public MethodSignature(Configuration configuration, Class mapperInterface, Method method) { 23 // 解析方法的返回值类型 24 Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface); 25 if (resolvedReturnType instanceof Class) { 26 this.returnType = (Class) resolvedReturnType; 27 } else if (resolvedReturnType instanceof ParameterizedType) { 28 this.returnType = (Class) ((ParameterizedType) resolvedReturnType).getRawType(); 29 } else { 30 this.returnType = method.getReturnType(); 31 } 32 // 初始化returnsVoid、returnsMany、returnsCursor、returnsOptional等参数 33 this.returnsVoid = void.class.equals(this.returnType); 34 this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray(); 35 this.returnsCursor = Cursor.class.equals(this.returnType); 36 this.returnsOptional = Optional.class.equals(this.returnType); 37 // 若MethodSignature对应方法的返回值是Map且制定了@MapKey注解,则使用getMapKey方法处理 38 this.mapKey = getMapKey(method); 39 this.returnsMap = this.mapKey != null; 40 // 初始化rowBoundsIndex、resultHandlerIndex字段 41 this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class); 42 this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class); 43 // 创建ParamNameResolver对象 44 this.paramNameResolver = new ParamNameResolver(configuration, method); 45 }

  MethodSignature包含了方法返回值信息:返回值类型、返回值是否为void标识、返回结果是否为集合标识、返回结果是否为游标标识等;

  参数解析器ParamNameResolver对象的初始化。 3、总结

  sqlCommand主要用于锁定目标执行方法及目标执行方法要做的数据操作(增/删/改/查);MethodSignature主要用于对方法的参数、返回值的信息处理。用创建好的sqlCommand、MethodSignature创建MapperMethod对象。

2、MapperMethod#execute()

  根据不同的SQL类型执行不同的sqlSession方法,MapperMethod#execute() 核心代码:

1 public Object execute(SqlSession sqlSession, Object[] args) { 2 Object result; 3 // 根据SQL语句的类型调用SqlSession对应的方法 4 switch (command.getType()) { 5 // 新增 6 case INSERT: { 7 // 使用ParamNameResolver处理args数组,将用户传入的实参与指定参数名称关联起来 8 Object param = method.convertArgsToSqlCommandParam(args); 9 // 调用sqlSession.insert方法,rowCountResult方法会根据method字段中记录的方法的返回值类型对结果进行转换 10 result = rowCountResult(sqlSession.insert(command.getName(), param)); 11 break; 12 } 13 // 更新 14 case UPDATE: { 15 Object param = method.convertArgsToSqlCommandParam(args); 16 result = rowCountResult(sqlSession.update(command.getName(), param)); 17 break; 18 } 19 // 删除 20 case DELETE: { 21 Object param = method.convertArgsToSqlCommandParam(args); 22 result = rowCountResult(sqlSession.delete(command.getName(), param)); 23 break; 24 } 25 // 查询 26 case SELECT: 27 // 处理返回值为void是ResultSet通过ResultHandler处理的方法 28 if (method.returnsVoid() && method.hasResultHandler()) { 29 // 如果有结果处理器 30 executeWithResultHandler(sqlSession, args); 31 result = null; 32 // 如果结果有多条记录 33 } else if (method.returnsMany()) { 34 result = executeForMany(sqlSession, args); 35 // 处理返回值为map的方法 36 } else if (method.returnsMap()) { 37 result = executeForMap(sqlSession, args); 38 // 处理返回值为cursor的方法 39 } else if (method.returnsCursor()) { 40 result = executeForCursor(sqlSession, args); 41 } else { 42 // 处理返回值为单一对象的方法 43 Object param = method.convertArgsToSqlCommandParam(args); // 获取方法参数值 44 result = sqlSession.selectOne(command.getName(), param); // 根据 类全限定名 + 方法名、方法参数,执行sql 45 if (method.returnsOptional() && 46 (result == null || !method.getReturnType().equals(result.getClass()))) { 47 result = Optional.ofNullable(result); 48 } 49 } 50 break; 51 case FLUSH: 52 result = sqlSession.flushStatements(); 53 break; 54 default: 55 throw new BindingException("Unknown execution method for: " + command.getName()); 56 } 57 if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { 58 throw new BindingException("Mapper method '" + command.getName() 59 + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); 60 } 61 return result; 62 }

  根据不同的sqlCommandType和返回类型执行不同sqlSession方法。以查询为例,进行SQL执行流程分析:

UserMapper mapper = sqlSession.getMapper(UserMapper.class); // 查询流程 User user02 = mapper.selectUserById("101"); 2.1、方法参数解析成SQL参数

  方法参数args数组转换成SQL语句对应的参数列表,MapperMethod#convertArgsToSqlCommandParam() 核心方法:

public Object convertArgsToSqlCommandParam(Object[] args) { return paramNameResolver.getNamedParams(args); }

  通过参数名称解析器paramNameResolver解析方法参数,paramNameResolver对象在创建MethodSignature时被初始化赋值的。

2.2、执行SQL 1、查询

  后续查询流程,详见Mybatis源码(八):查询执行流程。

2、新增|更新|删除

  后续增删改流程,详见:Mybtais源码(九):增删改执行流程。

3、核心流程图

  以查询为例,核心流程图如下:

 

 

 

 



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有