1. 程式人生 > >Mybatis 程式碼流程及實現原理解析(三)

Mybatis 程式碼流程及實現原理解析(三)

接上篇, 這篇繼續分析XMLMapperBuilder.parse()裡的configurationElement() 這個方法。

private void configurationElement(XNode context) {
    try {
      //mapper可以加namespace來避免重複情況
      String namespace = context.getStringAttribute("namespace");
      builderAssistant.setCurrentNamespace(namespace);
      //該節點表示從其他名目空間引用快取配置 
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));//解析cache子節點.
      //解析parameterMap 子節點,因為有多個並列的parameterMap節點,所以要加上路徑,解析返回的列表
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //解析resultMap子節點,因為有多個並列的resultMap節點,所以要加上路徑,解析返回的列表
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //解析sql子節點,因為有多個並列的sql節點,所以要加上路徑,解析返回的列表
      sqlElement(context.evalNodes("/mapper/sql"));
      //解析select,insert,update,delete子節點
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new RuntimeException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }
此方法逐步解析<mapper>的子節點。

子節點<cache-ref>

xml配置片段:

<cache-ref namespace="com.Book"/>

java程式碼解析:

private void cacheRefElement(XNode context) {
    if (context != null) {
     //首先用該節點的namespace替換cacherefmap中mapper檔案的。
      configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
      CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
      try {
       //建立resolver,判斷該namespace是否可用。
    	  cacheRefResolver.resolveCacheRef();
      } catch (IncompleteElementException e) {
        //如果不可用,加入到為完成列表,在所有節點解析完成後,再統一做處理
    	  configuration.addIncompleteCacheRef(cacheRefResolver);
      }
    }
  }
CacheRefResolver.java的resolve方法
public Cache resolveCacheRef() {
  return assistant.useCacheRef(cacheRefNamespace);
}
MapperBuilderAssistant.java的useCacheRef方法:
public Cache useCacheRef(String namespace) {
    if (namespace == null) {
      throw new BuilderException("cache-ref element requires a namespace attribute.");
    }
    try {
      unresolvedCacheRef = true;
      Cache cache = configuration.getCache(namespace);
      //如果當前configuration物件沒有持有該namespace的Cache, 則丟擲異常。
      if (cache == null) {
        throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
      }
      currentCache = cache;
      unresolvedCacheRef = false;
      return cache;
    } catch (IllegalArgumentException e) {
      throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
    }
  }

子節點<cache>

xml配置如下:

<mapper namespace="com.skoyou.domain.UserDAO">
<cache type="com.domain.something.MyCustomCache"
  eviction="FIFO"
  flushInterval="60000"
  size="512"
  readOnly="tr">
<property name="cacheFile" value="/tmp/my-custom-cache.tmp"/>
</cache>
type:配置自定義快取或為其他第三方快取方案 建立介面卡來完全覆蓋快取行為。如果未指定的話, 使用“PERPETFUL”型別(PerpetualCache.java)。
eviction:快取回收策略,預設值是“LRU”, 有如下幾種策略可以指定:
LRU:最近最少使用的:移除最長時間不被使用的物件。
FIFO:先進先出:按物件進入快取的順序來移除它們。
SOFT:軟引用:移除基於垃圾回收器狀態和軟引用規則的物件。
WEAK:弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的物件.
flushInterval:重新整理間隔)可以被設定為任意的正整數,而且它們代表一個合理的毫秒 形式的時間段。預設情況是不設定,也就是沒有重新整理間隔,快取僅僅呼叫語句時重新整理。
size:(引用數目)可以被設定為任意正整數,要記住你快取的物件數目和你執行環境的 可用記憶體資源數目。預設值是 1024。
readyOnly:(只讀)屬性可以被設定為 true 或 false。只讀的快取會給所有呼叫者返回緩 存物件的相同例項。因此這些物件不能被修改。這提供了很重要的效能優勢。可讀寫的快取 會返回快取物件的拷貝(通過序列化) 。這會慢一些,但是安全,因此預設是 false。
cache可以有個子節點<property> 來載入.

java方法解析:

private void cacheElement(XNode context) throws Exception {
    if (context != null) {
      //獲取cache type的值,沒有的話,使用預設PERPETUAL
      String type = context.getStringAttribute("type", "PERPETUAL");
      // 得到type類, 預設是PERPETUAL 對應的PerpetualCache
      Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
      //獲取eviction的值,沒有的話,使用預設LRU得到eviction類, 即LruCache.class
      String eviction = context.getStringAttribute("eviction", "LRU");
      Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
      Long flushInterval = context.getLongAttribute("flushInterval");
      Integer size = context.getIntAttribute("size"); 
      boolean readWrite = !context.getBooleanAttribute("readOnly", false); 
     Properties props = context.getChildrenAsProperties();
     //根據提供的引數, 建一個cache物件, 並將這個物件放入到Configuration 物件中. 
     builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, props); 
    } 
}



