Mybatis 常见问题深入解析--2

问题. 通常一个mapper.xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?

一、SqlSource类
1、Mybatis会把每个sql语句封装成一个SqlSource对象,根据xml文件或者注解的配置,创建sql 语句对应的SqlSource对象。

2、MappedStatement类
每一个sql 语句就对应一个MappedStatement对象,这里面有两个属性很重要。
id :全限定类名+方法名组成的ID。
sqlSource:当前sql语句对应的SqlSource对象

3、MapperProxyFactory 类用来创建接口的代理对象
每个DAO接口类对应一个MapperProxyFactory ,MapperProxyFactory 可以通过jdk 的动态代理生成接口的代理对象。

4、MapperRegistry 类,用来注册接口类对应的MapperProxyFactory
MapperRegistry 类里面有一个名为knownMappers的HashMap, 这个map 用来存放接口类的MapperProxyFactory 。键为:接口类对象,值为:接口类对象对应的MapperProxyFactory 。

5、MapperProxy 类
这个类实现了InvocationHandler 接口,在生成接口的动态代理对象时,这类就是代理对象里面的
InvocationHandler。

6、Configuration类
mybatis 里面的全局配置文件对象,Configuration类里面有一个mappedStatements属性,是用来存放所有的MappedStatement对象,mappedStatements 是一个StrictMap。这个Map的键为:接口的全限定名 + 方法名,值为:MappedStatement,即sql 语句对应的MappedStatement对象。所以可以根据全限定接口名+ 方法名 确定一个唯一的MappedStatement对象。

总结:mybatis 里面 sql 语句执行流程及xml 文件 或者SQL注解 和DAO 文件的映射关系
1、生成SqlSessionFactory对象
默认实现是DefaultSqlSessionFactory

2、获取SqlSession对象
通过调用DefaultSqlSessionFactory的openSession()方法来获取SqlSession对象,
默认返回的是一个DefaultSqlSession实例对象。

3、获取MapperProxy对象

	UserDao userMapper = sqlSession.getMapper(UserDao.class);  
	// sqlSession.getMapper 方法
	  public <T> T getMapper(Class<T> type) {
	    return configuration.<T>getMapper(type, this);
	  }
	  
	 // Configuration类的getMapper方法
	 public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
	    return mapperRegistry.getMapper(type, sqlSession);
	  }
  
  // MapperRegistry类的getMapper
  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 {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
通过上述代码可以看出sqlSession.getMapper() 最终会调用MapperRegistry类的getMapper()方法,拿到Mapper 对象对应的mapperProxyFactory, 然后通过mapperProxyFactory.newInstance() 创建Mapper 的代理对象。生成的userMapper 实际上是一个动态代理对象。

User user = userMapper.findUserById(10);  // 当代理对象调用方法时,由动态代理的原理可知,会调用到handler 里面的invoke 方法。
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
   // mapperMethod.execute() 方法
	 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);
        }
        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;
  }

// sqlSession.update(command.getName(), param)
	public int update(String statement, Object parameter) {
    try {
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

//statement 为权限定类名加方法名,通过statement拿到sql 语句对应的MappedStatement。这样DAO 文件和 xml文件或者sql 注解通过全限定类名+方法名对应起来。

总结:DAO 文件的工作原理其实是通过jdk底层的动态代理技术实现的,由于权限定类名+ 方法名唯一确定一个 sql 语句的MappedStatement。所有DAO文件的方法不能重载,也就是说,方法名不能相同。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值