dbcp source code analysis-get the connection object

dbcp source code analysis-get the connection object

Entry-BasicDataSource

When executing sql, it will get the database connection first. Where can I get it? right here.

/**
 * Create (if necessary) and return a connection to the database.
 *
 * @throws SQLException if a database access error occurs
 * @return a database connection
 */
public Connection getConnection() throws SQLException {
    return createDataSource().getConnection();
}
 

Get the core class of the connection

Core steps
1. Get the connection object from the connection pool
2. Verify that the connection is valid


Source code

GenericObjectPool

public T borrowObject() throws Exception {
  long starttime = System.currentTimeMillis();
  GenericObjectPool.Latch<T> latch = new GenericObjectPool.Latch();
  byte whenExhaustedAction;
  long maxWait;

//
  synchronized(this) {
    whenExhaustedAction = this._whenExhaustedAction;
    maxWait = this._maxWait;
    this._allocationQueue.add(latch);
  }

//
  this.allocate();

  while(true) {
    synchronized(this) {
      this.assertOpen();
    }

    if (latch.getPair() == null && !latch.mayCreate()) {
      switch(whenExhaustedAction) {
      case 0:
        synchronized(this) {
          if (latch.getPair() == null && !latch.mayCreate()) {
            this._allocationQueue.remove(latch);
            throw new NoSuchElementException("Pool exhausted");
          }
          break;
        }
      case 1:
        try {
          synchronized(latch) {
            if (latch.getPair() != null || latch.mayCreate()) {
              break;
            }

            if (maxWait <= 0L) {
              latch.wait();
            } else {
              long elapsed = System.currentTimeMillis() - starttime;
              long waitTime = maxWait - elapsed;
              if (waitTime > 0L) {
                latch.wait(waitTime);
              }
            }
          }

          if (this.isClosed()) {
            throw new IllegalStateException("Pool closed");
          }
        } catch (InterruptedException var51) {
          boolean doAllocate = false;
          synchronized(this) {
            if (latch.getPair() == null && !latch.mayCreate()) {
              this._allocationQueue.remove(latch);
            } else if (latch.getPair() == null && latch.mayCreate()) {
              --this._numInternalProcessing;
              doAllocate = true;
            } else {
              --this._numInternalProcessing;
              ++this._numActive;
              this.returnObject(latch.getPair().getValue());
            }
          }

          if (doAllocate) {
            this.allocate();
          }

          Thread.currentThread().interrupt();
          throw var51;
        }

        if (maxWait <= 0L || System.currentTimeMillis() - starttime < maxWait) {
          continue;
        }

        synchronized(this) {
          if (latch.getPair() != null || latch.mayCreate()) {
            break;
          }

          this._allocationQueue.remove(latch);
        }

        throw new NoSuchElementException("Timeout waiting for idle object");
      case 2:
        synchronized(this) {
          if (latch.getPair() == null && !latch.mayCreate()) {
            this._allocationQueue.remove(latch);
            ++this._numInternalProcessing;
          }
          break;
        }
      default:
        throw new IllegalArgumentException("WhenExhaustedAction property " + whenExhaustedAction + " not recognized.");
      }
    }

    boolean newlyCreated = false;
    if (null == latch.getPair()) {
      boolean var36 = false;

      try {
        var36 = true;
        T obj = this._factory.makeObject();
        latch.setPair(new ObjectTimestampPair(obj));
        newlyCreated = true;
        var36 = false;
      } finally {
        if (var36) {
          if (!newlyCreated) {
            synchronized(this) {
              --this._numInternalProcessing;
            }

            this.allocate();
          }

        }
      }

      if (!newlyCreated) {
        synchronized(this) {
          --this._numInternalProcessing;
        }

        this.allocate();
      }
    }


    try {
      this._factory.activateObject(latch.getPair().value);//
      if (this._testOnBorrow//false && !this._factory.validateObject(latch.getPair().value)) {//_testOnBorrow=false
        throw new Exception("ValidateObject failed");
      }

      synchronized(this) {
        --this._numInternalProcessing;//1
        ++this._numActive;//1
      }

      return latch.getPair().value;//
    } catch (Throwable var47) {
      PoolUtils.checkRethrow(var47);

      try {
        this._factory.destroyObject(latch.getPair().value);
      } catch (Throwable var42) {
        PoolUtils.checkRethrow(var42);
      }

      synchronized(this) {
        --this._numInternalProcessing;
        if (!newlyCreated) {
          latch.reset();
          this._allocationQueue.add(0, latch);
        }
      }

      this.allocate();
      if (newlyCreated) {
        throw new NoSuchElementException("Could not create a validated object, cause: " + var47.getMessage());
      }
    }
  }
}
 

PoolableConnectionFactory

Activate the connection object, why do I need to activate? Because the connection pool in the actual project is to turn off auto-commit, that is,
1. Start the transaction//At the beginning, the auto-commit is turned off
2.
sql1
3. sql2 4. Commit the transaction//When the transaction is finally submitted, it will be set to automatic submit

