欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MyBatis 二級緩存實(shí)現(xiàn)機(jī)制的示例解析

 更新時(shí)間:2025年05月22日 09:38:40   作者:一名技術(shù)極客  
本文主要介紹了MyBatis 二級緩存實(shí)現(xiàn)機(jī)制的示例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前置準(zhǔn)備

既然要看MyBatis源碼,當(dāng)然是把源碼拉取下來debug一步一步看才方便呢,這里已經(jīng)拉取下來,并準(zhǔn)備好例子了。

@Slf4j
public class Main {
  public static void main(String[] args) {
    String resource = "org/apache/ibatis/yyqtest/resource/mybatis-config.xml";
    InputStream inputStream = null;
    try {
      // 將XML配置文件構(gòu)建為Configuration配置類
      inputStream = Resources.getResourceAsStream(resource);
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    // 通過加載配置文件流構(gòu)建一個(gè)SqlSessionFactory DefaultSqlSessionFactory
    SqlSessionFactory sqlSessionFactory = null;
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = null;
    try {
      sqlSession = sqlSessionFactory.openSession();
      RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
      //Role role = roleMapper.testManyParam(1L, "張三");
      //Role role = roleMapper.testAnnotation(1L);
      Role role = roleMapper.testDynamicParam(1L, "");

      log.info(role.getId() + ":" + role.getRoleName() + ":" + role.getNote());
      sqlSession.commit();

    } catch (Exception e) {
      // TODO Auto-generated catch block
      sqlSession.rollback();
      e.printStackTrace();
    } finally {
      sqlSession.close();
    }
  }
}

從上訴例子來看,我們觀察到首先是解析配置文件,再獲取SqlSession,獲取到Sqlsession之后在進(jìn)行各種的CRUD操作,我們先來看下SqlSession是怎么獲取的。

SqlSession與SqlSessionFactory

在這里插入圖片描述

?我們根據(jù)時(shí)序圖,一步一步解開SqlSession的執(zhí)行流程,首先來是通過SqlSessionFactoryBuilder解析配置文件,再根據(jù)配置文件構(gòu)建并返回一個(gè)DefaultSqlSessionFactory,源碼如下:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    // 2. 創(chuàng)建XMLConfigBuilder對象用來解析XML配置文件,生成Configuration對象
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    // 3. 將XML配置文件內(nèi)的信息解析成Java對象Configuration對象
    Configuration config = parser.parse();
    // 4. 根據(jù)Configuration對象創(chuàng)建出SqlSessionFactory對象
    return build(config);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
      if (inputStream != null) {
        inputStream.close();
      }
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}

public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}

拿到SqlSessionFactory之后,就可以根據(jù)這個(gè)SqlSessionFactory拿到SqlSession對象,源碼如下:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
    final Environment environment = configuration.getEnvironment();
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    // 通過事務(wù)工廠從一個(gè)數(shù)據(jù)源來產(chǎn)生一個(gè)事務(wù)
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    // 獲取執(zhí)行器,這邊獲得的執(zhí)行器已經(jīng)代理攔截器的功能
    final Executor executor = configuration.newExecutor(tx, execType);
    // 獲取執(zhí)行器后,再將configuration和executor封裝成DefaultSqlSession
    return new DefaultSqlSession(configuration, executor, autoCommit);
  } 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();
  }
}

通過上面兩個(gè)步驟拿到SqlSession之后,就可以進(jìn)行CRUD

  • 將xml配置文件解析成流
  • SqlSessionFactory.build將配置文件解析成Configuration,并返回一個(gè)SqlSessionFactory對象
  • 拿到SqlSessionFactory對象之后,從Environment中拿到DataSource并產(chǎn)生一個(gè)事務(wù)
  • 根據(jù)事務(wù)Transaction和執(zhí)行器類型(SIMPLE, REUSE, BATCH,默認(rèn)是SIMPLE)獲取一個(gè)執(zhí)行器Executor(–>該對象非常重要,事實(shí)上sqlsession的所有操作都是通過它完成的)
  • 創(chuàng)建sqlsession對象。

MapperProxy

拿到Sqlsession對象之后,就可以拿到我們所寫的xxxMapper,再根據(jù)這個(gè)xxxmapper就可以調(diào)用方法,最終進(jìn)行CRUD。我們來關(guān)注下我們這個(gè)xxxMapper是一個(gè)接口,接口是不能被實(shí)例化的,為什么可以直接通過getMapper方式拿到呢?xxxMapper和我們的xxxMapper.xml文件之間又是如何聯(lián)系的?別急,我們接著往下看。

