Mybatis学习(四) --- binding模块

    在使用mybatis的时候需要使用对应的接口,不像需要写具体的实现,就可以实现数据库的操作,Mybatis可以根据接口和对应的方法找到xml文件中sql语句,进行绑定,以及参数的填充。接下来我们看下binding模块的代码。下面是核心组件的关系

  • MapperProxy:是Mapper接口的代理对象,让我们在使用mybatis的时候不需要实现接口
  • MapperProxyFactory:MapperProxy的工厂类

MapperProxy

    实现了InvocationHandler接口,通过代理的方式为Mapper接口实现代理对象。

//代理对象执行方法,代理以后,所有Mapper的方法调用的时候,都对调用这个方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    //通过是Object的通用方法,无须执行
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else if (method.isDefault()) {
      //如果是默认的方法则执行默认方法
      if (privateLookupInMethod == null) {
        return invokeDefaultMethodJava8(proxy, method, args);
      } else {
        return invokeDefaultMethodJava9(proxy, method, args);
      }
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
  //从缓存中获取一个MapperMethod
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  //执行对应的方法
  return mapperMethod.execute(sqlSession, args);
}

    主要调用的是MapperMethod的方法

MapperProxyFactory

    MapperProxyFactory就是用来创建MapperProxxy类的

//代理类的创建
protected T newInstance(MapperProxy<T> mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

MapperRegistry

    在mybatis初始化的时候,加载配置文件及mapper接口信息,注册在MapperRegistry中,需要执行sql语句的时候,在从注册中心获取mapper接口的代理对象。

    注册mapper接口

public <T> void addMapper(Class<T> type) {
  //判断是不是接口
  if (type.isInterface()) {
    //避免重复注册
    if (hasMapper(type)) {
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    boolean loadCompleted = false;
    try {
      //为每一个mapper接口创建一个代理工厂MapperProxyFactory
      knownMappers.put(type, new MapperProxyFactory<>(type));
      //xml的解析和注解的处理
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

    获取接口代理

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  //获取代理工厂
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  }
  try {
    //通过工厂创建代理MapperProxy
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}

MapperMethod

    MapperProxy代理中是通过调用MapperMethod执行的,其主要完成方法参数的转换和执行方法对应的SQL。可以看做是Mapper接口方法与SQL语句之间的桥梁,只有两个属性SqlCommand和MethodSignature。核心的方法是execute,执行对应的sql语句。

public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  switch (command.getType()) {
    case INSERT: {
      //将用户传递参数的值与指定的参数绑定起来
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
      break;
    }
    case UPDATE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
      break;
    }
    case DELETE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
      break;
    }
    case SELECT:
      if (method.returnsVoid() && method.hasResultHandler()) {
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        result = executeForMap(sqlSession, args);
      } else if (method.returnsCursor()) {
        result = executeForCursor(sqlSession, args);
      } else {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
        if (method.returnsOptional()
            && (result == null || !method.getReturnType().equals(result.getClass()))) {
          result = Optional.ofNullable(result);
        }
      }
      break;
    case FLUSH:
      result = sqlSession.flushStatements();
      break;
    default:
      throw new BindingException("Unknown execution method for: " + command.getName());
  }
  if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
    throw new BindingException("Mapper method '" + command.getName()
        + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  }
  return result;
}

SqlCommand

    记录了 SQL 语句的名称和类型

public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
  final String methodName = method.getName();
  final Class<?> declaringClass = method.getDeclaringClass();
  //SQL语句的名称有接口名+方法构成的,MappedStatement封装了SQL语句的基本信息
  MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
      configuration);
  if (ms == null) {
    if (method.getAnnotation(Flush.class) != null) {
      name = null;
      type = SqlCommandType.FLUSH;
    } else {
      throw new BindingException("Invalid bound statement (not found): "
          + mapperInterface.getName() + "." + methodName);
    }
  } else {
    //获取 SQL 名称和类型
    name = ms.getId();
    type = ms.getSqlCommandType();
    if (type == SqlCommandType.UNKNOWN) {
      throw new BindingException("Unknown execution method for: " + name);
    }
  }
}

MethodSignature

    接口中方法对应的信息,内部使用了ParamNameResolver解析参数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值