子節點<parameterMap>

在Mybatis中,這個節點已經被廢棄了!老式風格的引數對映。內聯引數是首選,這個元素可能在將來被移除。其實解析還是比較簡單的。取這個節點的所有屬性,build出一個ParameterMapping物件,方法parametermappings列表裡, 然後將這個列表更新到configuration物件中的ParameterMap中去。

    for (XNode parameterMapNode : list) {
      String id = parameterMapNode.getStringAttribute("id");
      String type = parameterMapNode.getStringAttribute("type");
      Class<?> parameterClass = resolveClass(type);
      List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
      List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
      for (XNode parameterNode : parameterNodes) {
        String property = parameterNode.getStringAttribute("property");
        String javaType = parameterNode.getStringAttribute("javaType");
        String jdbcType = parameterNode.getStringAttribute("jdbcType");
        String resultMap = parameterNode.getStringAttribute("resultMap");
        String mode = parameterNode.getStringAttribute("mode");
        String typeHandler = parameterNode.getStringAttribute("typeHandler");
        Integer numericScale = parameterNode.getIntAttribute("numericScale");
        ParameterMode modeEnum = resolveParameterMode(mode);
        Class<?> javaTypeClass = resolveClass(javaType);
        JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
        @SuppressWarnings("unchecked")
        Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
        ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
        parameterMappings.add(parameterMapping);
      }
      builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
    }
  }

子節點<resultMap>

這個節點的解析較複雜,會在下一遍中單獨講解。

子節點<sql>

<sql id="userColumns"> id,username,password </sql>
這個元素可以被用來定義可重用的 SQL 程式碼段,可以包含在其他語句中。
Java 程式碼
private void sqlElement(List<XNode> list) throws Exception {
    if (configuration.getDatabaseId() != null) {  //傳人configuration現有的database id。
      sqlElement(list, configuration.getDatabaseId());
    }
    sqlElement(list, null);
  }
sqlElement方法:
  private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
    for (XNode context : list) { //由於有多個並列的sql節點,需求逐個解析。
     //sql 也可以有自己的database id,但實際上這個id要與全域性配置的一致
      String databaseId = context.getStringAttribute("databaseId");  
      String id = context.getStringAttribute("id");
     //判斷id是否包含有當前的namespace,包含的話直接返回當前id。 沒有的話,則以當前mapper的namespace.id值返回
      id = builderAssistant.applyCurrentNamespace(id, false);
      if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) 
       sqlFragments.put(id, context);
    }
  }
databaseIdMatchesCurrent 方法:

sql節點自己指定的database id要與configuration中的一致。

private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
    if (requiredDatabaseId != null) {
      if (!requiredDatabaseId.equals(databaseId)) {
        return false;
      }
    } else {
      if (databaseId != null) {
        return false;
      }
      // skip this fragment if there is a previous one with a not null databaseId
      if (this.sqlFragments.containsKey(id)) {
        XNode context = this.sqlFragments.get(id);
        if (context.getStringAttribute("databaseId") != null) {
          return false;
        }
      }
    }
    return true;
  }
實際上這個sqlFragments map是在XMLMappBuilder初始化的時候,有configuration傳入的, 所以configuration能得到最新的對這個map的修改。這個map會在build select等節點的時候被用到。

子節點<select>,<insert>,<update>,<delete>
節點配置,以select為例:

<select
  id="selectPerson"
  databaseId="test"
  fetchSize="256"
  timeout="10000"
  parameterMap="deprecated"
  parameterType="int"
  resultMap="personResultMap"
  resultType="hashmap"
  lang="XML"
  resultSetType="FORWARD_ONLY"
  statementType="PREPARED"
  flushCache="false"
  useCache="true"
  resultOrdered="false"
  >

