最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解MyBatis Mapper 代理實現(xiàn)數(shù)據(jù)庫調用原理

 更新時間:2019年10月27日 09:12:20   作者:coderbee筆記  
這篇文章主要介紹了詳解MyBatis Mapper 代理實現(xiàn)數(shù)據(jù)庫調用原理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

1. Mapper 代理層執(zhí)行

Mapper 代理上執(zhí)行方法調用時,調用被委派給 MapperProxy 來處理。

public class MapperProxy<T> implements InvocationHandler, Serializable {
 private final SqlSession sqlSession;
 private final Class<T> mapperInterface;
 private final Map<Method, MapperMethod> methodCache;

 public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
  this.sqlSession = sqlSession;
  this.mapperInterface = mapperInterface;
  this.methodCache = methodCache;
 }

 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);
   }
  }
  // 接口里聲明的方法,轉換為 MapperMethod 來調用
  final MapperMethod mapperMethod = cachedMapperMethod(method);

  // 與 Spring 集成時此處的 sqlSession 仍然 SqlSessionTemplate
  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;
 }
}

MapperMethod 根據(jù) mapperInterface.getName() + "." + method.getName() 從 Configuration 對象里找到對應的 MappedStatement ,從而得到要執(zhí)行的 SQL 操作類型(insert/delete/update/select/flush),然后調用傳入的 sqlSession 實例上的相應的方法。

public Object execute(SqlSession sqlSession, Object[] args) {
 Object result;
 if (SqlCommandType.INSERT == command.getType()) {
  // 把參數(shù)轉換為 SqlSession 能處理的格式
  Object param = method.convertArgsToSqlCommandParam(args);

  // 在 sqlSession 上執(zhí)行并處理結果
  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));
 ...省略

如果上述方法傳入的是 SqlSessionTemplate ,那么這些方法調用會被 SqlSessionInterceptor 攔截,加入與 Spring 事務管理機制協(xié)作的邏輯,具體可以看這篇文章MyBatis 事務管理,這里不再展開,最終會調用到 DefaultSqlSession 實例上的方法。

2. 會話層的執(zhí)行過程

SqlSession 里聲明的所有方法的第一個參數(shù)如果是 String statement ,則都是 mapperInterface.getName() + "." + method.getName() ,表示要調用的 SQL 語句的標識符。通過它從 configuration 找到 MappedStatement 。

會話層最主要的邏輯是進行參數(shù)的包裝,獲取對應的 MappedStatement ,然后調用持有的 Executor 的方法去執(zhí)行。

public class DefaultSqlSession implements SqlSession {
 private Configuration configuration;
 private Executor executor;

 private boolean autoCommit;
 private boolean dirty;
 private List<Cursor<?>> cursorList;

 public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
  this.configuration = configuration;
  this.executor = executor;
  this.dirty = false;
  this.autoCommit = autoCommit;
 }

 public <E> List<E> 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();
  }
 }

3. Executor 執(zhí)行的過程

我們知道 JDBC 里有三種數(shù)據(jù)庫語句: java.sql.Statement/PreparedStatement/CallableStatement ,每種語句的執(zhí)行方式是不一樣的,MyBatis 創(chuàng)建了 StatementHandler 抽象來表示數(shù)據(jù)庫語句的處理邏輯,有對應的三種具體實現(xiàn): SimpleStatementHandler/PreparedStatementHandler/CallableStatementHandler 。

RoutingStatementHandler 是個門面模式,構建時根據(jù)要執(zhí)行的數(shù)據(jù)庫語句類型實例化 SimpleStatementHandler/PreparedStatementHandler/CallableStatementHandler 中的一個類作為目標 delegate,并把調用都轉給這個 delegate 的方法。

不同的 handler 實現(xiàn)實現(xiàn)了對應的:數(shù)據(jù)庫語句的創(chuàng)建、參數(shù)化設置、執(zhí)行語句。

通過這層抽象,MyBatis 統(tǒng)一了 Executor 里的執(zhí)行流程,以下以 SimpleExecutor 的流程為例:

1. 對于傳入的 MappedStatement ms ,得到 Configuration configuration 。

2. configuration 通過 ms 的語句類型得到一個 RoutingStatementHandler 的實例(內部有個 delegate 可以委派) handler ,并用 InterceptorChain 對 handler 實例進行裝飾。

3. 通過 SimpleExecutor 持有的 Transaction 實例獲取對應的數(shù)據(jù)庫連接 connection。

4. handler 通過數(shù)據(jù)庫連接初始化數(shù)據(jù)庫語句 java.sql.Statement 或其子類 stmt ,設置超時時間和 fetchSize 。

5. 用 handler 對 stmt 進行參數(shù)化處理(比如 PreparedStatement 設置預編譯語句的參數(shù)值)。

6. handler 執(zhí)行相應的 stmt 完成數(shù)據(jù)庫操作。

7. 用 ResultSetHandler 對結果集進行處理。 ResultSetHandler 會調用 TypeHandler 來進行 Java 類型與數(shù)據(jù)庫列類型之間轉換。

// SimpleExecutor
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
 Statement stmt = null;
 try {
  Configuration configuration = ms.getConfiguration();

  // 創(chuàng)建 handler 來負責具體的執(zhí)行
  StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

  // 創(chuàng)建數(shù)據(jù)庫語句
  stmt = prepareStatement(handler, ms.getStatementLog());

  // 執(zhí)行數(shù)據(jù)庫操作
  return handler.<E>query(stmt, resultHandler);
 } finally {
  closeStatement(stmt);
 }
}

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

