Análisis del código fuente de mybaits (cuatro) explicación detallada de la fuente de datos de mybaits

Fuente de datos de análisis de código fuente de mybaits (cuatro)

Prólogo : Para el marco ORM, las fuentes de datos son componentes indispensables. Aunque el marco mybaits integra Spring, los objetos de la fuente de datos se pueden especificar externamente y construir en SqlSessionFactoryBean, pero vale la pena explorar cómo mybaits en sí mismo implementa las fuentes de datos. Este artículo realizará un análisis detallado de la estructura de la fuente de datos del framework MyBatis, y un análisis en profundidad del pool de conexiones de MyBatis.

El contenido de este artículo se divide preliminarmente en las siguientes partes
        

    : Descripción general de la
        
    fuente de datos mybaits La construcción de la fuente de datos mybaits y
                
    el análisis del uso de UnpooledDataSource El análisis de la     fuente de datos simple
        
    PooledDataSource
        
demostración del caso

 1. Descripción general de la fuente de datos Mybaits

1. Clasificación de la fuente de datos
       MyBatis divide la fuente de datos DataSource en tres tipos de
        UnpooledDataSource: Fuente de datos que no usa pool de conexiones
        PooledDataSource: Fuente de datos que usa pool de conexiones JndiDataSource: Fuente de datos
        implementada usando JNDI

2. Estructura de clases de fuentes de datos

Se crea DatasourceFacotry fuente de datos, UnpooledDataSourceFacotry crear UnpooledDataSource, PooledDataSourceFacotry crear PooledDataSource y UnpooledDataSourceFacotry heredan propiedades de Configuración universal UnpooledDataSourceFacotry de uso, PooledDataSource interna mantiene UnpooledDataSource ejemplo, crear un enlace de base de datos para las propiedades PooledDataSource caché de agrupación de conexiones, interna se utiliza para mejorar y envolver el PooledConnection del objeto de conexión real y del objeto de conexión proxy .

La estructura de clases es la siguiente:

 3. Creación de fuente de datos

    Explicamos principalmente UnpooledDataSource y PooledDataSource. Entre ellos, UnpooledDataSource es una fuente de datos que no utiliza agrupación de conexiones. PooledDataSource envuelve UnpooledDataSource y expande la función de agrupación de conexiones. El UnpooledDataSource se utiliza para crear una conexión basada en el uso interno del contenedor de lista para realizar la función de grupo de conexiones.
    La creación de la fuente de datos adopta el modo de fábrica. La creación de UnpooledDataSource y PooledDataSource son creadas por diferentes clases de fábrica que implementan DataSourceFactory, y se establecen algunas propiedades de configuración. El siguiente es el proceso de UnpooledDataSourceFactory para crear una fuente de datos.

 public UnpooledDataSourceFactory() { // 构造函数创建对应工厂的数据源
		    this.dataSource = new UnpooledDataSource();
		 }
		 public void setProperties(Properties properties) { // 设置属性
		    Properties driverProperties = new Properties();
		    MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
		    for (Object key : properties.keySet()) {
		      String propertyName = (String) key;
		      if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) { // 设置驱动属性
		        String value = properties.getProperty(propertyName);
		        driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
		      } else if (metaDataSource.hasSetter(propertyName)) { // 其他常规数据,依据有无set方法进行设置
		        String value = (String) properties.get(propertyName);
		        Object convertedValue = convertValue(metaDataSource, propertyName, value);
		        metaDataSource.setValue(propertyName, convertedValue);
		      } else {
		        throw new DataSourceException("Unknown DataSource property: " + propertyName);
		      }
		    }
		    if (driverProperties.size() > 0) {
		      metaDataSource.setValue("driverProperties", driverProperties);
		    }
		  }

  En segundo lugar, la construcción de la fuente de datos mybaits

 Lo anterior es una breve introducción a la clasificación de fuentes de datos, la estructura del diagrama de clases y el proceso de creación de fuentes de datos para la clase de fábrica. Sabiendo que el marco creará diferentes fuentes de datos de acuerdo con las diferentes clases de fábrica, entonces cómo crear una clase de fábrica de fuentes de datos en mybaits ¿Qué pasa con

 1. Construcción de fuentes de datos

  A continuación se muestra el archivo de configuración xml

          <dataSource type="POOLED">
                <property name="driver" value="${db.driver}" />
                <property name="url" value="${db.url}" />
                <property name="username" value="${db.username}" />
                <property name="password" value="${db.password}" />
            </dataSource>

      Cuando se crea la fuente de datos, la clase de fábrica correspondiente se instanciará de acuerdo con el atributo de tipo del nodo dataSource para crear la fuente de datos, y el atributo props del nodo secundario se agregará al método establecido de la fuente de datos. significa que se crearán los datos de PooledDataSource Source, Unpooled significa que se creará una fuente de datos de UnpooledDataSource.

        private DataSourceFactory dataSourceElement(XNode context) throws Exception {
          if (context != null) { // context是解析dataSource节点的内容
              String type = context.getStringAttribute("type"); // type属性
              Properties props = context.getChildrenAsProperties(); // 子属性键值对
              DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
              factory.setProperties(props);
              return factory;
          }
          throw new BuilderException("Environment declaration requires a DataSourceFactory.");
        }

    2. Uso de fuentes de datos

    La función de la fuente de datos es obtener la conexión del objeto de conexión, entonces en el proceso de ejecución, ¿cuándo obtenemos el objeto de conexión de la fuente de datos? De la transacción anterior, aprendimos que la conexión está empaquetada en el objeto Transaction.Cuando creamos SQLSession y ejecutamos la instrucción sql a través de SqlSession, Mybatis llamará a la fuente de datos DataSource para crear el objeto Connection.