節點屬性描述:
屬性 描述
id 在名稱空間中唯一的識別符號,可以被用來引用這條語句。
databaseId 如果配置了databaseProvider,databaseId要與配置的一致。如果不匹配的話,後續程式碼不會繼續解析           
fetchSize 這是暗示驅動程式每次批量返回的結果行數。預設不設定(驅動 自行處理)。
timeout 這個設定驅動程式等待資料庫返回請求結果,並丟擲異常時間的 最大等待值。預設不設定(驅動自行處理)
parameterMap 這是引用外部 parameterMap 的已經被廢棄的方法。使用內聯引數 對映和 parameterType 屬性。
parameterType 將會傳入這條語句的引數類的完全限定名或別名。
resultMap  命名引用外部的 resultMap。 返回 map 是 MyBatis 最具力量的特性, 對其有一個很好的理解的話, 許多複雜對映的情形就 能被解決了。 使用 resultMap 或 resultType,但不能同時使用。
resultType 從這條語句中返回的期望型別的類的完全限定名或別名。注意集 合情形,那應該是集合可以包含的型別,而不能是集合本身。使 用 resultType 或 resultMap,但不能同時使用
lang 提供”XML“或”RAW“,不同的lang,可能會影響最終的sql語句的生成,預設是”XML“
resultSetType FORWARD_ONLY|SCROLL_SENSITIVE|SCROLL_INSENSITIVE 中的一種。預設是不設定(驅動自行處理)。
statementType STA TEMENT,PREPARED 或 CALLABLE 的一種。 這會讓 MyBatis 使用選擇使用 Statement,PreparedStatement 或 CallableStatement。 預設值:PREPARED
fhushCache 將其設定為 true,不論語句什麼時候被呼叫,都會導致快取被 清空。預設值:false
userCache 將其設定為 true, 將會導致本條語句的結果被快取。 預設值: true
resultOrdered 這個屬性值適用於有巢狀結果的select 語句。如果是true, 那麼就認為包含了巢狀的結果, 或者巢狀的結果被分在一組。這樣的話, 當一個新的主結果返回時, 就不會出現上一個結果行。 這能夠更加記憶體友好地填充巢狀結果。預設是false。

另外還有3個是隻對insert有效的屬性:

useGeneratedKeys  這會告訴 MyBatis使用JDBC的getGeneratedKeys 方法來取出由資料(比如:像 MySQL 和 SQL Server 這樣的資料庫管理系統的自動遞增欄位)內部生成的主鍵。 預設值:false
keyProperty  標記一個屬性, MyBatis 會通過 getGeneratedKeys 或者通過 insert 語句的 selectKey 子元素設定它的值。 預設: 不設定
keyColumn 標記一個屬性, MyBatis 會通過 getGeneratedKeys 或者通過 insert 語句的 selectKey 子元素設定它的值。 預設: 不設定

sql語句的解析實際上是在 XMLStatementBuider的parseStatementNode方法中解析的。

  private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }

  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      //構造statementBuilder。這幾個sql語句相關的節點在這個類裡解析
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

XMLStatementBuilder.java的 parseStatementNode方法,如下。這個方法比較長,但主要是讀取屬性值,然後包裝生成一個MappedStatement,並放入configuration物件中。
public void parseStatementNode() {
    //讀取id 屬性
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");
    //讀取databaseId 屬性,但是一定要與現有存在的匹配, 否則不繼續解析剩餘節點 
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;

    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultType = context.getStringAttribute("resultType");
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    Class<?> resultTypeClass = resolveClass(resultType);
    String resultSetType = context.getStringAttribute("resultSetType");
    //statementType 預設是PreparedStatement
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    //如果是select語句,則預設值是false。否則預設值是true。
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    //如果是select語句,則預設值是true。否則預設值是false。
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // 解析<include>子節點,其實是用引用替換<sql>節點的實際內容。
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    // 解析<selectKey>子節點,僅對<insert>有用。後面會講解parseSelectKeyNodes方法
    List<XNode> selectKeyNodes = context.evalNodes("selectKey");
    if (configuration.getDatabaseId() != null) {
      parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId());
    }
    parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, null);

    //預設是XMLLanguageDriver,parameterTypeClass用不上
    //生成持有當前sql語句的sqlSource物件,後面會具體看這個sqlSource是怎麼生成的
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    //keyProperty和keyColumn使用於<insert>語句
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
    }
    //通過解析出來的屬性值,構造MappedStatement物件。並放入configuration物件中
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver);
  }

<insert>節點的子節點<selectKey>的解析

<selectKey>配置例子:

<selectKey
  databaseId="mysql“
  keyProperty="id"
  resultType="int"
  order="BEFORE"
  statementType="PREPARED">
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
  </selectKey>

keyProperty:selectKey 語句結果應該被設定的目標屬性。就是給哪個欄位生成主鍵值。

resultType:結果的型別。MyBatis 通常可以算出來,但是寫上也沒有問題。 MyBatis 允許任何簡單型別用作主鍵的型別,包括字串