我們先來看下時(shí)序圖:

在這里插入圖片描述

通過這個(gè)時(shí)序圖可以看到是通過代理的方式來搞定的,當(dāng)執(zhí)行到自己寫的方法里面的時(shí)候,其實(shí)通過了MapperProxy在進(jìn)行代理。話不多說,我們直接來看下怎么獲取MapperProxy對象的吧: 哦吼↑ ↓

DefaultSqlSession

/**
* 什么都不做,直接去configuration里面去找
* @param type Mapper interface class
* @param <T>
* @return
*/
@Override
public <T> T getMapper(Class<T> type) {
	return configuration.getMapper(type, this);
}

Configuration

// mapperRegistry實(shí)質(zhì)上是一個(gè)Map,里面注冊了啟動過程中解析的各種Mapper.xml
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

/**
 * configuration也什么都不做,去mapperRegistry里面找
 * @param type
 * @param sqlSession
 * @param <T>
 * @return
 */
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
	return mapperRegistry.getMapper(type, sqlSession);
}

這里會發(fā)現(xiàn),這個(gè)mapperRegistry是什么東西啊,mapperRegistry實(shí)質(zhì)上是一個(gè)Map,里面注冊了啟動過程中解析的各種Mapper.xml。我們點(diǎn)進(jìn)去看看

Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

mapperRegistry的key是接口的Class類型
mapperRegistry的Value是MapperProxyFactory,用于生成對應(yīng)的MapperProxy(動態(tài)代理類)

?這里具體是怎么加進(jìn)去的可以去了解了解解析配置的篇章,我們接著往下走,看看MapperRegistry的源碼:

MapperRegistry

/**
 * MapperRegistry標(biāo)識很無奈,沒人做,只有我來做了
 * @param type
 * @param sqlSession
 * @param <T>
 * @return
 */
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  // MapperRegistry表示也要偷懶,偷偷的把粗活交給MapperProxyFactory去做。
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  // 如果配置文件中沒有配置相關(guān)Mapper,直接拋異常
  if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  }
  try {
    // 我們看到mapper都是接口,因?yàn)榻涌谑遣荒軐?shí)例化的,這里通過動態(tài)代理實(shí)現(xiàn),具體可以看下MapperProxy這個(gè)類
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}

MapperProxyFactory這里是將接口實(shí)例化的過程,我們進(jìn)去看看:

MapperProxyFactory

public class MapperProxyFactory<T> {
  /**
   * 生成Mapper接口的動態(tài)代理類MapperProxy,MapperProxy實(shí)現(xiàn)了InvocationHandler接口
   *
   * @param mapperProxy
   * @return
   */
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
      // 動態(tài)代理,我們寫的一些接口
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
  }

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

通過這個(gè)MapperProxyFactory就可以拿到類MapperProxy代理對象,簡單來說,我們通過這個(gè)代理就可以很方便的去使用我們寫的一些dao層的接口了,上例子

  RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
  Role role = roleMapper.testManyParam(1L, "張三");
  Role role = roleMapper.testAnnotation(1L);
  Role role = roleMapper.testDynamicParam(1L, "");

拿到這個(gè)對象之后,接下來就是sql語句的執(zhí)行了呢,來,上源碼,真正的主菜來了。

Executor

SqlSession只是一個(gè)門面,前面做的一些事情都只是鋪墊,真正的的干事的其實(shí)是Excutor,SqlSession對數(shù)據(jù)庫的操作都是通過Executor來完成的。與SqlSession一樣,Executor也是動態(tài)創(chuàng)建的,老樣子先上時(shí)序圖:

在這里插入圖片描述

在看MapperProxy的方法之前,我們先回到獲取Excutor對象的方法configuration.newExecutor(tx, execType)

