zoukankan      html  css  js  c++  java
  • mybatis源码分析(4)----org.apache.ibatis.binding包

    1.  我们通过接口操作数据库时,发现相关的操作都是在org.apache.ibatis.binding下

    • 从sqSessin 获取getMapper()
    SqlSession session = sqlSessionFactory.openSession();
    CxCaseMapper caseMapper = session.getMapper(CxCaseMapper.class);
    CxCaseTable tablelm = (CxCaseTable) caseMapper.selectById(id);

    • binging 包结构

      可以发现binding 包输入mybatis 下面,是mybatis 的核心文件。这个包中包含有四个类:

    1. BindingException 该包中的异常类
    2. MapperMethod 代理类中真正执行数据库操作的类
    3. MapperProxy 实现了InvocationHandler接口的动态代理类
    4. MapperProxyFactory 为代理工厂
    5. MapperRegistry mybatis中mapper的注册类及对外提供生成代理类接口的类

       在sqlSession初始化的时候,将所有的mapper 注册到了MapperRegistry 中,以Map<Class<?>, MapperProxyFactory<?>>的形式存储。

    2. 通过sqlSession接口的代理对象

     

    • SqSession中持有configuration,configuration中持有mapperRegister、mapperRegister中持有的mapper代理对象工程Map<Class<?>, MapperProxyFactory<?>>。
    • 当mapperRegister通过getMapper方法获取,代理对象的时候,流程已经进入到binding 包中。
      public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        //以mapper的class对象为key值,获取当面mapper的代理对象工厂
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        if (mapperProxyFactory == null) {
          //如果没有获取到代理对象工厂 抛出BindingException 异常信息
          throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        }
        try {
          //用获取到的代理对象工厂,生产目标对象以及目标代理对象
          return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
          throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
      }
    • 代理对象工厂生产目标对象已经目标代理对象
      @SuppressWarnings("unchecked")
      protected T newInstance(MapperProxy<T> mapperProxy) {
        // 3.mapperProxy实现了InvocationHandler接口,主要为具体接口的代理 
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
      }
    
      public T newInstance(SqlSession sqlSession) {
        // 1. 产生目标对象MapperProxy
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        // 2. 根据目标对象,产生代理对象
        return newInstance(mapperProxy);
      }

     3.mapperProxy

    •  这个类继承了InvocationHandler接口,我们主要看这个类中的两个方法。一是生成具体代理类的函数newMapperProxy,另一个就是实现InvocationHandler接口的invoke。我们先看invoke方法。
    public class MapperProxy<T> implements InvocationHandler, Serializable {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //并不是任何一个方法都需要执行调用代理对象进行执行,如果这个方法是Object中通用的方法(toString、hashCode等)无需执行
        if (Object.class.equals(method.getDeclaringClass())) {
          try {
            return method.invoke(this, args);
          } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
          }
        }
        //生成MapperMethod对象,从cache中获取
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        //执行MapperMethod对象的execute方法,目标方法的执行
        return mapperMethod.execute(sqlSession, args);
      }
    
      private MapperMethod cachedMapperMethod(Method method) {
        MapperMethod mapperMethod = methodCache.get(method);
        if (mapperMethod == null) {
          mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
          methodCache.put(method, mapperMethod);
        }
        return mapperMethod;
      }
    
    }

    4. mapperMethod

    • 在初始化后就是向外提供的函数了,这个类向外提供服务主要是通过如下的函数进行:
    public class MapperMethod {
      public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        //根据初始化时确定的命令类型,选择对应的操作
        if (SqlCommandType.INSERT == command.getType()) {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = rowCountResult(sqlSession.insert(command.getName(), param));
        } else if (SqlCommandType.UPDATE == command.getType()) {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = rowCountResult(sqlSession.update(command.getName(), param));
        } else if (SqlCommandType.DELETE == command.getType()) {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = rowCountResult(sqlSession.delete(command.getName(), param));
        } else if (SqlCommandType.SELECT == command.getType()) {
          //相比较而言,查询稍微有些复杂,不同的返回结果类型有不同的处理方法
          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 {
            Object param = method.convertArgsToSqlCommandParam(args);
        //这个函数整体上还是调用sqlSession的各个函数进行相应的操作,在执行的过程中用到了初始化时的各个参数。
            result = sqlSession.selectOne(command.getName(), param);
          }
        } else if (SqlCommandType.FLUSH == command.getType()) {
            result = sqlSession.flushStatements();
        } else {
          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;
      }
    }

    5 总结:

  • 相关阅读:
    uboot配置和编译过程详解
    gcc 与 g++的区别
    ARM交叉编译器GNUEABI、NONE-EABI、ARM-EABI、GNUEABIHF等的区别
    SPI UART区别是什么
    C#获取时间戳的封装方法函数+使用获取当前时间时间戳
    C#中Timer定时器的使用示例
    Linux查看文件夹大小
    Python对象的创建和赋值
    使用mutt自动发送邮件
    pyTorch安装
  • 原文地址:https://www.cnblogs.com/chihirotan/p/6595976.html
Copyright © 2011-2022 走看看