Tres, explicación detallada de UnpooledDataSource

    La fuente de datos UnpooledDataSource es creada por UnpooledDataSourceFactory, esta fuente de datos es para instanciar directamente el objeto de conexión, y luego para que la persona que llama lo use, es decir, use el método getConnection de la fuente de datos UnpooledDataSource, cada llamada producirá un objeto Connection.

 public UnpooledDataSource(String driver, String url, String username, String password) {
	    this.driver = driver;
	    this.url = url;
	    this.username = username;
	    this.password = password;
	 }
	 public Connection getConnection() throws SQLException {
	    return doGetConnection(username, password);
	 }
	 private Connection doGetConnection(String username, String password) throws SQLException {
	    Properties props = new Properties();
	    if (driverProperties != null) {
	      props.putAll(driverProperties); // 其他参数
	    }
	    if (username != null) { // username参数
	      props.setProperty("user", username);
	    }
	    if (password != null) { // password参数
	      props.setProperty("password", password);
	    }
	    return doGetConnection(props);
	 }
	 private Connection doGetConnection(Properties properties) throws SQLException {
	    initializeDriver(); // 初始化驱动
	    Connection connection = DriverManager.getConnection(url, properties); // 获得连接
	    configureConnection(connection);
	    return connection;
	 }			

Por lo anterior, sabemos que getConnection de UnpooledDataSource es el proceso de crear directamente un objeto Connection de conexión jdbc.

Cuatro, explicación detallada de PooledDataSource

    Fuente de datos PooledDataSource En lo anterior, presenté la fuente de datos que tiene un grupo de conexiones. Ahora miramos el principio básico de PooledDataSource: la fuente de datos PooledDataSource envuelve el objeto Connection en el objeto PooledConnection y lo coloca en el contenedor PoolState para su mantenimiento. MyBatis divide la PooledConnection en el pool de conexiones en dos estados: inactivo y activo Los objetos PooledConnection de estos dos estados se almacenan en las dos colecciones List de idleConnections y activeConnections en el contenedor PoolState, respectivamente.
    La función principal del paquete interno de PooledDataSource de UnpooledDataSource es que cuando necesita crear una fuente de datos, puede llamar a UnpooledDataSource para crear una fuente de datos.

1. Los principales atributos de PoolState y PooledDataSource

public class PoolState {
	  protected PooledDataSource dataSource;
	  // 空闲连接
	  protected final List<PooledConnection> idleConnections = new ArrayList<PooledConnection>();
	  // 活跃连接
	  protected final List<PooledConnection> activeConnections = new ArrayList<PooledConnection>();
	
	public class PooledDataSource implements DataSource {
	  private final PoolState state = new PoolState(this); // 封装空闲连接和活跃连接的集合
	  private final UnpooledDataSource dataSource; // unpooledDataSource用于真实获取连接用途
	  protected int poolMaximumActiveConnections = 10; // 最大活跃个数
	  protected int poolMaximumIdleConnections = 5; // 最大空闲个数
	  protected int poolMaximumCheckoutTime = 20000; // 最大工作超时时间
	  protected int poolTimeToWait = 20000; // 未获得连接的等待时间
	  protected String poolPingQuery = "NO PING QUERY SET"; // 心跳查询
	  protected boolean poolPingEnabled = false;
	  protected int poolPingConnectionsNotUsedFor = 0;
	  private int expectedConnectionTypeCode;
	  public PooledDataSource() {
	    dataSource = new UnpooledDataSource();
	  }	

    2. Lógica de ejecución básica

   Una vez finalizada la estructura de atributos de la clase, el flujo de trabajo del grupo de conexiones es claro. Dado que es un grupo de conexiones, desde la perspectiva de la actualización del grupo de conexiones, hay un proceso de abrir una conexión y volver a colocarla en el grupo de conexiones cuando se agota. Este proceso corresponde al método popConnection y pushConnection.

         1) Saque la conexión popConnection

El proceso de desconexión de la conexión es básicamente si el inactivo está allí, luego se toma del inactivo, de lo contrario, se juzga si el tamaño activo es mayor que el tamaño activo máximo, si es menor, entonces un nuevo enlace puede ser creado, si es mayor o igual a, entonces no se puede crear., Saque el primer enlace usado para determinar si está caducado, si caduca, deshaga, actualice a un enlace, si no caduca, espere y luego continúe realizando la adquisición.

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.isEmpty()) { 
          conn = state.idleConnections.remove(0); // 从空闲中取
        } else {
          // 如果活跃尺寸少于最大活跃
          if (state.activeConnections.size() < poolMaximumActiveConnections) {
          	创建一个新的--这个datasource 是UnpooledDataSource,即获取真实连接。
            conn = new PooledConnection(dataSource.getConnection(), this);
          } else {
            // 否则从旧的中取
            PooledConnection oldestActiveConnection = state.activeConnections.get(0);
            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
            // 判断最先用的是否超时
            if (longestCheckoutTime > poolMaximumCheckoutTime) {
              // 是超时就移除旧的
              state.activeConnections.remove(oldestActiveConnection);
              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
                try { // 并且旧的要回滚
                  oldestActiveConnection.getRealConnection().rollback();
                } catch (SQLException e) {
                }  
              }
              // 把旧的真实连接取出,包装成一个新的PooledConnection
              conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
              oldestActiveConnection.invalidate();
            } else {
              try {
              	// 如果没法获得连接就等待,等待恢复后,从新执行while下面的流程判断。
                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); // 等待
              } catch (InterruptedException e) {
                break;
              }
            }
          }
        }
        if (conn != null) {
          if (conn.isValid()) {
            if (!conn.getRealConnection().getAutoCommit()) {
              conn.getRealConnection().rollback(); // 再次保证全部进行了rollBack,才可以使用
            }
            state.activeConnections.add(conn); // 添加到activeConnections
          } else {
            conn = null;
          }
        }
      }
    }
    return conn;
  }

El diagrama de flujo se muestra en la siguiente figura.  

2) Conexión de retorno oushConnection

A partir del análisis anterior, sabemos que nuestra colección activa se creará cuando no haya exceso de capacidad, entonces, ¿cómo juzga nuestro primer nivel la cantidad de contenedores inactivos? Se trata de analizar el proceso de conexión de retorno a continuación.
       El proceso de devolución de la conexión es mucho más sencillo que el anterior, es decir, si el contenedor idel es menor que el límite máximo de actividad, entonces se coloca en el contenedor libre, si excede se cerrará directamente (nótese que esta es una llamada de conexión real para cerrar la conexió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();
          state.notifyAll();
        // 否则直接关闭
        } else {
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          conn.getRealConnection().close();
          conn.invalidate();
        }
      } else {
      	// ....
      }
    }
  }

 3. Análisis de PooledConnection

Lo que analizamos anteriormente son los métodos pushConnection y popConnection que manipulan directamente el grupo de conexiones, entonces, ¿cómo llamamos a estos dos métodos externamente? En primer lugar, el método popConnection es muy simple, es llamado por el método de interfaz del grupo de conexiones getConnection. ¿Y qué pasa con el método pushConnection? , ¿Llamamos manualmente a este método cada vez que terminamos de usar la conexión?
       Definitivamente esto no es posible. Lo que esperamos es que cada vez que se use la conexión para el cierre, el cierre no se realice realmente, sino que se llame al método pushConnection. Ni siquiera pienses en esta función. Debe ser un proxy dinámico. La implementación específica es que nuestro PooledDataSource obtiene la conexión ProxyConnection envuelta por PooledConnection.

Las siguientes son las principales propiedades y constructores de 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 boolean valid;
	
	  public PooledConnection(Connection connection, PooledDataSource dataSource) {
	    this.hashCode = connection.hashCode();
	    this.realConnection = connection;
	    this.dataSource = dataSource;
	    this.valid = true;
	    this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
	  }

Como se muestra arriba, PooledConnection es una mejora de proxy que implementa la clase InvocationHandler, y se crea un objeto de conexión de proxy en el constructor. Solo necesitamos ver cómo se mejora el método de invocación de esta clase.

  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);  // 核心 -- 如果Colse方法就返回池中
	      return null;
	    } else {
	      try {
	        if (!Object.class.equals(method.getDeclaringClass())) {
	          checkConnection();
	        }
	        // 普通方法直接执行
	        return method.invoke(realConnection, args);
	      } catch (Throwable t) {
	        throw ExceptionUtil.unwrapThrowable(t);
	      }
	    }
	  }

  Cinco, demostración simple de casos de fuentes de datos

1. Empaquetado de conexión de datos MyPoolConnection y mejora e instanciación de proxy

public class MyPoolConnection implements InvocationHandler {

	private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };
	private MyPoolDataSource dataSource; // 保持数据源的链接
	private Connection realConnection; // 真实连接
	private Connection proxyConnection; // 代理连接
	private long checkoutTimestamp; // 检测使用时间

	public MyPoolConnection(Connection connection, MyPoolDataSource dataSource) {
		this.realConnection = connection;
		this.dataSource = dataSource;
		this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
	}

	public MyPoolDataSource getDataSource() {
		return dataSource;
	}

	public void setDataSource(MyPoolDataSource dataSource) {
		this.dataSource = dataSource;
	}

	public Connection getRealConnection() {
		return realConnection;
	}

	public void setRealConnection(Connection realConnection) {
		this.realConnection = realConnection;
	}

	public Connection getProxyConnection() {
		return proxyConnection;
	}

	public void setProxyConnection(Connection proxyConnection) {
		this.proxyConnection = proxyConnection;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		String methodName = method.getName();
		if ("close".equals(methodName)) {
			dataSource.pushConnection(this);
		} else {
			if (!Object.class.equals(method.getDeclaringClass())) {
				// 检测链接是否有效
			}
			return method.invoke(realConnection, args);
		}
		return null;
	}

	public long getCheckoutTime() {
		return System.currentTimeMillis() - checkoutTimestamp;
	}

	public void setCheckoutTimestamp(long checkoutTimestamp) {
		this.checkoutTimestamp = checkoutTimestamp;
	}

}