/**
 * Executor分成兩大類,一類是CacheExecutor,另一類是普通Executor。
 *
 * @param transaction
 * @param executorType
 * @return
 */
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? defaultExecutorType : executorType;
  // 這句再做一下保護(hù),囧,防止粗心大意的人將defaultExecutorType設(shè)成null?
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;
  // 普通Executor又分為三種基本的Executor執(zhí)行器,SimpleExecutor、ReuseExecutor、BatchExecutor。
  if (ExecutorType.BATCH == executorType) {
    executor = new BatchExecutor(this, transaction);
  } else if (ExecutorType.REUSE == executorType) {
    // 執(zhí)行update或select,以sql作為key查找Statement對象,存在就使用,不存在就創(chuàng)建,用完后,不關(guān)閉Statement對象,而是放置于Map<String,Statement>內(nèi),供下一次使用。簡言之,就是重復(fù)使用Statement對象
    executor = new ReuseExecutor(this, transaction);
  } else {
    // 每執(zhí)行一次update或select,就開啟一個(gè)Statement對象,用完立刻關(guān)閉Statement對象。
    executor = new SimpleExecutor(this, transaction);
  }
  if (cacheEnabled) {
    // CacheExecutor其實(shí)是封裝了普通的Executor,和普通的區(qū)別是在查詢前先會查詢緩存中
    // 是否存在結(jié)果,如果存在就使用緩存中的結(jié)果,如果不存在還是使用普通的Executor進(jìn)行
    // 查詢,再將查詢出來的結(jié)果存入緩存。
    executor = new CachingExecutor(executor);
  }
  // 調(diào)用每個(gè)攔截器的方法,這里的話應(yīng)該是為了方便擴(kuò)展,方便開發(fā)者自定義攔截器做一些處理
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

從上訴代碼可以看出,如果不開啟緩存cache的話,創(chuàng)建的Executor只是3中基礎(chǔ)類型之一,BatchExecutor專門用于執(zhí)行批量sql操作,ReuseExecutor會重用statement執(zhí)行sql操作,SimpleExecutor只是簡單執(zhí)行sql沒有什么特別的。

?開啟了緩存(默認(rèn)開啟),就會去創(chuàng)建CachingExecutor,這個(gè)緩存執(zhí)行器在查詢數(shù)據(jù)庫前會先查找緩存是否存在結(jié)果,如果存在就使用緩存中的結(jié)果,如果不存在還是使用普通的Executor進(jìn)行查詢,再將查詢出來的結(jié)果存入緩存。我們還看到這里有個(gè)加載插件的方法,就說明這里是可以被插件攔截的,如果定義了針對Executor類型的插件,最終生成的Executor對象是被各個(gè)插件插入后的代理對象。

接下來,咱們才要真正的去了解sql的執(zhí)行過程了。回到上面來看,我們拿到了MapperProxy,調(diào)用Mapper接口的所有方法都會優(yōu)先調(diào)用到這個(gè)代理類的invoke方法(注意由于MyBatis中的Mapper接口沒有實(shí)現(xiàn)類,所以MpperProxy這個(gè)代理對象中沒有委托類,也就是說MapperProxy干了代理類和委托類的事情),具體是怎么做的,上源碼:

MapperProxy

MapperProxyinvoke方法本質(zhì)上是交給了MapperMethodInvoker,MapperMethodInvoker實(shí)質(zhì)就是封裝了一層MapperMethod。

/**
 * 通過動態(tài)代理后,所有的Mapper方法調(diào)用都會走這個(gè)invoke方法
 *
 * @param proxy
 * @param method
 * @param args
 * @return
 * @throws Throwable
 */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    // 并不是任何一個(gè)方法都需要執(zhí)行調(diào)用代理對象進(jìn)行執(zhí)行,如果這個(gè)方法是Object中通用的方法(toString、hashCode等)無需執(zhí)行
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else {
      // cachedInvoker封裝了一個(gè)MapperMethod對象
      // 拆分出來更好理解
      MapperMethodInvoker mapperMethodInvoker = cachedInvoker(method);
      return mapperMethodInvoker.invoke(proxy, method, args, sqlSession);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
}

MapperMethod

根據(jù)參數(shù)和返回值類型選擇不同的sqlSession來執(zhí)行。這樣mapper對象和sqlSession就真正關(guān)聯(lián)起來了

