真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

MyBatis之SqlSession介紹

魯春利的工作筆記,好記性不如爛筆頭

創(chuàng)新互聯(lián)長期為上千客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為平川企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站制作,平川網(wǎng)站改版等技術(shù)服務(wù)。擁有十余年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。


轉(zhuǎn)載自:深入淺出MyBatis-Sqlsession

SqlSession的創(chuàng)建

Sqlsession對應(yīng)著一次數(shù)據(jù)庫會話。由于數(shù)據(jù)庫回話不是永久的,因此Sqlsession的生命周期也不應(yīng)該是永久的,相反,在你每次訪問數(shù)據(jù)庫時(shí)都需要?jiǎng)?chuàng)建它(當(dāng)然并不是說在Sqlsession里只能執(zhí)行一次sql,你可以執(zhí)行多次,當(dāng)一旦關(guān)閉了Sqlsession就需要重新創(chuàng)建它)。創(chuàng)建Sqlsession的地方只有一個(gè),那就是SqlsessionFactory的openSession方法:

package org.apache.ibatis.session;

import java.sql.Connection;

public interface SqlSessionFactory {

  SqlSession openSession();

  SqlSession openSession(boolean autoCommit);
  SqlSession openSession(Connection connection);
  SqlSession openSession(TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType);
  SqlSession openSession(ExecutorType execType, boolean autoCommit);
  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType, Connection connection);

  Configuration getConfiguration();

}

實(shí)際創(chuàng)建SqlSession的地方是openSessionFromDataSource,如下:

package org.apache.ibatis.session.defaults;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;

public class DefaultSqlSessionFactory implements SqlSessionFactory {

  private final Configuration configuration;

  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }

  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  
  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();
    }
  }
}

可以看出,創(chuàng)建sqlsession經(jīng)過了以下幾個(gè)主要步驟:

1)       從配置中獲取Environment;

2)       從Environment中取得DataSource;

3)       從Environment中取得TransactionFactory;

4)       從DataSource里獲取數(shù)據(jù)庫連接對象Connection;

5)       在取得的數(shù)據(jù)庫連接上創(chuàng)建事務(wù)對象Transaction;

6)       創(chuàng)建Executor對象(該對象非常重要,事實(shí)上sqlsession的所有操作都是通過它完成的);

7)       創(chuàng)建sqlsession對象。

Executor的創(chuàng)建

Sqlsession只是個(gè)門面,真正干事的是Executor,Sqlsession對數(shù)據(jù)庫的操作都是通過Executor來完成的。與Sqlsession一樣,Executor也是動(dòng)態(tài)創(chuàng)建的:

final Executor executor = configuration.newExecutor(tx, execType, autoCommit);

Configuration類的實(shí)現(xiàn):

public class Configuration {

  protected Environment environment;
  
  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;
  } 
}

Executor的定義:

package org.apache.ibatis.executor;

import java.sql.SQLException;
import java.util.List;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;

public interface Executor {

  ResultHandler NO_RESULT_HANDLER = null;

  int update(MappedStatement ms, Object parameter) throws SQLException;

   List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;

   List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

  List flushStatements() throws SQLException;

  void commit(boolean required) throws SQLException;

  void rollback(boolean required) throws SQLException;

  CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);

  boolean isCached(MappedStatement ms, CacheKey key);

  void clearLocalCache();

  void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class targetType);

  Transaction getTransaction();

  void close(boolean forceRollback);

  boolean isClosed();

}

MyBatis之SqlSession介紹

可以看出,創(chuàng)建的Executor只是3中基礎(chǔ)類型之一:

      BatchExecutor專門用于執(zhí)行批量sql操作

      ReuseExecutor會重用statement執(zhí)行sql操作

      SimpleExecutor只是簡單執(zhí)行sql沒有什么特別的。