protected boolean _defaultAutoCommit = true;//

public void activateObject(Object obj) throws Exception {
    if (obj instanceof DelegatingConnection) {
      ((DelegatingConnection)obj).activate();
    }

    if (obj instanceof Connection) {
      Connection conn = (Connection)obj;
      if (conn.getAutoCommit()//false != this._defaultAutoCommit//true) {
        conn.setAutoCommit(this._defaultAutoCommit);//
      }

      if (this._defaultTransactionIsolation != -1 && conn.getTransactionIsolation() != this._defaultTransactionIsolation) {
        conn.setTransactionIsolation(this._defaultTransactionIsolation);
      }

      if (this._defaultReadOnly != null && conn.isReadOnly() != this._defaultReadOnly) {
        conn.setReadOnly(this._defaultReadOnly);
      }

      if (this._defaultCatalog != null && !this._defaultCatalog.equals(conn.getCatalog())) {
        conn.setCatalog(this._defaultCatalog);
      }
    }

  }
 

Get the connection object from the connection pool

It is this method that really gets the connection object

private CursorableLinkedList<ObjectTimestampPair<T>> _pool;//LinkedList

//
private synchronized void allocate() {
  if (!this.isClosed()) {
    GenericObjectPool.Latch latch;//
    while(!this._pool.isEmpty() && !this._allocationQueue.isEmpty()) {
      latch = (GenericObjectPool.Latch)this._allocationQueue.removeFirst();//
      latch.setPair((ObjectTimestampPair)this._pool.removeFirst());//
      ++this._numInternalProcessing;
      synchronized(latch) {
        latch.notify();
      }
    }

    while(!this._allocationQueue.isEmpty() && (this._maxActive < 0 || this._numActive + this._numInternalProcessing < this._maxActive)) {
      latch = (GenericObjectPool.Latch)this._allocationQueue.removeFirst();
      latch.setMayCreate(true);
      ++this._numInternalProcessing;
      synchronized(latch) {
        latch.notify();
      }
    }

  }
}
 

Verify that the connection is valid

public boolean validateObject(Object obj) {
  if (obj instanceof Connection) {
    try {
      this.validateConnection((Connection)obj);//
      return true;//true
    } catch (Exception var3) {
      return false;//false
    }
  } else {
    return false;
  }
}
 
public void validateConnection(Connection conn) throws SQLException {
  String query = this._validationQuery;
  if (conn.isClosed()) {//
    throw new SQLException("validateConnection: connection closed");
  } else {//
    if (null != query) {
      Statement stmt = null;
      ResultSet rset = null;

      try {
        stmt = conn.createStatement();
        if (this._validationQueryTimeout > 0) {
          stmt.setQueryTimeout(this._validationQueryTimeout);
        }

        rset = stmt.executeQuery(query);
        if (!rset.next()) {
          throw new SQLException("validationQuery didn't return a row");
        }
      } finally {
        if (rset != null) {
          try {
            rset.close();
          } catch (Exception var14) {
          }
        }

        if (stmt != null) {
          try {
            stmt.close();
          } catch (Exception var13) {
          }
        }

      }
    }

  }
}
 

data structure

Connection object

Encapsulates the connection object

private static final class Latch<T> {
  private ObjectTimestampPair<T> _pair;
 
static class ObjectTimestampPair<T> implements Comparable<T> {
 /** @deprecated */
  @Deprecated
  T value;//

 /** @deprecated */
  @Deprecated
  long tstamp;
 

connection pool

private CursorableLinkedList<ObjectTimestampPair<T>> _pool;//
 

performance

When the connection is acquired, there is a lock

private synchronized void allocate() {//
  if (!this.isClosed()) {
    GenericObjectPool.Latch latch;
    while(!this._pool.isEmpty() && !this._allocationQueue.isEmpty()) {
      latch = (GenericObjectPool.Latch)this._allocationQueue.removeFirst();
      latch.setPair((ObjectTimestampPair)this._pool.removeFirst());
      ++this._numInternalProcessing;
      synchronized(latch) {
        latch.notify();
      }
    }

    while(!this._allocationQueue.isEmpty() && (this._maxActive < 0 || this._numActive + this._numInternalProcessing < this._maxActive)) {
      latch = (GenericObjectPool.Latch)this._allocationQueue.removeFirst();
      latch.setMayCreate(true);
      ++this._numInternalProcessing;
      synchronized(latch) {
        latch.notify();
      }
    }

  }
}
 

Screenshot

1. Get the connection object

How to actually get the connection object

2. Verify that the connection is valid

dbcp and pool

dbcp is based on pool, and the underlying connection pool is implemented by pool.

Call stack

borrowObject:1059, GenericObjectPool (org.apache.commons.pool.impl)//
getConnection:106, PoolingDataSource (org.apache.commons.dbcp)//
getConnection:1044, BasicDataSource (org.apache.commons.dbcp)//
getConnection:26, DbcpSecretBasicDataSource (xxx.qrcode.common.monitor.dbcp)//

getConnection:83, LocalDataSourceConnectionProvider (org.springframework.orm.hibernate3)

openConnection:446, ConnectionManager (org.hibernate.jdbc)
getConnection:167, ConnectionManager (org.hibernate.jdbc)
connection:160, JDBCContext (org.hibernate.jdbc)
begin:81, JDBCTransaction (org.hibernate.transaction)
beginTransaction:1473, SessionImpl (org.hibernate.impl)

doBegin:556, HibernateTransactionManager (org.springframework.orm.hibernate3)//
getTransaction:372, AbstractPlatformTransactionManager (org.springframework.transaction.support)//
createTransactionIfNecessary:417, TransactionAspectSupport (org.springframework.transaction.interceptor)
invokeWithinTransaction:255, TransactionAspectSupport (org.springframework.transaction.interceptor)
invoke:94, TransactionInterceptor (org.springframework.transaction.interceptor)
proceed:172, ReflectiveMethodInvocation (org.springframework.aop.framework)
invoke:91, ExposeInvocationInterceptor (org.springframework.aop.interceptor)
proceed:172, ReflectiveMethodInvocation (org.springframework.aop.framework)
intercept:631, CglibAopProxy$DynamicAdvisedInterceptor (org.springframework.aop.framework)
merModifyNoAudit:-1, UrmtminfService$$EnhancerByCGLIB$$c1377819 (xxx.qrcode.register.dbservice.impl)
merModifyNoAudit:517, QrcodeRegisterServer (xxx.qrcode.register.remoteserver.impl)
invokeMethod:-1, Wrapper22 (com.alibaba.dubbo.common.bytecode)
doInvoke:47, JavassistProxyFactory$1 (com.alibaba.dubbo.rpc.proxy.javassist)
invoke:76, AbstractProxyInvoker (com.alibaba.dubbo.rpc.proxy)
invoke:52, DelegateProviderMetaDataInvoker (com.alibaba.dubbo.config.invoker)
invoke:56, InvokerWrapper (com.alibaba.dubbo.rpc.protocol)
invoke:62, ExceptionFilter (com.alibaba.dubbo.rpc.filter)
invoke:72, ProtocolFilterWrapper$1 (com.alibaba.dubbo.rpc.protocol)
invoke:75, MonitorFilter (com.alibaba.dubbo.monitor.support)
invoke:72, ProtocolFilterWrapper$1 (com.alibaba.dubbo.rpc.protocol)
invoke:42, TimeoutFilter (com.alibaba.dubbo.rpc.filter)
invoke:72, ProtocolFilterWrapper$1 (com.alibaba.dubbo.rpc.protocol)
invoke:11, AppNameAppendFilter (com.xxx.log.rpc.dubbo)
invoke:72, ProtocolFilterWrapper$1 (com.alibaba.dubbo.rpc.protocol)
invoke:78, TraceFilter (com.alibaba.dubbo.rpc.protocol.dubbo.filter)
invoke:72, ProtocolFilterWrapper$1 (com.alibaba.dubbo.rpc.protocol)
invoke:60, ExecuteLimitFilter (com.alibaba.dubbo.rpc.filter)
invoke:72, ProtocolFilterWrapper$1 (com.alibaba.dubbo.rpc.protocol)
invoke:85, AccessLogExtFilter (xxx.qrcode.common.filter.dubbo)
invoke:72, ProtocolFilterWrapper$1 (com.alibaba.dubbo.rpc.protocol)
invoke:82, CatTransaction (com.xxx.log.rpc.dubbo)
invoke:72, ProtocolFilterWrapper$1 (com.alibaba.dubbo.rpc.protocol)
invoke:73, ContextFilter (com.alibaba.dubbo.rpc.filter)
invoke:72, ProtocolFilterWrapper$1 (com.alibaba.dubbo.rpc.protocol)
invoke:138, GenericFilter (com.alibaba.dubbo.rpc.filter)
invoke:72, ProtocolFilterWrapper$1 (com.alibaba.dubbo.rpc.protocol)
invoke:38, ClassLoaderFilter (com.alibaba.dubbo.rpc.filter)
invoke:72, ProtocolFilterWrapper$1 (com.alibaba.dubbo.rpc.protocol)
invoke:38, EchoFilter (com.alibaba.dubbo.rpc.filter)
invoke:72, ProtocolFilterWrapper$1 (com.alibaba.dubbo.rpc.protocol)
reply:104, DubboProtocol$1 (com.alibaba.dubbo.rpc.protocol.dubbo)
handleRequest:96, HeaderExchangeHandler (com.alibaba.dubbo.remoting.exchange.support.header)
received:173, HeaderExchangeHandler (com.alibaba.dubbo.remoting.exchange.support.header)
received:51, DecodeHandler (com.alibaba.dubbo.remoting.transport)
run:57, ChannelEventRunnable (com.alibaba.dubbo.remoting.transport.dispatcher)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:748, Thread (java.lang)