/**
 * 這里其實(shí)就是先判斷CRUD的類型,根據(jù)類型去選擇到底執(zhí)行了sqlSession中的哪個(gè)方法
 * @param sqlSession
 * @param args
 * @return
 */
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()) {
        // 如果結(jié)果是map
        result = executeForMap(sqlSession, args);
      } else if (method.returnsCursor()) {
        result = executeForCursor(sqlSession, args);
      } else {
        // 從字面意義上看,講轉(zhuǎn)換的參數(shù)放到sql里面
        // Map<String, Object> param
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
        if (method.returnsOptional()
            && (result == null || !method.getReturnType().equals(result.getClass()))) {
          result = Optional.ofNullable(result);
        }
      }
      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了,前面提到過,sqlsession只是一個(gè)門面,真正發(fā)揮作用的是executor,對sqlsession方法的訪問最終都會落到executor的相應(yīng)方法上去。Executor分成兩大類,一類是CacheExecutor,另一類是普通Executor。Executor的創(chuàng)建前面已經(jīng)介紹了,那么咱們就看看SqlSession的CRUD方法了,為了省事,還是就選擇其中的一個(gè)方法來做分析吧。這兒,咱們選擇了selectList方法。這里有寶寶就好奇了,從上面的代碼來看只有selectOne方法呢,哪里來的selectList。其實(shí)selectOne方法里面本質(zhì)就是調(diào)用了selectList方法。

@Override
public <T> T selectOne(String statement, Object parameter) {
  // Popular vote was to return null on 0 results and throw exception on too many.
  // 轉(zhuǎn)而去調(diào)用selectList,很簡單的,如果得到0條則返回null,得到1條則返回1條,得到多條報(bào)TooManyResultsException錯(cuò)
  // 這里沒有查詢到結(jié)果的時(shí)候會返回null。因此一般建議mapper中編寫resultType使用包裝類型
  List<T> list = this.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;
  }
}

石錘了?。?!,接下來我們來看下selectList方法。

DefaultSqlSession

/**
 *
 * @param statement 全限定名稱+方法名 例如org.apache.ibatis.yyqtest.mapper.RoleMapper.testAnnotation
 * @param parameter
 * @param rowBounds
 * @param handler
 * @param <E>
 * @return
 */
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
  try {
    // 根據(jù)傳入的全限定命+方法名從映射的map中取出MappedStatement對象
    MappedStatement ms = configuration.getMappedStatement(statement);
    // 之類能看到CRUD實(shí)際上是交給Executor處理
    return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

?這里的statement是全限定名稱+方法名字,例如org.apache.ibatis.yyqtest.mapper.RoleMapper.testAnnotation,具體怎么來的,可以自己debug看下是怎么得到的。這里的getMapperStatement做了什么呢。話不多說上源碼。

protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
    .conflictMessageProducer((savedValue, targetValue) ->
      ". please check " + savedValue.getResource() + " and " + targetValue.getResource());


public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
  if (validateIncompleteStatements) {
    buildAllStatements();
  }
  return mappedStatements.get(id);
}

?映射的語句,存在Map里,這里應(yīng)該是掃描配置文件的時(shí)候就已經(jīng)加進(jìn)去了 key是全限定名+方法名 value是對應(yīng)的mappedStatements。這個(gè)MapperStatements方法到底有什么作用呢,我們先別急。先回到executor.query的方法繼續(xù)往下走。

StatementHandler

可以看出,Executor本質(zhì)上也是個(gè)甩手掌柜,具體的事情原來是StatementHandler來完成的。當(dāng)Executor將指揮棒交給StatementHandler后,接下來的工作就是StatementHandler的事了。我們先看看StatementHandler是如何創(chuàng)建的:

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??蛇x的StatementHandlerSimpleStatementHandler、PreparedStatementHandlerCallableStatementHandler三種。選用哪種在mapper配置文件的每個(gè)statement里指定,默認(rèn)的是PreparedStatementHandler。同時(shí)還要注意到StatementHandler是可以被攔截器攔截的,和Executor一樣,被攔截器攔截后的對像是一個(gè)代理對象。

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

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

這里呢會創(chuàng)建一個(gè)StatementHandler對象,這個(gè)對象中同時(shí)會 封裝ParameterHandlerResultSetHandler對象。調(diào)用StatementHandler預(yù)編譯參數(shù) 以及設(shè)置參數(shù)值,使用ParameterHandler來給sql設(shè)置參數(shù)。

參數(shù)設(shè)置完畢后,執(zhí)行數(shù)據(jù)庫操作(update或query)。如果是query最后還有個(gè)查詢結(jié)果的處理過程。接下來,咱們看看StatementHandler 的一個(gè)實(shí)現(xiàn)類 PreparedStatementHandler(這也是我們最常用的,封裝的是PreparedStatement), 看看它使怎么去處理的:

@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  // 將處理好的結(jié)果交給了ResultSetHandler
  return resultSetHandler.handleResultSets(ps);
}

到這里,一次sql的執(zhí)行流程就執(zhí)行完了!

簡單總結(jié)