// RoutingStatementHandler
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
 // 根據(jù)SQL語句的執(zhí)行方式創(chuàng)建對應的 handler 實例
 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());
 }
}

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
 // 創(chuàng)建數(shù)據(jù)庫連接
 Connection connection = getConnection(statementLog);

 // 創(chuàng)建數(shù)據(jù)庫語句
 Statement stmt = handler.prepare(connection, transaction.getTimeout());

 // 參數(shù)化設置
 handler.parameterize(stmt);
 return stmt;
}

protected Connection getConnection(Log statementLog) throws SQLException {
 Connection connection = transaction.getConnection();
 if (statementLog.isDebugEnabled()) {
  return ConnectionLogger.newInstance(connection, statementLog, queryStack);
 } else {
  return connection;
 }
}

// BaseStatementHandler
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
 ErrorContext.instance().sql(boundSql.getSql());
 Statement statement = null;
 try {
  // 由具體的子類來創(chuàng)建對應的 Statement 實例
  statement = instantiateStatement(connection);

  // 通用參數(shù)設置
  setStatementTimeout(statement, transactionTimeout);
  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);
 }
}

// PreparedStatementHandler
protected Statement instantiateStatement(Connection connection) throws SQLException {
 String sql = boundSql.getSql();
 if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
  String[] keyColumnNames = mappedStatement.getKeyColumns();
  if (keyColumnNames == null) {
   return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
  } else {
   return connection.prepareStatement(sql, keyColumnNames);
  }
 } else if (mappedStatement.getResultSetType() != null) {
  return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
 } else {
  return connection.prepareStatement(sql);
 }
}

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

4. 問題

只在 XML 里定義 SQL、沒有對應的 Java 接口類能否使用 MyBatis ?

答:可以,通過 SqlSession 的方法來調用 XML 里的 SQL 語句。

Mapper 接口類里可以進行方法重載嗎?

答:不能,因為 MyBatis 里根據(jù) 類名 + “.” + 方法名 來查找 SQL 語句,重載會導致這樣的組合出現(xiàn)多條結果。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • springBoot項目集成quartz開發(fā)定時任務案例及注意事項

    springBoot項目集成quartz開發(fā)定時任務案例及注意事項

    這篇文章主要介紹了springBoot項目集成quartz開發(fā)定時任務案例及注意事項,這些功能的主要接口(API)是Scheduler接口。它提供了簡單的操作,例如:將任務納入日程或者從日程中取消,開始/停止/暫停日程進度,需要的朋友可以參考下
    2022-06-06
  • java8實現(xiàn)list集合中按照某一個值相加求和,平均值等操作代碼

    java8實現(xiàn)list集合中按照某一個值相加求和,平均值等操作代碼

    這篇文章主要介紹了java8實現(xiàn)list集合中按照某一個值相加求和,平均值等操作代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • JAVA讀取二進制文件以及畫圖教程

    JAVA讀取二進制文件以及畫圖教程

    由于項目需要,需要對二進制文件進行讀取,所以這篇文章主要給大家介紹了關于JAVA讀取二進制文件以及畫圖的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-07-07
  • java導出Excel通用方法的實例詳解

    java導出Excel通用方法的實例詳解

    這篇文章主要介紹了java導出Excel通用方法的實例詳解的相關資料,需要的朋友可以參考下
    2017-07-07
  • redis之基于SpringBoot實現(xiàn)Redis stream實時流事件處理方式

    redis之基于SpringBoot實現(xiàn)Redis stream實時流事件處理方式

    這篇文章主要介紹了redis之基于SpringBoot實現(xiàn)Redis stream實時流事件處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Spring Cloud Netflix架構淺析(小結)

    Spring Cloud Netflix架構淺析(小結)

    這篇文章主要介紹了Spring Cloud Netflix架構淺析(小結),詳解的介紹了Spring Cloud Netflix的概念和組件等,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • jmeter實現(xiàn)接口關聯(lián)的兩種方式(正則表達式提取器和json提取器)

    jmeter實現(xiàn)接口關聯(lián)的兩種方式(正則表達式提取器和json提取器)

    Jmeter用于接口測試時,后一個接口經(jīng)常需要用到前一次接口返回的結果,本文主要介紹了jmeter實現(xiàn)接口關聯(lián)的兩種方式,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Java?OpenCV學習之Mat的基本操作詳解

    Java?OpenCV學習之Mat的基本操作詳解

    OpenCV用來存儲圖像,很多時候都會用到這個Mat方法。數(shù)字圖像可看做一個數(shù)值矩陣,?其中的每一個元素表明一個像素點。Mat在?OpenCV?中表示的是?N?維稠密矩陣,與稠密矩陣相對的是稀疏矩陣。本文將重點介紹OpenCV中Mat的一些基本操作,需要的可以參考一下
    2022-03-03
  • springboot注冊bean的三種方法

    springboot注冊bean的三種方法

    這篇文章主要介紹了springboot注冊bean的三種方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-02-02
  • 使用@Autowired 注入RedisTemplate報錯的問題及解決

    使用@Autowired 注入RedisTemplate報錯的問題及解決

    這篇文章主要介紹了使用@Autowired 注入RedisTemplate報錯的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08

最新評論

许昌县| 太湖县| 吉林省| 手游| 上蔡县| 永安市| 包头市| 防城港市| 淳安县| 涪陵区| 泰宁县| 马尔康县| 依兰县| 阿城市| 林甸县| 梓潼县| 漯河市| 达孜县| 桦川县| 松阳县| 安福县| 诸城市| 宿州市| 沂水县| 新龙县| 湖南省| 凯里市| 健康| 新乐市| 英超| 大邑县| 台北县| 西乡县| 巴楚县| 丘北县| 揭阳市| 诸暨市| 屏边| 十堰市| 信宜市| 祁阳县|