statementType:MyBatis 支援 STA TEMENT ,PREPARED 和 CALLABLE 語句的對映型別,分別代表 PreparedStatement 和 CallableStatement 型別。

order:這可以被設定為 BEFORE 或 AFTER。如果設定為 BEFORE,那 麼它會首先選擇主鍵, 設定 keyProperty 然後執行插入語句。 如果 設定為 AFTER,那麼先執行插入語句,然後是 selectKey 元素- 這和如 Oracle 資料庫相似,可以在插入語句中嵌入序列呼叫.

java程式碼:

public void parseSelectKeyNodes(String parentId, List<XNode> list, Class<?> parameterTypeClass, LanguageDriver langDriver, String skRequiredDatabaseId) {
   //可以有多個<selectKey>節點。
   for (XNode nodeToHandle : list) {
      String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
      String databaseId = nodeToHandle.getStringAttribute("databaseId");
      if (databaseIdMatchesCurrent(id, databaseId, skRequiredDatabaseId)) {
        parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver, databaseId);
      }
    }
  }
  //
  public void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver, String databaseId) {
    String resultType = nodeToHandle.getStringAttribute("resultType");
    Class<?> resultTypeClass = resolveClass(resultType);
    StatementType statementType = StatementType.valueOf(nodeToHandle.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    String keyProperty = nodeToHandle.getStringAttribute("keyProperty");
    boolean executeBefore = "BEFORE".equals(nodeToHandle.getStringAttribute("order", "AFTER"));

    //defaults
    boolean useCache = false;
    boolean resultOrdered = false;
    KeyGenerator keyGenerator = new NoKeyGenerator();
    Integer fetchSize = null;
    Integer timeout = null;
    boolean flushCache = false;
    String parameterMap = null;
    String resultMap = null;
    ResultSetType resultSetTypeEnum = null;
    //解析生成主鍵的sql語句
    SqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass);
    SqlCommandType sqlCommandType = SqlCommandType.SELECT;
    //生成MappedStatement 物件,放入configuration物件的mappedStatements map中
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, null, databaseId, langDriver);

    id = builderAssistant.applyCurrentNamespace(id, false);

    MappedStatement keyStatement = configuration.getMappedStatement(id, false);
    //同時將這個MappedStatement 物件放入到configuration的 keyGenerator map中
    configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore));
    nodeToHandle.getParent().getNode().removeChild(nodeToHandle.getNode());
  }

下篇會主要看看<resultMap>的解析和sqlSource的生成。

相關推薦

Mybatis 程式碼流程實現原理解析()