如果開啟cache的話(默認(rèn)是開啟的并且沒有任何理由去關(guān)閉它),就會創(chuàng)建CachingExecutor,它以前面創(chuàng)建的Executor作為唯一參數(shù)。CachingExecutor在查詢數(shù)據(jù)庫前先查找緩存,若沒找到的話調(diào)用delegate(就是構(gòu)造時(shí)傳入的Executor對象)從數(shù)據(jù)庫查詢,并將查詢結(jié)果存入緩存中。

package org.apache.ibatis.executor;

import java.sql.SQLException;
import java.util.List;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.cache.TransactionalCacheManager;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;

public class CachingExecutor implements Executor {

  private Executor delegate;
  private boolean autoCommit; // issue #573. No need to call commit() on autoCommit sessions
  private TransactionalCacheManager tcm = new TransactionalCacheManager();

  private boolean dirty;

  public CachingExecutor(Executor delegate) {
    this(delegate, false);
  }

  public CachingExecutor(Executor delegate, boolean autoCommit) {
    this.delegate = delegate;
    this.autoCommit = autoCommit;
  }

  public Transaction getTransaction() {
    return delegate.getTransaction();
  }

  public void close(boolean forceRollback) {
    try {
      //issue #499. Unresolved session handling
      //issue #573. Autocommit sessions should commit
      if (dirty && !autoCommit) { 
        tcm.rollback();
      } else {
        tcm.commit();
      }
    } finally {
      delegate.close(forceRollback);
    }
  }
  
  // 其他代碼略  
  
}

Executor對象是可以被插件攔截的(攔截器Interceptor,如:

@Intercepts({@Signature(type = Executor.class, method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})

),如果定義了針對Executor類型的插件,最終生成的Executor對象是被各個(gè)插件插入后的代理對象。

Mapper介紹

Mybatis官方手冊建議通過mapper對象訪問mybatis,因?yàn)槭褂胢apper看起來更優(yōu)雅,就像下面這樣:

session = sqlSessionFactory.openSession();  
UserMapper userMapper= session.getMapper(UserMapper.class);  
UserBo user = new UserBo();  
user.setUsername("iMbatis");  
user.setPassword("iMbatis");  
userMapper.insertUser(user);

那么這個(gè)mapper到底是什么呢,它是如何創(chuàng)建的呢,它又是怎么與sqlsession等關(guān)聯(lián)起來的呢?下面為你一一解答。

表面上看mapper是在sqlsession里創(chuàng)建的,但實(shí)際創(chuàng)建它的地方是MapperRegistry:

package org.apache.ibatis.session;

import java.io.Closeable;
import java.sql.Connection;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.executor.BatchResult;

/**
 * The primary Java interface for working with MyBatis.
 * Through this interface you can execute commands, get mappers and manage transactions.
 *
 */
public interface SqlSession extends Closeable {

   T selectOne(String statement);

   T selectOne(String statement, Object parameter);

   List selectList(String statement);

   List selectList(String statement, Object parameter);

   List selectList(String statement, Object parameter, RowBounds rowBounds);

   Map selectMap(String statement, String mapKey);

   Map selectMap(String statement, Object parameter, String mapKey);

   Map selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);

  void select(String statement, Object parameter, ResultHandler handler);

  void select(String statement, ResultHandler handler);

  void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);

  int insert(String statement);

  int insert(String statement, Object parameter);

  int update(String statement);

  int update(String statement, Object parameter);

  int delete(String statement);


  int delete(String statement, Object parameter);

  void commit();

  void commit(boolean force);

  void rollback();

  void rollback(boolean force);

  List flushStatements();

  void close();

  void clearCache();

  Configuration getConfiguration();

   T getMapper(Class type);

  Connection getConnection();
}

SqlSeesion是接口,getMapper肯定是通過起實(shí)現(xiàn)類來完成的,查看其類結(jié)構(gòu):

MyBatis之SqlSession介紹

package org.apache.ibatis.session.defaults;

import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;

public class DefaultSqlSession implements SqlSession {

  private Configuration configuration;
  private Executor executor;

  private boolean dirty;
  
  
  public  T getMapper(Class type) {
    return configuration.getMapper(type, this);
  }
}  

