欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

MyBatis源碼淺析-創(chuàng)新互聯(lián)

什么是MyBatis

成都服務(wù)器托管,成都創(chuàng)新互聯(lián)提供包括服務(wù)器租用、中國(guó)電信成都樞紐中心、帶寬租用、云主機(jī)、機(jī)柜租用、主機(jī)租用托管、CDN網(wǎng)站加速、域名與空間等業(yè)務(wù)的一體化完整服務(wù)。電話咨詢:028-86922220

MyBatis是支持定制化SQL、存儲(chǔ)過(guò)程以及高級(jí)映射的優(yōu)秀的持久層框架。MyBatis 避免了幾乎所有的 JDBC 代碼和手工設(shè)置參數(shù)以及抽取結(jié)果集。MyBatis 使用簡(jiǎn)單的 XML 或注解來(lái)配置和映射基本體,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對(duì)象)映射成數(shù)據(jù)庫(kù)中的記錄。

MyBatis簡(jiǎn)單示例

雖然在使用MyBatis時(shí)一般都會(huì)使用XML文件,但是本文為了分析程序的簡(jiǎn)單性,簡(jiǎn)單的測(cè)試程序?qū)⒉话琗ML配置,該測(cè)試程序包含一個(gè)接口、一個(gè)啟動(dòng)類:

public interface UserMapper {   @Select("SELECT * FROM user WHERE id = #{id}")  User selectUser(int id); }public class Test2 { public static void main(String[] args) { SqlSessionFactory sqlSessionFactory = initSqlSessionFactory(); SqlSession session = sqlSessionFactory.openSession(); try { User user = (User) session.selectOne( "org.mybatis.example.UserMapper.selectUser", 1); System.out.println(user.getUserAddress()); System.out.println(user.getUserName()); } finally { session.close(); } } private static SqlSessionFactory initSqlSessionFactory() { DataSource dataSource = new PooledDataSource("com.mysql.jdbc.Driver", "jdbc:mysql://127.0.0.1:3306/jdbc", "root", ""); TransactionFactory transactionFactory = new JdbcTransactionFactory(); Environment environment = new Environment("development", transactionFactory, dataSource); Configuration configuration = new Configuration(environment); configuration.addMapper(UserMapper.class); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder() .build(configuration); return sqlSessionFactory; } }

UserMapper是一個(gè)接口,我們?cè)跇?gòu)建sqlSessionFactory時(shí)通過(guò)configuration.addMapper(UserMapper.class)把該接口注冊(cè)進(jìn)了sqlSessionFactory中。從上面的代碼中我們可以看出,要使用MyBatis,我們應(yīng)該經(jīng)過(guò)以下步驟:1、創(chuàng)建sqlSessionFactory(一次性操作);2、用sqlSessionFactory對(duì)象構(gòu)造sqlSession對(duì)象;3、調(diào)用sqlSession的相應(yīng)方法;4、關(guān)閉sqlSession對(duì)象。

在main方法中,我們沒(méi)有配置sql,也沒(méi)有根據(jù)查詢結(jié)果拼接對(duì)象,只需在調(diào)用sqlSession方法時(shí)傳入一個(gè)命名空間以及方法參數(shù)參數(shù)即可,所有的操作都是面向?qū)ο蟮摹T赨serMapper接口中,我們定制了自己的sql,MyBatis把書(shū)寫(xiě)sql的權(quán)利給予了我們,方便我們進(jìn)行sql優(yōu)化及sql排錯(cuò)。

JDBC基礎(chǔ)回顧

直接使用JDBC是很痛苦的,JDBC連接數(shù)據(jù)庫(kù)包含以下幾個(gè)基本步驟:1、注冊(cè)驅(qū)動(dòng) ;2、建立連接(Connection);3、創(chuàng)建SQL語(yǔ)句(Statement);4、執(zhí)行語(yǔ)句;5、處理執(zhí)行結(jié)果(ResultSet);6、釋放資源,示例代碼如下:

   test()  SQLException{    .forName();    Connection conn = DriverManager.getConnection(, , );    Statement st = conn.createStatement();    ResultSet rs = st.executeQuery();     (rs.()) {       User user =  User(rs.getObject(), rs.getObject());  }    rs.close();  st.close();  conn.close(); }

可以看到與直接使用JDBC相比,MyBatis為我們簡(jiǎn)化了很多工作:

1、把創(chuàng)建連接相關(guān)工作抽象成一個(gè)sqlSessionFactory對(duì)象,一次創(chuàng)建多次使用;

2、把sql語(yǔ)句從業(yè)務(wù)層剝離,代碼邏輯更加清晰,增加可維護(hù)性;

3、自動(dòng)完成結(jié)果集處理,不需要我們編寫(xiě)重復(fù)代碼。

但是,我們應(yīng)該知道的是,框架雖然能夠幫助我們簡(jiǎn)化工作,但是框架底層的代碼肯定還是最基礎(chǔ)的JDBC代碼,因?yàn)檫@是Java平臺(tái)連接數(shù)據(jù)庫(kù)的通用方法,今天我將分析一下MyBatis源碼,看看MyBatis是如何把這些基礎(chǔ)代碼封裝成一個(gè)框架的。

MyBatis調(diào)用流程

我們最終調(diào)用的是sqlSession對(duì)象上的方法,所以我們先跟蹤sqlSession的創(chuàng)建方法:sqlSessionFactory.openSession(),最終這個(gè)方法會(huì)調(diào)用到DefaultSqlSessionFactory的以下方法:

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

最終返回的對(duì)象是一個(gè)DefaultSqlSession對(duì)象,在調(diào)試模式下, 我們看到autoCommit為false,executor為CachingExecutor類型,在CachingExecutor里面有屬性delegate,其類型為simpleExecutor:

MyBatis源碼淺析

現(xiàn)在,我們跟進(jìn)DefaultSqlSession的selectOne()方法,查看該方法的調(diào)用流程,selectOne()方法又會(huì)調(diào)用selectList()方法:

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {    try {       MappedStatement ms = configuration.getMappedStatement(statement);      List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);      return result;     } catch (Exception e) {      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);     } finally {       ErrorContext.instance().reset();     }   }

可以看到要得到查詢結(jié)果,最終還是要調(diào)用executor上的query方法,這里的executor是CachingExecutor實(shí)例,跟進(jìn)程序得到如下代碼:

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {     BoundSql boundSql = ms.getBoundSql(parameterObject);     CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);   }  public <E> List<E> 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<E> list = (List<E>) tcm.getObject(cache, key);        if (list == null) {          list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);           tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks         }        return list;       }     }    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);   }

MyBatis框架首先生成了一個(gè)boundSql和CacheKey,在boundSql中包含有我們傳入的sql語(yǔ)句:

MyBatis源碼淺析

生成boundSql和CacheKey后會(huì)調(diào)用一個(gè)重載函數(shù),在重載函數(shù)中,我們會(huì)檢測(cè)是否有緩存,這個(gè)緩存是MyBatis的二級(jí)緩存,我們沒(méi)有配置,那么直接調(diào)用最后一句delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql),前面說(shuō)過(guò)這個(gè)delagate其實(shí)就是simpleExecutor,跟進(jìn)去查看一下:

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {     ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());    if (closed) throw new ExecutorException("Executor was closed.");    if (queryStack == 0 && ms.isFlushCacheRequired()) {       clearLocalCache();     }    List<E> list;    try {       queryStack++;      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;      if (list != null) {         handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);       } else {        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);       }     } finally {       queryStack--;     }    if (queryStack == 0) {      for (DeferredLoad deferredLoad : deferredLoads) {         deferredLoad.load();       }       deferredLoads.clear(); // issue #601       if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {         clearLocalCache(); // issue #482       }     }    return list;   }

關(guān)鍵代碼是以下三行:

list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;      if (list != null) {         handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);       } else {        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);       }

首先嘗試從localCache中根據(jù)key得到List,這里的localCache是MyBatis的一級(jí)緩存,如果得不到則調(diào)用queryFromDatabase()從數(shù)據(jù)庫(kù)中查詢:

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {    List<E> list;     localCache.putObject(key, EXECUTION_PLACEHOLDER);    try {      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);     } finally {       localCache.removeObject(key);     }     localCache.putObject(key, list);    if (ms.getStatementType() == StatementType.CALLABLE) {       localOutputParameterCache.putObject(key, parameter);     }    return list;   }

其中關(guān)鍵代碼是調(diào)用doQuery()代碼,SimpleExecutor的doQuery()方法如下:

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();       StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);       stmt = prepareStatement(handler, ms.getStatementLog());      return handler.<E>query(stmt, resultHandler);     } finally {       closeStatement(stmt);     }   }

調(diào)用了prepareStatement方法,該方法如下:

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

