Mybatis Source-datasource Summary

Keywords: Java Apache SQL

How do the main functions of this package get data source objects, and indirectly get Connections to manipulate databases

1. There are two ways to get a DataSource

1.1. Obtain from JNDI (InitialContext context), jndi's lookup method, get configuration from somewhere to generate a DataSource
1.2. With java code, incoming datasource requires parameters such as user name, password, driver class path, and so on

2. A sketch of the relationship of this package

3. PooledConnection class resolution

package org.apache.ibatis.datasource.pooled;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;

import org.apache.ibatis.reflection.ExceptionUtil;

/**
 * Connection Pool
 * 1,Connection Pool Data Source, True Connection, and Proxy Connection Object
 * 2,There is also a time correlation, such as the last time a connection object was used
 * 3,Implement the invoke method of the InvocationHandler by calling the method before calling the real method, creating the proxy object with the InvocationHandler
 * To associate, which is equivalent to the proxyConnection of a class member variable, the proxy object associates the invoke method of this class, mainly to determine whether execution is the close method, execution
 * close Method requires additional action
 * @author Clinton Begin
 */
class PooledConnection implements InvocationHandler {

  /**
   * Close
   */
  private static final String CLOSE = "close";

  /**
   * Connection class, create proxy object with changed
   */
  private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };

  /**
   * hashCode
   */
  private final int hashCode;

  /**
   * Connection Pool Data Source
   */
  private final PooledDataSource dataSource;

  /**
   * True Connection
   */
  private final Connection realConnection;
  /**
   * Proxy Connection
   */
  private final Connection proxyConnection;

  /**
   * Check-out timestamp
   */
  private long checkoutTimestamp;
  /**
   * Created timestamp
   */
  private long createdTimestamp;
  /**
   * Last used timestamp
   */
  private long lastUsedTimestamp;

  /**
   * This is mainly used to distinguish uniqueness. User name + password + url generates hashCode to determine uniqueness
   */
  private int connectionTypeCode;

  /**
   * Is valid?
   */
  private boolean valid;

  /**
   * Using Connection Objects and Connection Pool Data Source Objects
   * Constructor for SimplePooledConnection that uses the Connection and PooledDataSource passed in.
   *
   * @param connection - the connection that is to be presented as a pooled connection
   * @param dataSource - the dataSource that the connection is from
   */
  public PooledConnection(Connection connection, PooledDataSource dataSource) {
    //Connect hashCode
    //Connecting Objects
    //data source
    //Create timestamp
    //Last Use Time Stamp
    //Initialization is valid
    //The proxy connection object creation method that executes the proxyConnection will call the current invoke method
    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);
  }

  /**
   * Set Invalid Connection
   * Invalidates the connection.
   */
  public void invalidate() {
    valid = false;
  }

  /**
   * Determine whether it is valid, whether its field state is valid, whether the real connection is not null, and whether the data source can ping through
   * Method to see if the connection is usable.
   *
   * @return True if the connection is usable
   */
  public boolean isValid() {
    return valid && realConnection != null && dataSource.pingConnection(this);
  }

  /**
   * Get Real Connection
   * Getter for the *real* connection that this wraps.
   *
   * @return The connection
   */
  public Connection getRealConnection() {
    return realConnection;
  }

  /**
   * Get Proxy Connection
   * Getter for the proxy for the connection.
   *
   * @return The proxy
   */
  public Connection getProxyConnection() {
    return proxyConnection;
  }

  /**
   * Or a really connected hash value, if null returns 0
   * Gets the hashcode of the real connection (or 0 if it is null).
   *
   * @return The hashcode of the real connection (or 0 if it is null)
   */
  public int getRealHashCode() {
    return realConnection == null ? 0 : realConnection.hashCode();
  }

  /**
   * Get connection type code based on url username and password
   * Getter for the connection type (based on url + user + password).
   *
   * @return The connection type
   */
  public int getConnectionTypeCode() {
    return connectionTypeCode;
  }

  /**
   * Set Connection Type
   * Setter for the connection type.
   *
   * @param connectionTypeCode - the connection type
   */
  public void setConnectionTypeCode(int connectionTypeCode) {
    this.connectionTypeCode = connectionTypeCode;
  }

  /**
   * Get the current timestamp
   * Getter for the time that the connection was created.
   *
   * @return The creation timestamp
   */
  public long getCreatedTimestamp() {
    return createdTimestamp;
  }

  /**
   * Set Created Timestamp
   * Setter for the time that the connection was created.
   *
   * @param createdTimestamp - the timestamp
   */
  public void setCreatedTimestamp(long createdTimestamp) {
    this.createdTimestamp = createdTimestamp;
  }

  /**
   * Get the last timestamp
   * Getter for the time that the connection was last used.
   *
   * @return - the timestamp
   */
  public long getLastUsedTimestamp() {
    return lastUsedTimestamp;
  }

  /**
   * Set last used timestamp
   * Setter for the time that the connection was last used.
   *
   * @param lastUsedTimestamp - the timestamp
   */
  public void setLastUsedTimestamp(long lastUsedTimestamp) {
    this.lastUsedTimestamp = lastUsedTimestamp;
  }

  /**
   * Get the time interval from last use to now
   * Getter for the time since this connection was last used.
   *
   * @return - the time since the last use
   */
  public long getTimeElapsedSinceLastUse() {
    return System.currentTimeMillis() - lastUsedTimestamp;
  }

  /**
   * The age of the connected object, that is, from the beginning of creation to the present time, allows you to treat the connected object as a creature.
   * Getter for the age of the connection.
   *
   * @return the age
   */
  public long getAge() {
    return System.currentTimeMillis() - createdTimestamp;
  }

  /**
   * Gets the checkout timestamp of this connection object
   * Getter for the timestamp that this connection was checked out.
   *
   * @return the timestamp
   */
  public long getCheckoutTimestamp() {
    return checkoutTimestamp;
  }

  /**
   * Set checked-out timestamp
   * Setter for the timestamp that this connection was checked out.
   *
   * @param timestamp the timestamp
   */
  public void setCheckoutTimestamp(long timestamp) {
    this.checkoutTimestamp = timestamp;
  }

  /**
   * Get checkout time
   * Getter for the time that this connection has been checked out.
   *
   * @return the time
   */
  public long getCheckoutTime() {
    return System.currentTimeMillis() - checkoutTimestamp;
  }

  /**
   * Override hash method
   * @return
   */
  @Override
  public int hashCode() {
    return hashCode;
  }

  /**
   * To determine if two connected objects are equal,
   * Are the hashCodes of two real connection objects equal, and their own hashcodes equal
   * Allows comparing this connection to another.
   *
   * @param obj - the other connection to test for equality
   * @see Object#equals(Object)
   */
  @Override
  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;
    }
  }

  /**
   * Required for InvocationHandler implementation.
   * The proxy object is used to determine whether the method is a close method
   * @param proxy  - not used
   * @param method - the method to be executed
   * @param args   - the parameters to be passed to the method
   * @see java.lang.reflect.InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])
   */
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //Gets whether the name of the method called by Connection is the close method
    //Is connection closure required
    String methodName = method.getName();
    if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
      dataSource.pushConnection(this);
      return null;
    }
    try {
      if (!Object.class.equals(method.getDeclaringClass())) {
        // issue #579 toString() should never fail
        // throw an SQLException instead of a Runtime
        checkConnection();
      }
      return method.invoke(realConnection, args);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }

  }

  /**
   * Check if the connection is valid
   * @throws SQLException
   */
  private void checkConnection() throws SQLException {
    if (!valid) {
      throw new SQLException("Error accessing PooledConnection. Connection is invalid.");
    }
  }

}

3.1 Summary

  1. The key point here is that it creates a proxy Connection object, primarily intercepts the close method, and puts the connection object back into the pool after it has been exhausted

4,PooledDataSource

4.1. Summary

  1. The key method here is popConnection, which takes the connection object out of the connection pool, pushConnection puts the connection object back into the connection pool, pingConnection determines if the connection object is valid

Posted by shantred on Tue, 30 Jul 2019 11:37:42 -0700