2. Fuente de datos MyPoolDataSource

public class MyPoolDataSource extends AbstractDataSource {

	// 数据源基本属性
	private String driver;
	private String url;
	private String username;
	private String password;
	// 链接池属性
	protected int poolMaximumActiveConnections = 10;
	protected int poolMaximumIdleConnections = 5;
	protected int poolMaximumCheckoutTime = 20000;
	protected int poolTimeToWait = 20000;
	// 连接容器
	protected final List<MyPoolConnection> idleConnections = new ArrayList<MyPoolConnection>();
	protected final List<MyPoolConnection> activeConnections = new ArrayList<MyPoolConnection>();
	
	public MyPoolDataSource(String driver, String url, String username, String password) {
		super();
		this.driver = driver;
		this.url = url;
		this.username = username;
		this.password = password;
	}

	@Override
	public Connection getConnection() throws SQLException {
		return popConnection(username, password).getProxyConnection();
	}

	@Override
	public Connection getConnection(String username, String password) throws SQLException {
		return popConnection(username, password).getProxyConnection();
	}

	/**
	 * 真实创建链接
	 */
	public Connection doGetConnection() throws SQLException {
		initDriver();
		Properties props = new Properties();
		if (username != null) {
			props.setProperty("user", username);
		}
		if (password != null) {
			props.setProperty("password", password);
		}
		Connection connection = DriverManager.getConnection(url, props);
		return connection;
	}

	/**
	 * 用于判断是否已经加载驱动
	 */
	private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<String, Driver>();

	/**
	 * 初始化数据源驱动
	 */
	private void initDriver() {
		if (registeredDrivers.containsKey(this.driver)) {
			try {
				Class<?> driverType = Class.forName(this.driver);
				Driver driverInstance = (Driver) driverType.newInstance();
				registeredDrivers.put(this.driver, driverInstance);
			} catch (Exception e) {
				throw new ExceptionInInitializerError(e);
			}
		}
	}

	/**
	 * 重点方法: 返回池中
	 */
	public void pushConnection(MyPoolConnection conn) throws SQLException {
		synchronized (this) {
			activeConnections.remove(conn);
			if (!conn.getRealConnection().getAutoCommit()) {
				conn.getRealConnection().rollback();
			}
			// 空闲池有位置
			if (idleConnections.size() < poolMaximumIdleConnections) {
				MyPoolConnection newConn = new MyPoolConnection(conn.getRealConnection(), this);
				idleConnections.add(newConn);
				this.notifyAll(); // 通知有可能等待取连接的线程
			} else {
				// 直接真实释放
				conn.getRealConnection().close();
			}
		}
	}

