淺談Mybatis連接原理


       眾所周知數據庫連接的過程,但是最近面試的人(菜面菜),都說用的SSM框架,但是我問了一下,mybatis是怎么連接上mysql的,基本上都會說:配置好的,直接用了,今天我來拋磚引玉一下,歡迎拍磚!

       什么是JDBC?

       Java語言訪問數據庫的一種規范,是一套API。JDBC (Java Database Connectivity) API,即Java數據庫編程接口,是一組標准的Java語言中的接口和類,使用這些接口和類,Java客戶端程序可以訪問各種不同類型的數據庫。JDBC規范采用接口和實現分離的思想設計了Java數據庫編程的框架。接口包含在java.sql及javax.sql包中,其中java.sql屬於JavaSE,javax.sql屬於JavaEE。為了使客戶端程序獨立於特定的數據庫驅動程序,JDBC規范建議開發者使用基於接口的編程方式,即盡量使應用僅依賴java.sql及javax.sql中的接口和類。

        JAVA使用JDBC訪問數據庫的步驟:

        1.得到數據庫驅動程序

        2.創建數據庫連接

        3.執行SQL語句

        4.得到結果集

        5.對結果集做相應的處理(增,刪,改,查)

        6.關閉資源:這里釋放的是DB中的資源

        mysql的驅動包提供了java.sql.Driver這個SPI的實現,實現類是com.mysql.jdbc.Driver,在mysql-connector-java-5.1.6.jar中,我們可以看到有一個META-INF/services目錄,目錄下有一個文件名為java.sql.Driver的文件,其中的內容是com.mysql.jdbc.Driver。
在運行DriverManager.getDriver並傳入參數“com.mysql.jdbc.Driver”時,DriverManager會從mysql-connector-java-5.1.6.jar中找到com.mysql.jdbc.Driver並實例化返回一個com.mysql.jdbc.Driver的實例。而SPI(Service Provider Interface)是指一些提供給你繼承、擴展,完成自定義功能的類、接口或者方法。

       SPI是一種回調的思想,回調是指我們在使用api時,我們可以向API傳入一個類或者方法,API在合適的時間調用類或者方法。SPI是在一些通用的標准中,為標准的實現產商提供的擴展點。標准在上層提供API,API內部使用了SPI,當API被客戶使用時,會動態得從當前運行的classpath中尋找該SPI的實現,然后使用該SPI的實現來完成API的功能。
       SPI的實現方式是:提供實現的實現類打包成Jar文件,這個Jar文件里面必須有META-INF目錄,其下又有services目錄,其下有一個文本文件,文件名即為SPI接口的全名,文件的內容該jar包中提供的SPI接口的實現類名。
       大家看項目中Mybaits的jar包會發現:
private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

      sqlSessionTemplate.SqlSessionInterceptor源碼,有沒有一種很熟悉的感覺?至於getConnection自己去看;用過ElasticSearch和Redis的童鞋,細心的童鞋會發現連接字符串都大同小異,連接都是類似的,標准的連接方式,提高效率,有效控制連接;

      ElasticSearch的連接字符串:

protected SearchResponse getSearchResponse(String fieldName, String indexName) {
    client = null;
    SearchResponse response = null;
    try {
        getClient();
        MaxAggregationBuilder aggregation =
                AggregationBuilders
                        .max("agg")
                        .field(fieldName);
        SearchRequestBuilder request = client.prepareSearch(indexName).addAggregation(aggregation);
        response = request.execute().actionGet();
    } catch (Exception ex) {
        logger.error("getSearchResponse", ex);
    } finally {
        if (client != null) {
            client.close();
        }
        return response;
    }
}

     Jedis連接字符串:

執行命令如下:
Jedis jedis = null;
try {
    jedis = jedisPool.getResource();
    //具體的命令
    jedis.executeCommand()
} catch (Exception e) {
    logger.error("op key {} error: " + e.getMessage(), key, e);
} finally {
    //注意這里不是關閉連接,在JedisPool模式下,Jedis會被歸還給資源池。
    if (jedis != null) 
        jedis.close(); 
}

       攔截器的實現都是基於代理的設計模式實現的,簡單的說就是要創造一個目標類的代理類,在代理類中執行目標類的方法並在方法之前執行攔截器代碼,攔截器一般有登陸攔截器——驗證會話信息,權限攔截器——驗證權限信息,那么SqlSessionInterceptor是干什么的?

       Mybatis攔截器設計的一個初衷就是為了供用戶在某些時候可以實現自己的邏輯而不必去動Mybatis固有的邏輯。打個比方,對於Executor,Mybatis中有幾種實現:BatchExecutor、ReuseExecutor、SimpleExecutor和CachingExecutor。
這個時候如果你覺得這幾種實現對於Executor接口的query方法都不能滿足你的要求,那怎么辦呢?是要去改源碼嗎?當然不。我們可以建立一個Mybatis攔截器用於攔截Executor接口的query方法,在攔截之后實現自己的query方法邏輯,之后可以選擇是否繼續執行原來的query方法。允許你在已映射語句執行過程中的某一點進行攔截調用。有的用Mybatis攔截器統封裝分頁,有的用它實現讀寫分離等,如果讀寫分離還是建議配置多數據源;

       spring整合mybatis之后,通過動態代理的方式,使用SqlSessionTemplate持有的sqlSessionProxy屬性來代理執行sql操作,由spring管理的sqlSeesion在sql方法(增刪改查等操作)執行完畢后就自行關閉了sqlSession,不需要我們對其進行手動關閉。

       願你有情人終成眷屬,願你有個有趣的靈魂,願你拍我一磚!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM