mybatis foreach执行多条sql配置_MyBatis源码分析三:Sql执行

一、MyBatis中Sql执行过程

先上一段代码,看直接调用MyBatis Api是如何执行Sql的:

  // 获取配置文件输入流  InputStream inputStream = Resources.getResourceAsStream("META-INF/spring/mybatis-config.xml");  // 通过SqlSessionFactoryBuilder的build()方法创建SqlSessionFactory实例  SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);  //构造数据源  BoneCPDataSource dataSource= new BoneCPDataSource();  dataSource.setDriverClass(driverClass);  dataSource.setJdbcUrl(jdbcUrl);  dataSource.setUsername(userNmae);  dataSource.setPassword(password);  String id = "SqlSessionFactoryBean";  TransactionFactory transactionFactory = new SpringManagedTransactionFactory();  Environment newEnv = new Environment(id, transactionFactory, dataSource);  sqlSessionFactory.getConfiguration().setEnvironment(newEnv);  SqlSession sqlSession = sqlSessionFactory.openSession();  // 获取UserMapper代理对象  UserMapper userMapper = sqlSession.getMapper(UserMapper.class);  // 执行Mapper方法,获取执行结果  List userList = userMapper.listAllUser();

大概过程是先获取到SqlSession实例,然后获取Mapper,再执行Mapper中相应的方法。

在前面文章 MyBatis3使用 介绍了Spring中如何使用MyBatis,这里再总结下:

1、先编写Mapper

@Mapperpublic interface UserMapper {    ListlistAllUser();}

2、再编写Sql语句

<?xml  version="1.0" encoding="UTF-8" ?>/span>        "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.liujh.mapper.UserMapper">    <sql id="userFields">      id,name, phone,create_time    sql>    <select id="listAllUser"  resultType="com.liujh.entity.UserEntity" >        select        <include refid="userFields"/>        from user    select>mapper>

注意上面接口中的方法名要和Xml中id对应上,并且Namespace也要对上。

那Sql到底是怎么执行的,明明第一步只定义了一个接口,Java中接口是不能实例化的,只能通过类来实例的,它是如何和我们在Xml中编写的Sql绑定的呢?

二、接口和Sql绑定过程

只要加了@Mapper的注解,框架就会为这个Bean定义的类型设置为MapperFactoryBean:

7561e09f75a64eded52d99fd703e748a.png

而MapperFactoryBean的getObject为:

 public T getObject() throws Exception {    return getSqlSession().getMapper(this.mapperInterface);  }

其中mapperInterface为定义的接口。

getSqlSession返回SqlSessionTemplate,它的getMapper方法交给Configuration对象了:

 public  T getMapper(Classtype) {    return getConfiguration().getMapper(type, this);  }

Configuration对象的getMapper方法则交给了MapperProxyFactory:

public  T getMapper(Class<T> type, SqlSession sqlSession) {    final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) 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);    }  }

 MapperProxyFactory则返生成一个MapperProxy,再用JDK的动态代理来完成相关操作:

 public T newInstance(SqlSession sqlSession) {    final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);    return newInstance(mapperProxy);  }    protected T newInstance(MapperProxy mapperProxy) {    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);  }

即最后由MapperProxy来执行,我们知道JDK的动态代理要实现InvocationHandler接口,即实现invoke方法:

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    try {      if (Object.class.equals(method.getDeclaringClass())) {        return method.invoke(this, args);      } else if (isDefaultMethod(method)) {        return invokeDefaultMethod(proxy, method, args);      }    } catch (Throwable t) {      throw ExceptionUtil.unwrapThrowable(t);    }    final MapperMethod mapperMethod = cachedMapperMethod(method);    return mapperMethod.execute(sqlSession, args);

cachedMapperMethod生成MapperMethod并且缓存:

 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;  }

所以最终的执行是由MapperMethod来完成的:

public MapperMethod(Class> mapperInterface, Method method, Configuration config) {    this.command = new SqlCommand(config, mapperInterface, method);    this.method = new MethodSignature(config, mapperInterface, method);  }

在初始化SqlCommand的时候会得到对应的sql语句的一些信息,如id和sql的类型:SELECT/UPDATE/INSERT:

public SqlCommand(Configuration configuration, Class> mapperInterface, Method method) {      final String methodName = method.getName();      final Class> declaringClass = method.getDeclaringClass();      //查找对应的sql对应的MapperedStatement对象      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 {        name = ms.getId();        type = ms.getSqlCommandType();        if (type == SqlCommandType.UNKNOWN) {          throw new BindingException("Unknown execution method for: " + name);        }      }    }

其中resolveMappedStatement会在Configuration中查找是否有注册相应的MappedStatement,具体过程参考下一篇文章 MyBatis源码分析二:启动过程。

private MappedStatement resolveMappedStatement(Class> mapperInterface, String methodName,        Class> declaringClass, Configuration configuration) {      String statementId = mapperInterface.getName() + "." + methodName;      if (configuration.hasStatement(statementId)) {        return configuration.getMappedStatement(statementId);      } else if (mapperInterface.equals(declaringClass)) {        return null;      }

最后我们再看下执行的过程:

  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;  }

这里以SELECT命令并且返回一个集合为例,执行的堆栈如下:

65973db4394dc5386a3623957bb7581f.png

在执行过程中会通过方法和类名从Configuration对象中得到相应的MappedStatement,然后交由相应的Executor执行 

 @Override  public  List selectList(String statement, Object parameter, RowBounds rowBounds) {    try {      MappedStatement ms = configuration.getMappedStatement(statement);      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);    } catch (Exception e) {      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);    } finally {      ErrorContext.instance().reset();    }  }

因代码比较多就不一一详列了,大概的交互如下:

MapperMethod——》SqlSession(SqlSessionTemplate)——》Executor(CachingExecutor)——》SimpleExecutor

接下来是调用JDBC相关API完成操作。

@Override  public Listquery(Statement statement, ResultHandler resultHandler) throws SQLException {    PreparedStatement ps = (PreparedStatement) statement;    ps.execute();    return resultSetHandler. handleResultSets(ps);  }

三、总结

MyBatis使用动态代理技术将接口和Xml中的Sql语句关联起来,具体来说为每一个接口创建一个MapperProxy;

接口中每一个方法对应一个MapperMethod,执行接口的方法就是执行MapperMethod的execute方法,在执行过程中通过查找接口名称对应的MapperedStatement对象(代表一条Sql语句)来执行相应的Sql,从而达到接口和Sql关联 。


MyBatis源码分析二:启动过程

MyBatis源码分析一:核心组件

MyBatis3使用

RabbitMQ Fedration插件

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值