ibatis一次sql过程

本文详细剖析了Ibatis框架中SQL执行的具体流程,包括SqlSessionFactory的构建、SqlSession的获取、执行器的选择及参数绑定等关键步骤,并深入探讨了查询执行过程中涉及的缓存处理与数据库交互细节。
摘要由CSDN通过智能技术生成

为了理清楚

ibatis一次sql过程中发生了什么,本文将对ibatis一次sql过程做简要的分析。
String resource = "mybatis.cfg.xml";  
 Reader reader = Resources.getResourceAsReader(resource);  
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);     
SqlSession session = ssf.openSession();          
try {  
        UserInfo user = (UserInfo) session.selectOne("User.selectUser", "1");  
         System.out.println(user);  
 } catch (Exception e) {  
       e.printStackTrace();  
} finally {  
      session.close();  
 }  

首先分析下SqlSession session = ssf.openSession(); 这条语句里面发生了什么,其实这条语句就是通过SqlSessionFactory工厂获取一次查询的Session。其中上一句SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader); 这句代码通过xml文件获取到SqlSessionFactory工厂,深入进去发现最终获取色SqlSessionFactory最终调用的是下面这个方法
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
拿到SqlSessionFactory后我们再去看看SqlSession session = ssf.openSession();这句代码究竟干了什么?深入代码发现ssf.openSession()最终调用了一下方法。

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
 try {
 final Environment environment = configuration.getEnvironment();
 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
 final Executor executor = configuration.newExecutor(tx, execType, autoCommit);
 return new DefaultSqlSession(configuration, executor);
 } catch (Exception e) {
    closeTransaction(tx); // may have fetched a connection so lets call close()
 throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
 } finally {
    ErrorContext.instance().reset();
 }
}

可以从方法中看出,首先获取加载配置文件的环境信息,为后面获取环境信息中配置的数据源做准备,从环境配置中获取事务工厂。以JDBD事务为例。在JdbcTransaction中封装了数据库连接操作,因此此处并没有
创建数据库连接。接着根据获取到的事务和执行类型以及是否提交获取Executor实例。其中newExecutor(tx, execType, autoCommit)代码如下所示;

public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {
  executorType = executorType == null ? defaultExecutorType : executorType;
 executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
 Executor executor;
 if (ExecutorType.BATCH == executorType) {
    executor = new BatchExecutor(this, transaction);
 } else if (ExecutorType.REUSE == executorType) {
    executor = new ReuseExecutor(this, transaction);
 } else {
    executor = new SimpleExecutor(this, transaction);
 }
  if (cacheEnabled) {
    executor = new CachingExecutor(executor, autoCommit);
 }
  executor = (Executor) interceptorChain.pluginAll(executor);
 return executor;
}

这里主要做了三件事
(1)判断执行器类型,如果配置文件中没有配置执行器类型,则采用默认执行类型ExecutorType.SIMPLE。
(2)根据执行器类型返回不同类型的执行器(执行器有三种,分别是 BatchExecutor、SimpleExecutor和CachingExecutor,后面我们再详细看看)。
(3)跟执行器绑定拦截器插件。
最后将返回DefaultSqlSession(configuration, executor)。 DefaultSqlSession实现了SqlSession接口,而SqlSession接口装封装了大量跟数据操作有关的方法,可以断言,所有的操作将在
DefaultSqlSession中发生,例子中UserInfo user = (UserInfo) session.selectOne(“User.selectUser”, “1”); 为例进去看看,这个代码最终会执行以下几个方法。

public <T> T selectOne(String statement, Object parameter) {
  // Popular vote was to return null on 0 results and throw exception on too many.
 List<T> list = this.<T>selectList(statement, parameter);
 if (list.size() == 1) {
    return list.get(0);
 } else if (list.size() > 1) {
    throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
 } else {
    return null;
 }
}
public <E> List<E> selectList(String statement, Object parameter) {
  return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

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

其中MappedStatement ms = configuration.getMappedStatement(statement);这句话是根据“User.selectUser”找到在Mapper配置文件的信息。
接着根据配置文件信息也就是MappedStatement 去执行executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
进入到executor.query()方法中可以看出:首先调用BaseExecutor中这个方法;

 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
   BoundSql boundSql = ms.getBoundSql(parameter);
 CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
 return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

这个方法中进行了sql参数绑定。并且做了相应的缓存处理。然后执行了下面这个重载方法

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
 if (closed) throw new ExecutorException("Executor was closed.");
 if (queryStack == 0 && ms.isFlushCacheRequired()) {
    clearLocalCache();
 }
  List<E> list;
 try {
    queryStack++;
 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
 if (list != null) {
      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
 } else {
      list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }
  } finally {
    queryStack--;
 }
  if (queryStack == 0) {
    for (DeferredLoad deferredLoad : deferredLoads) {
      deferredLoad.load();
 }
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
      clearLocalCache(); // issue #482
 }
  }
  return list;
}

这个方法中我着重看下这个语句list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);意思是从数据库中获取查询,具体如下所示:

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  List<E> list;
 localCache.putObject(key, EXECUTION_PLACEHOLDER);
 try {
    list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
 } finally {
    localCache.removeObject(key);
 }
  localCache.putObject(key, list);
 if (ms.getStatementType() == StatementType.CALLABLE) {
    localOutputParameterCache.putObject(key, parameter);
 }
  return list;
}

暂时还没有看到时如何查询,进一步深入到doQuery(ms, parameter, rowBounds, resultHandler, boundSql);这个方法中是个抽象方法在子类中有实现,我们挑选其中一个实现看了下具体如下所示:

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
 Configuration configuration = ms.getConfiguration();
 StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
 Statement stmt = prepareStatement(handler, ms.getStatementLog());
 return handler.<E>query(stmt, resultHandler);
}
其中prepareStatement(handler, ms.getStatementLog());方法如下所示,可以看出真正创建数据库链接在这里。
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
 BoundSql boundSql = handler.getBoundSql();
 String sql = boundSql.getSql();
 if (hasStatementFor(sql)) {
    stmt = getStatement(sql);
 } else {
    Connection connection = getConnection(statementLog);
 stmt = handler.prepare(connection);
 putStatement(sql, stmt);
 }
  handler.parameterize(stmt);
 return stmt;
}

后面基本就是JDBC操作数据库的内容了。到此一次SQL语句到此完毕。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值