	/**
	 * 重点方法:从池中取连接
	 */
	public MyPoolConnection popConnection(String username, String password) throws SQLException {
		MyPoolConnection conn = null;
		while (conn == null) {
			synchronized (this) {
				// idel池有货
				if (idleConnections.size() > 0) {
					conn = idleConnections.remove(0);
				} else {
					// active池未满
					if (activeConnections.size() < poolMaximumActiveConnections) {
						Connection connection = this.doGetConnection();
						conn = new MyPoolConnection(connection, this);
						activeConnections.add(conn);
					} else {
						MyPoolConnection oldConnection = this.activeConnections.get(0);
						long longestCheckoutTime = oldConnection.getCheckoutTime();
						// 旧的超时
						if (longestCheckoutTime > poolMaximumCheckoutTime) {
							Connection connection = oldConnection.getRealConnection();
							if (!connection.getAutoCommit()) {
								connection.rollback();
							}
							conn = new MyPoolConnection(connection, this);
							activeConnections.remove(oldConnection);
							// 没取到
						} else {
							try {
								wait(poolTimeToWait); // wait等待池等待poolTimeToWait时间,如果notifyAll就会提前取消等待
							} catch (InterruptedException e) {
								break;
							}
						}
					}
				}
			}
		}
		// 取到连接了
		if (conn != null) {
			if (!conn.getRealConnection().getAutoCommit()) {
				conn.getRealConnection().rollback();
			}
			conn.setCheckoutTimestamp(System.currentTimeMillis());
		}
		// 没取到报错
		if (conn == null) {
			throw new SQLException(
					"PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
		}
		return conn;
	}

	public String getDriver() {
		return driver;
	}

	public void setDriver(String driver) {
		this.driver = driver;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public int getPoolMaximumActiveConnections() {
		return poolMaximumActiveConnections;
	}

	public void setPoolMaximumActiveConnections(int poolMaximumActiveConnections) {
		this.poolMaximumActiveConnections = poolMaximumActiveConnections;
	}

	public int getPoolMaximumIdleConnections() {
		return poolMaximumIdleConnections;
	}

	public void setPoolMaximumIdleConnections(int poolMaximumIdleConnections) {
		this.poolMaximumIdleConnections = poolMaximumIdleConnections;
	}

	public int getPoolMaximumCheckoutTime() {
		return poolMaximumCheckoutTime;
	}

	public void setPoolMaximumCheckoutTime(int poolMaximumCheckoutTime) {
		this.poolMaximumCheckoutTime = poolMaximumCheckoutTime;
	}

	public int getPoolTimeToWait() {
		return poolTimeToWait;
	}

	public void setPoolTimeToWait(int poolTimeToWait) {
		this.poolTimeToWait = poolTimeToWait;
	}

	public static Map<String, Driver> getRegisteredDrivers() {
		return registeredDrivers;
	}

	public static void setRegisteredDrivers(Map<String, Driver> registeredDrivers) {
		MyPoolDataSource.registeredDrivers = registeredDrivers;
	}
	
	public String logSate() {
		return "空闲池" + idleConnections.size() + "," + "活跃池" + activeConnections.size();
	}
}

3. Prueba y uso

/**
 * 数据源案例的测试
 */
public class DataSourceDemo {

	/**
	 * 测试逻辑:(空闲max参数5,活跃max参数10) 建立30个线程,执行一个sql,然后每个线程执行完,打印一次池状态,
	 * 并且10次再打印一次池状态。
	 */
	public static void main(String[] args) throws SQLException {
		String driver = "com.mysql.jdbc.Driver";
		String url = "jdbc:mysql://localhost:3306/java_base?characterEncoding=utf8";
		String username = "root";
		String password = "123456";
		MyPoolDataSource pool = new MyPoolDataSource(driver, url, username, password);
		excute(pool);
		CyclicBarrier cb = new CyclicBarrier(10, () -> {
			System.out.println("10个线程执行完:" + pool.logSate());
		});
		// 30个线程执行sql
		for (int i = 0; i < 30; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						excute(pool);
						System.out.println("单个线程执行完:" + pool.logSate());
					} catch (SQLException e) {
						e.printStackTrace();
					}
					try {
						cb.await();
					} catch (Exception e1) {
						e1.printStackTrace();
					}
				};
			}, "thread" + i).start();
		}
	}

	private static void excute(MyPoolDataSource pool) throws SQLException {
		Connection connection = pool.getConnection();
		PreparedStatement prepareStatement = connection.prepareStatement("select * from blog");
		ResultSet executeQuery = prepareStatement.executeQuery();
		while (executeQuery.next()) {
			// System.out.println(executeQuery.getObject("id"));
		}
		connection.close();
	}
}

 

¡fin!

Supongo que te gusta

Origin blog.csdn.net/shuixiou1/article/details/113621338
Recomendado
Clasificación