接上篇, 這篇繼續分析XMLMapperBuilder.parse()裡的configurationElement() 這個方法。 private void configurationElement(XNode context) { try { //m

log4j2 自動刪除過期日誌檔案配置實現原理解析

  日誌檔案自動刪除功能必不可少,當然你可以讓運維去做這事,只是這不地道。而日誌元件是一個必備元件,讓其多做一件刪除的工作,無可厚非。本文就來探討下 log4j 的日誌檔案自動刪除實現吧。 0. 自動刪除配置參考樣例: (log4j2.xml) <?xml version="1.0" enc

TCP/IP協議的次握手實現原理

簡單 查找 32位 端口 包括 弱點 建立 成功 有效 TCP/IP是很多的不同的協議組成,實際上是一個協議組,TCP用戶數據報表協議(也稱作TCP傳輸控制協議,Transport Control Protocol。可靠的主機到主機層協議。這裏要先強調一下,傳輸控制協議是O

mybatis介面程式設計原理解析

一、引言 前面兩篇文章比較詳細的介紹了mybatis介面程式設計實現的原理,連結地址: https://blog.csdn.net/Wenlong_L/article/details/82942831 https://blog.csdn.net/Wenlong_L/article/det

Struts2框架實現原理和工作流程

Struts2為提供了一個為使用者快速構建應用程式的平臺。 Struts2是基於OpenSymphony的網路工程框架。 Struts2實現模型檢視控制器(MVC)設計模式。 在Struts2的模型、檢視和控制器中分別實現了Action,result和FilterDispa

Shiro許可權管理框架():Shiro中許可權過濾器的初始化流程實現原理

本篇是Shiro系列第三篇,Shiro中的過濾器初始化流程和實現原理。Shiro基於URL的許可權控制是通過Filter實現的,本篇從我們注入的ShiroFilterFactoryBean開始入手,翻看原始碼追尋Shiro中的過濾器的實現原理。 初始化流程 ShiroFilterFactoryBean實現

數據庫水平切分(拆庫拆表)的實現原理解析(轉)

數字 一個數據庫 java ins 結果 都對 不同 com 嚴重 第1章 引言 隨著互聯網應用的廣泛普及,海量數據的存儲和訪問成為了系統設計的瓶頸問題。對於一個大型的互聯網應用,每天幾十億的PV無疑對數據庫造成了相當 高的負載。對於系統的穩定性和擴展性造成了極大的問題。

【dubbo基礎】dubbo學習過程、使用經驗分享實現原理簡單介紹

multi spring配置 不同 影響 為什麽 exception 同事 sock services 一、前言 部門去年年中開始各種改造,第一步是模塊服務化,這邊初選dubbo試用在一些非重要模塊上,慢慢引入到一些稍微重要的功能上,半年時間,學習過程及線上使用遇到的些問

Spring技術內幕:Spring AOP的實現原理

dede ide configure ida mini == src min dem 生成SingleTon代理對象在getSingleTonInstance方法中完畢,這種方法時ProxyFactoryBean生成AopProxy對象的入口。代理對象會

數據庫水平切分的實現原理解析——分庫,分表,主從,集群,負載均衡器(轉)

支付 讀取 dba 我們 課題研究 穩定性 存在 use 根據 第1章 引言 隨著互聯網應用的廣泛普及,海量數據的存儲和訪問成為了系統設計的瓶頸問題。對於一個大型的互聯網應用,每天幾十億的PV無疑對數據庫造成了相當高的負載。對於系統的穩定性和擴展性造成了極大的問題。通過數

EJB2.0教程 詳解EJB技術實現原理

tee nsa 普通 事情 println 配置 ransac 教程 聲明 EJB是什麽呢?EJB是一個J2EE體系中的組件.再簡單的說它是一個能夠遠程調用的javaBean.它同普通的javaBean有兩點不同.第一點,就是遠程調用.第二點,就是事務的功能,我們在EJB中

dubbo學習過程、使用經驗分享實現原理簡單介紹

sum 使用 相同 應該 lib blog 組合 功能模塊 返回 一、前言 部門去年年中開始各種改造,第一步是模塊服務化,這邊初選dubbo試用在一些非重要模塊上,慢慢引入到一些稍微重要的功能上,半年時間,學習過程及線上使用遇到的些問題在此總結下。 整理這篇文章差不多花

ThreadLocal的使用場景實現原理

局部變量 運行 內部 然而 cal private 中間 pub new t 1. 什麽是ThreadLocal? 線程局部變量(通常,ThreadLocal變量是private static修飾的,此時ThreadLocal變量相當於成為了線程內部的全局變量) 2. 使用

C++函式模板實現原理

    C++為我們提供了函式模板機制。所謂函式模板,實際上是建立一個通用函式,其函式型別和形參型別不具體指定,用一個虛擬的型別來代表。這個通用函式就稱為函式模板。     凡是函式體相同的函式都可以用這個模板來代替,不必定義多個函式,只需在模板中定義

python學習之列表物件實現原理解析

l=[1,2,3] id(l[0]) 1652911120 id(l[1]) 1652911152 id(l[2]) 1652911184

.NetCore 中介軟體之AddAuthentication服務說明實現原理簡述

  如果你使用過.NetCore開發過程序,你會很清楚,在其中我們經常會用到一些如下的程式碼 services.AddAuthentication(options => { options.DefaultAuthentic

Mysql MVCC實現原理解析

  MVCC(Multi-Version Concurrency Control | 多版本併發控制)  InnoDB通過為每一行記錄新增兩個額外的隱藏的值來實現MVCC,這兩個值一個記錄這行資料何時被建立,另外一個記錄這行資料何時過期(或者被刪除)。但是InnoDB

JSP工作流程執行原理

JSP起源 在很多動態網頁中,絕大部分內容都是固定不變的,只有區域性內容需要動態產生和改變。 如果使用Servlet程式來輸出只有區域性內容需要動態改變的網頁,其中所有的靜態內容也需要程式設計師用Java程式程式碼產生,整個Servlet程式的程式碼將非常臃腫,編寫和維護都將非常困難。 對大

Linux inotify功能實現原理

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Oracle Redo log 狀態工作原理解析

Oracle重做日誌(redo log)是用來記錄操作條目,用於資料庫資料恢復。為了提高效率,oracle通常建議設定三組redo log。本文將對重做日誌組的狀態以及多種狀態之間切換做解析,力求掌握該知識點。  概述 oracle重做日誌組通常有四種狀態,即unused,inactive,ac