獲取SqlSession流程

  • SqlSessuibFactoryBuilder解析配置文件,并將這些配置放到一個(gè)Configuration對象,這個(gè)對象中包含了MyBatis需要的所有配置,然后會用這個(gè)Configuration對象再去創(chuàng)建一個(gè)SqlSessionFactory對象。
  • 拿到SqlSessionFactory對象后,會調(diào)用SqlSessionFactory的openSession方法,這個(gè)方法根據(jù)解析配置文件的事務(wù)和執(zhí)行器類型來創(chuàng)建一個(gè)執(zhí)行器,這個(gè)執(zhí)行器就是真正sql的執(zhí)行者。最后將執(zhí)行器和configuration封裝起來返回一個(gè)DefaultSqlSession
  • 拿到SqlSession之后就能執(zhí)行各種CRUD的方法了。

Sql執(zhí)行流程

  • 拿到SqlSession之后調(diào)用getMapper方法,拿到Maper接口的代理對象MapperProxy,調(diào)用Mapper接口的所有方法都會調(diào)用MapperProxy的invoke方法。
  • 到達(dá)invoke方法,就會掉用執(zhí)行器的executer方法。
  • 往下,層層調(diào)下來會進(jìn)入Executor組件(如果配置插件會對Executor進(jìn)行動態(tài)代 理)的query方法,這個(gè)方法中會創(chuàng)建一個(gè)StatementHandler對象,這個(gè)對象中同時(shí)會 封裝ParameterHandlerResultSetHandler對象。調(diào)用StatementHandler預(yù)編譯參數(shù) 以及設(shè)置參數(shù)值,使用ParameterHandler來給sql設(shè)置參數(shù)。
  • 調(diào)用StatementHandler的增刪改查方法獲得結(jié)果,ResultSetHandler對結(jié)果進(jìn)行封 裝轉(zhuǎn)換,請求結(jié)束。

MyBatis一些重要的類

  • MapperRegistry: 本質(zhì)上是一個(gè)Map其中的key是Mapper接口的全限定名, value的MapperProxyFactory;
  • MapperProxyFactory: 這個(gè)類是MapperRegistry中存的value值,在通過 sqlSession獲取Mapper時(shí),其實(shí)先獲取到的是這個(gè)工廠,然后通過這個(gè)工廠創(chuàng)建 Mapper的動態(tài)代理類;
  • MapperProxy: 實(shí)現(xiàn)了InvocationHandler接口,Mapper的動態(tài)代理接口方法的調(diào) 用都會到達(dá)這個(gè)類的invoke方法;
  • MapperMethod: 判斷你當(dāng)前執(zhí)行的方式是增刪改查哪一種,并通過SqlSession執(zhí) 行相應(yīng)的操作;
  • SqlSession: 作為MyBatis工作的主要頂層API,表示和數(shù)據(jù)庫交互的會話,完成必 要數(shù)據(jù)庫增刪改查功能;
  • Executor: MyBatis執(zhí)行器,是MyBatis 調(diào)度的核心,負(fù)責(zé)SQL語句的生成和查詢緩 存的維護(hù);
  • StatementHandler: 封裝了JDBC Statement操作,負(fù)責(zé)對JDBC statement 的操作,如設(shè) 置參數(shù)、將Statement結(jié)果集轉(zhuǎn)換成List集合。
  • ParameterHandler: 負(fù)責(zé)對用戶傳遞的參數(shù)轉(zhuǎn)換成JDBC Statement 所需要的參數(shù)
  • ResultSetHandler: 負(fù)責(zé)將JDBC返回的ResultSet結(jié)果集對象轉(zhuǎn)換成List類型的集合;
  • TypeHandler: 負(fù)責(zé)java數(shù)據(jù)類型和jdbc數(shù)據(jù)類型之間的映射和轉(zhuǎn)換
  • MappedStatement: MappedStatement維護(hù)了一條節(jié)點(diǎn) 的封裝
  • SqlSource: 負(fù)責(zé)根據(jù)用戶傳遞的parameterObject,動態(tài)地生成SQL語句,將信息封裝到 BoundSql對象中,并返回
  • BoundSql: 表示動態(tài)生成的SQL語句以及相應(yīng)的參數(shù)信息
  • Configuration: MyBatis所有的配置信息都維持在Configuration對象之中。

到此這篇關(guān)于MyBatis 二級緩存實(shí)現(xiàn)機(jī)制的示例解析的文章就介紹到這了,更多相關(guān)MyBatis 二級緩存機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

最新評論