-- 其中configuration的實(shí)現(xiàn)為
  public  T getMapper(Class type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
  
-- MapperRegistry的實(shí)現(xiàn)為
package org.apache.ibatis.binding;

public class MapperRegistry {

  private Configuration config;
  private final Map, MapperProxyFactory> knownMappers = new HashMap, MapperProxyFactory>();

  public MapperRegistry(Configuration config) {
    this.config = config;
  }
  
  @SuppressWarnings("unchecked")
  public  T getMapper(Class 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);
    }
  }
  
  public  boolean hasMapper(Class type) {
        return knownMappers.containsKey(type);
    }

    public  void addMapper(Class type) {
     if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
}

可以看到,mapper是一個(gè)代理對象,它實(shí)現(xiàn)的接口就是傳入的type,這就是為什么mapper對象可以通過接口直接訪問。同時(shí)還可以看到,創(chuàng)建mapper代理對象時(shí)傳入了sqlsession對象,這樣就把sqlsession也關(guān)聯(lián)起來了。我們進(jìn)一步看看mapperProxyFactory.newInstance(sqlSession);背后發(fā)生了什么事情:

package org.apache.ibatis.binding;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.ibatis.session.SqlSession;

public class MapperProxyFactory {

  private final Class mapperInterface;
  private Map methodCache = new ConcurrentHashMap();

  public MapperProxyFactory(Class mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class getMapperInterface() {
    return mapperInterface;
  }

  public Map getMethodCache() {
    return methodCache;
  }

   /**
    * newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
    *   ClassLoader loader :類加載器,一般使用和被代理對象相同的類加載器
    *   Class[] interfaces :被代理對象(目標(biāo)對象)的接口數(shù)組
    *   InvocationHandler h : 設(shè)置回調(diào)對象,當(dāng)代理對象的方法被調(diào)用時(shí),會委派給該參數(shù)指定對象的invoke方法 
    */
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

看起來沒什么特別的,和其他代理類的創(chuàng)建一樣,我們重點(diǎn)關(guān)注一下MapperProxy的invoke方法。

我們知道對被代理對象的方法的訪問都會落實(shí)到代理者的invoke上來,MapperProxy的invoke如下:

package org.apache.ibatis.binding;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.SqlSession;

/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public class MapperProxy implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class mapperInterface;
  private final Map methodCache;

  public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        // 轉(zhuǎn)調(diào)具體目標(biāo)對象的方法,這里目標(biāo)對象為this
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    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;
  }

}

可以看到invoke把執(zhí)行權(quán)轉(zhuǎn)交給了MapperMethod,我們來看看MapperMethod里又是怎么運(yùn)作的:

package org.apache.ibatis.binding;

import org.apache.ibatis.annotations.Flush;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 * @author Lasse Voss
 */
public class MapperMethod {

  private final SqlCommand command;
  private final MethodSignature method;

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

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

}

可以看到,MapperMethod就像是一個(gè)分發(fā)者,他根據(jù)參數(shù)和返回值類型選擇不同的sqlsession方法來執(zhí)行。這樣mapper對象與sqlsession就真正的關(guān)聯(lián)起來了。

Executor介紹

sqlsession只是一個(gè)門面,真正發(fā)揮作用的是executor,對sqlsession方法的訪問最終都會落到executor的相應(yīng)方法上去。

Executor分成兩大類:一類是CacheExecutor,另一類是普通Executor。

CacheExecutor

CacheExecutor有一個(gè)重要屬性delegate,它保存的是某類普通的Executor,值在構(gòu)照時(shí)傳入。執(zhí)行數(shù)據(jù)庫update操作時(shí),它直接調(diào)用delegate的update方法,執(zhí)行query方法時(shí)先嘗試從cache中取值,取不到再調(diào)用delegate的查詢方法,并將查詢結(jié)果存入cache中。

  @Override
  public  List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, parameterObject, boundSql);
        @SuppressWarnings("unchecked")
        List list = (List) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
     // delegate.query實(shí)際是org.apache.ibatis.executor.BaseExecutor.query
     // 最終會調(diào)用BaseExecutor
     return delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

普通Executor

普通Executor有3類,他們都繼承于BaseExecutor,BatchExecutor專門用于執(zhí)行批量sql操作,ReuseExecutor會重用statement執(zhí)行sql操作,SimpleExecutor只是簡單執(zhí)行sql沒有什么特別的。下面以SimpleExecutor為例:

  @Override
  public  List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

可以看出,Executor本質(zhì)上也是個(gè)甩手掌柜,具體的事情原來是StatementHandler來完成的。

StatementHandler介紹

當(dāng)Executor將指揮棒交給StatementHandler后,接下來的工作就是StatementHandler的事了。我們先看看StatementHandler是如何創(chuàng)建的。

StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

configuration.newStatementHandler的實(shí)現(xiàn)為:

  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

可以看到每次創(chuàng)建的StatementHandler都是RoutingStatementHandler,它只是一個(gè)分發(fā)者,他一個(gè)屬性delegate用于指定用哪種具體的StatementHandler。

同時(shí)還要注意到StatementHandler是可以被攔截器攔截的,和Executor一樣,被攔截器攔截后的對像是一個(gè)代理對象。由于mybatis沒有實(shí)現(xiàn)數(shù)據(jù)庫的物理分頁,眾多物理分頁的實(shí)現(xiàn)都是在這個(gè)地方使用攔截器實(shí)現(xiàn)的。

可選的StatementHandler有SimpleStatementHandler、PreparedStatementHandler和CallableStatementHandler三種。選用哪種在mapper配置文件的每個(gè)statement里指定,默認(rèn)的是PreparedStatementHandler。

MyBatis之SqlSession介紹

MyBatis之SqlSession介紹

statementType的取值是通過DTD文件來約束的:

詳見:http://mybatis.org/dtd/mybatis-3-mapper.dtd


StatementHandler創(chuàng)建后需要執(zhí)行一些初始操作,比如statement的開啟和參數(shù)設(shè)置、對于PreparedStatement還需要執(zhí)行參數(shù)的設(shè)置操作等。

代碼如下:org.apache.ibatis.executor.SimpleExecutor

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection);
    handler.parameterize(stmt);
    return stmt;
  }

MyBatis之SqlSession介紹

statement的開啟和參數(shù)設(shè)置沒什么特別的地方:

package org.apache.ibatis.executor.statement;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

/**
 * @author Clinton Begin
 */
public class RoutingStatementHandler implements StatementHandler {

  private final StatementHandler delegate;

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

  @Override
  public Statement prepare(Connection connection) throws SQLException {
    return delegate.prepare(connection);
  }
}

org.apache.ibatis.executor.statement.PreparedStatementHandler并沒有對prepare的具體實(shí)現(xiàn),調(diào)用的還是其父類org.apache.ibatis.executor.statement.BaseStatementHandler的實(shí)現(xiàn):

  @Override
  public Statement prepare(Connection connection) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      statement = instantiateStatement(connection);
      setStatementTimeout(statement);
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }

handler.parameterize倒是可以看看是怎么回事:

MyBatis之SqlSession介紹

handler.parameterize通過調(diào)用ParameterHandler的setParameters完成參數(shù)的設(shè)置,ParameterHandler隨著StatementHandler的創(chuàng)建而創(chuàng)建,默認(rèn)的實(shí)現(xiàn)是DefaultParameterHandler:

// ======================================================================
package org.apache.ibatis.executor.statement;

/**
 * @author Clinton Begin
 */
public class PreparedStatementHandler extends BaseStatementHandler {

  public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
  }

  // 代碼略

  @Override
  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }

}


// ======================================================================
package org.apache.ibatis.executor.statement;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;

/**
 * @author Clinton Begin
 */
public abstract class BaseStatementHandler implements StatementHandler {

  protected final Configuration configuration;
  protected final ObjectFactory objectFactory;
  protected final TypeHandlerRegistry typeHandlerRegistry;
  protected final ResultSetHandler resultSetHandler;
  protected final ParameterHandler parameterHandler;

  protected final Executor executor;
  protected final MappedStatement mappedStatement;
  protected final RowBounds rowBounds;

  protected BoundSql boundSql;

  protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    this.configuration = mappedStatement.getConfiguration();
    this.executor = executor;
    this.mappedStatement = mappedStatement;
    this.rowBounds = rowBounds;

    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();

    if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

    this.boundSql = boundSql;
    
    // 默認(rèn)為org.apache.ibatis.scripting.defaults.DefaultParameterHandler
    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
  }

}

同Executor和StatementHandler一樣,ParameterHandler也是可以被攔截的。

DefaultParameterHandler

DefaultParameterHandler里設(shè)置參數(shù)的代碼如下:

package org.apache.ibatis.scripting.defaults;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;

import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeException;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

/**
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public class DefaultParameterHandler implements ParameterHandler {

  private final TypeHandlerRegistry typeHandlerRegistry;

  private final MappedStatement mappedStatement;
  private final Object parameterObject;
  private BoundSql boundSql;
  private Configuration configuration;

  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    this.mappedStatement = mappedStatement;
    this.configuration = mappedStatement.getConfiguration();
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    this.parameterObject = parameterObject;
    this.boundSql = boundSql;
  }

  @Override
  public Object getParameterObject() {
    return parameterObject;
  }

  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          } catch (SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

}

這里面最重要的一句其實(shí)就是最后一句代碼,它的作用是用合適的TypeHandler完成參數(shù)的設(shè)置。那么什么是合適的TypeHandler呢,它又是如何決斷出來的呢?BaseStatementHandler的構(gòu)造方法里有這么一句:

    if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }

它觸發(fā)了sql 的解析,在解析sql的過程中,TypeHandler也被決斷出來了,決斷的原則就是根據(jù)參數(shù)的類型和參數(shù)對應(yīng)的JDBC類型決定使用哪個(gè)TypeHandler。比如:參數(shù)類型是String的話就用StringTypeHandler,參數(shù)類型是整數(shù)的話就用IntegerTypeHandler等。

參數(shù)設(shè)置完畢后,執(zhí)行數(shù)據(jù)庫操作(update或query)。如果是query最后還有個(gè)查詢結(jié)果的處理過程。

ResultSetHandler

結(jié)果處理使用ResultSetHandler來完成,默認(rèn)的ResultSetHandler是FastResultSetHandler,它在創(chuàng)建StatementHandler時(shí)一起創(chuàng)建,代碼如下:

org.apache.ibatis.executor.statement.BaseStatementHandler

    this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);

org.apache.ibatis.session.Configuration

  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

org.apache.ibatis.executor.statement.PreparedStatementHandler

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

可以看出ResultSetHandler也是可以被攔截的,可以編寫自己的攔截器改變ResultSetHandler的默認(rèn)行為。

MyBatis之SqlSession介紹

  private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
    List autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
    boolean foundValues = false;
    if (autoMapping.size() > 0) {
      for (UnMappedColumAutoMapping mapping : autoMapping) {
        final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
        // issue #377, call setter on nulls
        if (value != null || configuration.isCallSettersOnNulls()) {
          if (value != null || !mapping.primitive) {
            metaObject.setValue(mapping.property, value);
          }
          foundValues = true;
        }
      }
    }
    return foundValues;
  }

從代碼里可以看到,決斷TypeHandler使用的是結(jié)果參數(shù)的屬性類型。因此我們在定義作為結(jié)果的對象的屬性時(shí)一定要考慮與數(shù)據(jù)庫字段類型的兼容性。

附件:http://down.51cto.com/data/2366409

分享題目:MyBatis之SqlSession介紹
網(wǎng)站路徑:http://weahome.cn/article/jjpcco.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部