終于,我們看到熟悉的代碼了,首先得到Connection,然后從Connection中得到Statement,同時(shí)在調(diào)試模式下我們看到,我們的sql語(yǔ)句已經(jīng)被設(shè)置到stmt中了:

MyBatis源碼淺析

現(xiàn)在Statement對(duì)象有了,sql也設(shè)置進(jìn)去了,就只差執(zhí)行以及對(duì)象映射了,繼續(xù)跟進(jìn)代碼,我們會(huì)跟蹤到org.apache.ibatis.executor.statement.

PreparedStatementHandler類的executor方法:

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

在這里,調(diào)用了ps.execute()方法執(zhí)行sql,接下來(lái)調(diào)用的resultSetHandler.<E> handleResultSets(ps)方法明顯是對(duì)結(jié)果集進(jìn)行封裝,我就不繼續(xù)跟進(jìn)了。

MyBatis的數(shù)據(jù)庫(kù)連接池

上面一部分介紹了MyBatis執(zhí)行的整體流程,這一部分打算討論一個(gè)具體話題:MyBatis的數(shù)據(jù)庫(kù)連接池。

我們知道,每次連接數(shù)據(jù)庫(kù)時(shí)都創(chuàng)建Connection是十分耗費(fèi)性能的,所以我們?cè)趯?xiě)JDBC代碼時(shí),一般都會(huì)使用數(shù)據(jù)庫(kù)連接池,把用過(guò)的Connection不是直接關(guān)閉,而是放入數(shù)據(jù)庫(kù)連接池中,方便下次復(fù)用,開(kāi)源的數(shù)據(jù)庫(kù)連接池有DBCP、C3P0等,MyBatis也實(shí)現(xiàn)了自己的數(shù)據(jù)庫(kù)連接池,在這一節(jié)我將探索一下MyBatis實(shí)現(xiàn)的數(shù)據(jù)庫(kù)連接池源碼。

跟進(jìn)上一節(jié)的getConnection()方法,我們最終會(huì)進(jìn)入JdbcTransaction的getConnection()方法,getConnection()方法又會(huì)調(diào)用openConnection()方法,而openConnection()又將調(diào)用dataSource的getConnection()方法:

public Connection getConnection() throws SQLException { if (connection == null) { openConnection(); } return connection; }protected void openConnection() throws SQLException { if (log.isDebugEnabled()) { log.debug("Opening JDBC Connection"); } connection = dataSource.getConnection(); if (level != null) { connection.setTransactionIsolation(level.getLevel()); } setDesiredAutoCommit(autoCommmit); }

這里的dataSource是PooledDataSource類型,跟進(jìn)查看源碼如下:

public Connection getConnection() throws SQLException {    return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();   }  private PooledConnection popConnection(String username, String password) throws SQLException {    //暫不分析   }

可以看到,在這里我們返回的對(duì)象其實(shí)已經(jīng)不是原生的Connection對(duì)象了,而是一個(gè)動(dòng)態(tài)代理對(duì)象,是PooledConnection的一個(gè)屬性,所有對(duì)對(duì)Connection對(duì)象的操作都將被PooledConnection攔截,我們可以查看PooledConnection的定義如下:

class PooledConnection implements InvocationHandler { private static final String CLOSE = "close"; private static final Class<?>[] IFACES = new Class<?>[] { Connection.class }; private int hashCode = 0; private PooledDataSource dataSource; private Connection realConnection; private Connection proxyConnection; private long checkoutTimestamp; private long createdTimestamp; private long lastUsedTimestamp; private int connectionTypeCode; private boolean valid; public PooledConnection(Connection connection, PooledDataSource dataSource) { this.hashCode = connection.hashCode(); this.realConnection = connection; this.dataSource = dataSource; this.createdTimestamp = System.currentTimeMillis(); this.lastUsedTimestamp = System.currentTimeMillis(); this.valid = true; this.proxyConnection = (Connection) Proxy.newProxyInstance( Connection.class.getClassLoader(), IFACES, this); } public void invalidate() { valid = false; } public boolean isValid() { return valid && realConnection != null && dataSource.pingConnection(this); } public Connection getRealConnection() { return realConnection; } public Connection getProxyConnection() { return proxyConnection; } public int getRealHashCode() { if (realConnection == null) { return 0; } else { return realConnection.hashCode(); } } public int getConnectionTypeCode() { return connectionTypeCode; } public void setConnectionTypeCode(int connectionTypeCode) { this.connectionTypeCode = connectionTypeCode; } public long getCreatedTimestamp() { return createdTimestamp; } public void setCreatedTimestamp(long createdTimestamp) { this.createdTimestamp = createdTimestamp; } public long getLastUsedTimestamp() { return lastUsedTimestamp; } public void setLastUsedTimestamp(long lastUsedTimestamp) { this.lastUsedTimestamp = lastUsedTimestamp; } public long getTimeElapsedSinceLastUse() { return System.currentTimeMillis() - lastUsedTimestamp; } public long getAge() { return System.currentTimeMillis() - createdTimestamp; } public long getCheckoutTimestamp() { return checkoutTimestamp; } public void setCheckoutTimestamp(long timestamp) { this.checkoutTimestamp = timestamp; } public long getCheckoutTime() { return System.currentTimeMillis() - checkoutTimestamp; } public int hashCode() { return hashCode; } public boolean equals(Object obj) { if (obj instanceof PooledConnection) { return realConnection.hashCode() == (((PooledConnection) obj).realConnection .hashCode()); } else if (obj instanceof Connection) { return hashCode == obj.hashCode(); } else { return false; } } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) { dataSource.pushConnection(this); return null; } else { try { if (!Object.class.equals(method.getDeclaringClass())) { checkConnection(); } return method.invoke(realConnection, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } } private void checkConnection() throws SQLException { if (!valid) { throw new SQLException( "Error accessing PooledConnection. Connection is invalid."); } } }

可以看到這個(gè)類暴露了很多接口檢測(cè)Connection狀態(tài),例如連接是否有效,連接創(chuàng)建時(shí)間最近使用連接等:

MyBatis源碼淺析

這個(gè)類實(shí)現(xiàn)了InvocationHandler接口,最主要的一個(gè)方法如下:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    String methodName = method.getName();    if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {       dataSource.pushConnection(this);      return null;     } else {      try {        if (!Object.class.equals(method.getDeclaringClass())) {           checkConnection();         }        return method.invoke(realConnection, args);       } catch (Throwable t) {        throw ExceptionUtil.unwrapThrowable(t);       }     }   }

可以看到,PooledConnection會(huì)攔截close方法,當(dāng)客戶端調(diào)用close()方法時(shí),程序不會(huì)關(guān)閉Connection,而是會(huì)調(diào)用dataSource.pushConnection(this)方法,該方法的實(shí)現(xiàn)如下:

protected void pushConnection(PooledConnection conn) throws SQLException {     synchronized (state) {       state.activeConnections.remove(conn);      if (conn.isValid()) {        if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {           state.accumulatedCheckoutTime += conn.getCheckoutTime();          if (!conn.getRealConnection().getAutoCommit()) {             conn.getRealConnection().rollback();           }           PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);           state.idleConnections.add(newConn);           newConn.setCreatedTimestamp(conn.getCreatedTimestamp());           newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());           conn.invalidate();          if (log.isDebugEnabled()) {            log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");           }           state.notifyAll();         } else {           state.accumulatedCheckoutTime += conn.getCheckoutTime();          if (!conn.getRealConnection().getAutoCommit()) {             conn.getRealConnection().rollback();           }           conn.getRealConnection().close();          if (log.isDebugEnabled()) {            log.debug("Closed connection " + conn.getRealHashCode() + ".");           }           conn.invalidate();         }       } else {        if (log.isDebugEnabled()) {          log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");         }         state.badConnectionCount++;       }     }   }

可以看到,首先會(huì)把Connection從活躍列表中刪除,然后檢測(cè)空閑列表的長(zhǎng)度有沒(méi)有達(dá)到大長(zhǎng)度(默認(rèn)為5),若沒(méi)有達(dá)到,把Connection放入空閑鏈表,否則關(guān)閉連接。這里的state是一個(gè)PoolState對(duì)象,該對(duì)象定義如下:

public class PoolState {  protected PooledDataSource dataSource;  protected final List<PooledConnection> idleConnections = new ArrayList<PooledConnection>();  protected final List<PooledConnection> activeConnections = new ArrayList<PooledConnection>();  protected long requestCount = 0;  protected long accumulatedRequestTime = 0;  protected long accumulatedCheckoutTime = 0;  protected long claimedOverdueConnectionCount = 0;  protected long accumulatedCheckoutTimeOfOverdueConnections = 0;  protected long accumulatedWaitTime = 0;  protected long hadToWaitCount = 0;  protected long badConnectionCount = 0;  public PoolState(PooledDataSource dataSource) {    this.dataSource = dataSource;   }  public synchronized long getRequestCount() {    return requestCount;   }  public synchronized long getAverageRequestTime() {    return requestCount == 0 ? 0 : accumulatedRequestTime / requestCount;   }  public synchronized long getAverageWaitTime() {    return hadToWaitCount == 0 ? 0 : accumulatedWaitTime / hadToWaitCount;   }  public synchronized long getHadToWaitCount() {    return hadToWaitCount;   }  public synchronized long getBadConnectionCount() {    return badConnectionCount;   }  public synchronized long getClaimedOverdueConnectionCount() {    return claimedOverdueConnectionCount;   }  public synchronized long getAverageOverdueCheckoutTime() {    return claimedOverdueConnectionCount == 0 ? 0 : accumulatedCheckoutTimeOfOverdueConnections / claimedOverdueConnectionCount;   }  public synchronized long getAverageCheckoutTime() {    return requestCount == 0 ? 0 : accumulatedCheckoutTime / requestCount;   }  public synchronized int getIdleConnectionCount() {    return idleConnections.size();   }  public synchronized int getActiveConnectionCount() {    return activeConnections.size();   } }

可以看到最終我們的Connection對(duì)象是放在ArrayList中的,該類還提供一些接口返回連接池基本信息。

好了,現(xiàn)在我們可以回去看看PooledDataSource的popConnection方法了:

private PooledConnection popConnection(String username, String password) throws SQLException {     boolean countedWait = false;     PooledConnection conn = null;    long t = System.currentTimeMillis();    int localBadConnectionCount = 0;    while (conn == null) {       synchronized (state) {        if (state.idleConnections.size() > 0) {          // Pool has available connection           conn = state.idleConnections.remove(0);          if (log.isDebugEnabled()) {            log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");           }         } else {          // Pool does not have available connection           if (state.activeConnections.size() < poolMaximumActiveConnections) {            // Can create new connection             conn = new PooledConnection(dataSource.getConnection(), this);             @SuppressWarnings("unused")            //used in logging, if enabled             Connection realConn = conn.getRealConnection();            if (log.isDebugEnabled()) {              log.debug("Created connection " + conn.getRealHashCode() + ".");             }           } else {            // Cannot create new connection             PooledConnection oldestActiveConnection = state.activeConnections.get(0);            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();            if (longestCheckoutTime > poolMaximumCheckoutTime) {              // Can claim overdue connection               state.claimedOverdueConnectionCount++;               state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;               state.accumulatedCheckoutTime += longestCheckoutTime;               state.activeConnections.remove(oldestActiveConnection);              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {                 oldestActiveConnection.getRealConnection().rollback();               }               conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);               oldestActiveConnection.invalidate();              if (log.isDebugEnabled()) {                log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");               }             } else {              // Must wait               try {                if (!countedWait) {                   state.hadToWaitCount++;                   countedWait = true;                 }                if (log.isDebugEnabled()) {                  log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");                 }                long wt = System.currentTimeMillis();                 state.wait(poolTimeToWait);                 state.accumulatedWaitTime += System.currentTimeMillis() - wt;               } catch (InterruptedException e) {                break;               }             }           }         }        if (conn != null) {          if (conn.isValid()) {            if (!conn.getRealConnection().getAutoCommit()) {               conn.getRealConnection().rollback();             }             conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));             conn.setCheckoutTimestamp(System.currentTimeMillis());             conn.setLastUsedTimestamp(System.currentTimeMillis());             state.activeConnections.add(conn);             state.requestCount++;             state.accumulatedRequestTime += System.currentTimeMillis() - t;           } else {            if (log.isDebugEnabled()) {              log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");             }             state.badConnectionCount++;             localBadConnectionCount++;             conn = null;            if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {              if (log.isDebugEnabled()) {                log.debug("PooledDataSource: Could not get a good connection to the database.");               }              throw new SQLException("PooledDataSource: Could not get a good connection to the database.");             }           }         }       }     }    if (conn == null) {      if (log.isDebugEnabled()) {        log.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");       }      throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");     }    return conn;   }

可以看到獲取Connection一共分以下幾種情況:1、如果有空閑Connection,那么直接使用空閑Connection,否則2;2、如果活躍Connection沒(méi)有達(dá)到活躍Connection的上限,那么創(chuàng)建一個(gè)新Connection并返回,否則3;3、如果達(dá)到活躍上限,且被檢出的Connection檢出時(shí)間過(guò)長(zhǎng),那么把該Connection置為失效,新創(chuàng)建一個(gè)Connection,否則4;4、等待空閑Connection。

至此,我們就把MyBatis的數(shù)據(jù)庫(kù)連接池代碼整理了一遍,其中有兩個(gè)關(guān)鍵點(diǎn):1、檢出的Connection其實(shí)不是原生Connection,而是一個(gè)代理對(duì)象;2、存放Connection的容器是ArrayList,Connection的檢出遵從先進(jìn)先出原則。

這篇博客講的很好,可以mark一下:×××/technology

MyBatis的事務(wù)

首先回顧一下JDBC的事務(wù)知識(shí)。

JDBC可以操作Connection的setAutoCommit()方法,給它false參數(shù),提示數(shù)據(jù)庫(kù)啟動(dòng)事務(wù),在下達(dá)一連串的SQL命令后,自行調(diào)用Connection的commit()方法,提示數(shù)據(jù)庫(kù)確認(rèn)(Commit)操作。如果中間發(fā)生錯(cuò)誤,則調(diào)用rollback(),提示數(shù)據(jù)庫(kù)撤銷(xiāo)(ROLLBACK)所有執(zhí)行。同時(shí),如果僅想要撤回某個(gè)SQL執(zhí)行點(diǎn),則可以設(shè)置存儲(chǔ)點(diǎn)(SAVEPOINT)。一個(gè)示范的事務(wù)流程如下:

Connection conn = ...;Savepoint point = null;try { conn.setAutoCommit(false); Statement stmt = conn.createStatement(); stmt.executeUpdate("INSERT INTO ..."); ... point = conn.setSavepoint(); stmt.executeUpdate("INSERT INTO ..."); ... conn.commit(); } catch (SQLException e) { e.printStackTrace(); if (conn != null) { try { if (point == null) { conn.rollback(); } else { conn.rollback(point); conn.releaseSavepoint(point); } } catch (SQLException ex) { ex.printStackTrace(); } } } finally { ... if (conn != null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException ex) { ex.printStackTrace(); } } }

在MyBatis調(diào)用流程一節(jié)就寫(xiě)過(guò),在調(diào)試模式下, 我們看到autoCommit為false, 所以每個(gè)sqlSession其實(shí)都是一個(gè)事務(wù),這也是為什么每次做刪、改、查時(shí)都必須調(diào)用commit的原因.

源碼來(lái)源:×××/technology

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。

本文題目:MyBatis源碼淺析-創(chuàng)新互聯(lián)
當(dāng)前地址:http://aaarwkj.com/article10/dpidgo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站導(dǎo)航、虛擬主機(jī)、手機(jī)網(wǎng)站建設(shè)、標(biāo)簽優(yōu)化、網(wǎng)站制作、移動(dòng)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁(yè)設(shè)計(jì)
亚洲天堂福利视频网站| 蜜桃臀视频一区二区三区| 国产区一区二区三在线播放| 日韩精品专区中文字幕| 欧美aⅴ精品一区二区三区| 成人做爰片免费看视频| 国产龙熟女高潮一区二区| 日韩免费色视频一区| 成人深夜免费观看视频| 中文字幕亚洲精品乱码在线| 欧美人与性禽动交情品| 很色很污无遮挡的网色污| 小黄片免费在线播放观看| 欧美精品色精品免费观看| 高颜值美女后入内射视频| 四虎永久精品在线视频| 日本午夜福利视频在线观看| 色婷婷亚洲一区二区三区| 欧美一级免费黄片在线播放| 91免费视频精品麻豆| 日本高清加勒比免费在线| 国产成人亚洲合色婷婷| 91欧美精品在线视频| 天堂av一区二区三区| 欧美一区二区日本国产激情| 免费看av网站一区二区| 国产91精品系列在线观看| 亚洲日本国产精品一区| 日韩一二三区欧美四五区新| 久草视频亚洲免费在线| 高清av中文免费在线| 亚洲精品一区二区播放| 欧美午夜福利视频网址| 日本一区二区高清网址| 水蜜桃在线观看一区二区国产| 福利一区二区在线视频| 日本姜女黄视频免费看| 漂亮人妻被中出中文字幕| 亚洲精品一区二区影院| 在线国产视频一区二区三区| 欧美日韩在线一区二区精品|