原文链接 https://blog.csdn.net/NongYeting/article/details/106408985 https://tech.meituan.com/2018/01/19/mybatis-cache.html https://juejin.cn/post/6844903665094901767

在springboot下的配置

MyBatis 一级缓存(MyBaits 称其为 Local Cache)无法关闭,但是有两种级别可选:

A.session

在同一个 sqlSession 内,对同样的查询将不再查询数据库,直接从缓存中获取。

  1. mybatis:
  2. configuration:
  3. cache-enabled: false #禁用二级缓存
  4. local-cache-scope: session #一级缓存指定为session级别

B.statement

每次查询结束都会清掉一级缓存,实际效果就是禁用了一级缓存;

  1. mybatis:
  2. configuration:
  3. cache-enabled: false #禁用二级缓存
  4. local-cache-scope: statement #一级缓存指定为statement级别
  1. public abstract class BaseExecutor implements Executor {
  2. ...
  3. ...
  4. ...
  5. ...
  6. @SuppressWarnings("unchecked")
  7. @Override
  8. public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  9. ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
  10. if (closed) {
  11. throw new ExecutorException("Executor was closed.");
  12. }
  13. if (queryStack == 0 && ms.isFlushCacheRequired()) {
  14. clearLocalCache();
  15. }
  16. List<E> list;
  17. try {
  18. queryStack++;
  19. list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
  20. if (list != null) {
  21. handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
  22. } else {
  23. list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
  24. }
  25. } finally {
  26. queryStack--;
  27. }
  28. if (queryStack == 0) {
  29. for (DeferredLoad deferredLoad : deferredLoads) {
  30. deferredLoad.load();
  31. }
  32. // issue #601
  33. deferredLoads.clear();
  34. //-------
  35. //LocalCacheScope.STATEMENT 每次都会清空缓存间接等于无缓存
  36. //-------
  37. if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
  38. // issue #482
  39. clearLocalCache();
  40. }
  41. }
  42. return list;
  43. }
  44. ...
  45. ...
  46. ...
  47. ...
  48. }

MyBatis一、二级缓存的基本概念

  1. MyBatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。
  2. MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。
  3. 在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis、Memcached等分布式缓存可能成本更低,安全性也更高。

最终建议MyBatis缓存特性在生产环境中进行关闭(单体服务例外)单纯作为一个ORM框架使用可能更为合适

LocalCacheScope类

  1. package org.apache.ibatis.session;
  2. /**
  3. * @author Eduardo Macarron
  4. */
  5. public enum LocalCacheScope {
  6. SESSION,STATEMENT
  7. }

Configuration类

  1. package org.apache.ibatis.session;
  2. import java.util.Arrays;
  3. import java.util.Collection;
  4. import java.util.HashMap;
  5. import java.util.HashSet;
  6. import java.util.Iterator;
  7. import java.util.LinkedList;
  8. import java.util.List;
  9. import java.util.Map;
  10. import java.util.Properties;
  11. import java.util.Set;
  12. import java.util.function.BiFunction;
  13. /**
  14. * @author Clinton Begin
  15. */
  16. public class Configuration {
  17. protected Environment environment;
  18. protected boolean safeRowBoundsEnabled;
  19. protected boolean safeResultHandlerEnabled = true;
  20. protected boolean mapUnderscoreToCamelCase;
  21. protected boolean aggressiveLazyLoading;
  22. protected boolean multipleResultSetsEnabled = true;
  23. protected boolean useGeneratedKeys;
  24. protected boolean useColumnLabel = true;
  25. protected boolean cacheEnabled = true;
  26. protected boolean callSettersOnNulls;
  27. protected boolean useActualParamName = true;
  28. protected boolean returnInstanceForEmptyRow;
  29. protected boolean shrinkWhitespacesInSql;
  30. protected boolean nullableOnForEach;
  31. protected boolean argNameBasedConstructorAutoMapping;
  32. protected String logPrefix;
  33. protected Class<? extends Log> logImpl;
  34. protected Class<? extends VFS> vfsImpl;
  35. protected Class<?> defaultSqlProviderType;
  36. protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  37. protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  38. protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
  39. protected Integer defaultStatementTimeout;
  40. protected Integer defaultFetchSize;
  41. protected ResultSetType defaultResultSetType;
  42. protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  43. protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
  44. protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
  45. protected Properties variables = new Properties();
  46. protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
  47. protected ObjectFactory objectFactory = new DefaultObjectFactory();
  48. protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
  49. protected boolean lazyLoadingEnabled = false;
  50. protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
  51. protected String databaseId;
  52. /**
  53. * Configuration factory class.
  54. * Used to create Configuration for loading deserialized unread properties.
  55. *
  56. * @see <a href='https://github.com/mybatis/old-google-code-issues/issues/300'>Issue 300 (google code)</a>
  57. */
  58. protected Class<?> configurationFactory;
  59. protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
  60. protected final InterceptorChain interceptorChain = new InterceptorChain();
  61. protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
  62. protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  63. protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
  64. protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
  65. .conflictMessageProducer((savedValue, targetValue) ->
  66. ". please check " + savedValue.getResource() + " and " + targetValue.getResource());
  67. protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
  68. protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
  69. protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
  70. protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");
  71. protected final Set<String> loadedResources = new HashSet<>();
  72. protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
  73. protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
  74. protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
  75. protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
  76. protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();
  77. /*
  78. * A map holds cache-ref relationship. The key is the namespace that
  79. * references a cache bound to another namespace and the value is the
  80. * namespace which the actual cache is bound to.
  81. */
  82. protected final Map<String, String> cacheRefMap = new HashMap<>();
  83. public Configuration(Environment environment) {
  84. this();
  85. this.environment = environment;
  86. }
  87. public Configuration() {
  88. typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
  89. typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
  90. typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
  91. typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
  92. typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
  93. typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
  94. typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
  95. typeAliasRegistry.registerAlias("LRU", LruCache.class);
  96. typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
  97. typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
  98. typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
  99. typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
  100. typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
  101. typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
  102. typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
  103. typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
  104. typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
  105. typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
  106. typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
  107. typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
  108. typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
  109. typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
  110. languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
  111. languageRegistry.register(RawLanguageDriver.class);
  112. }

UnpooledDataSourceFactory 非池化类

package org.apache.ibatis.datasource.unpooled;
image.png

  1. package org.apache.ibatis.datasource.unpooled;
  2. /**
  3. * @author Clinton Begin
  4. */
  5. public class UnpooledDataSourceFactory implements DataSourceFactory {
  6. private static final String DRIVER_PROPERTY_PREFIX = "driver.";
  7. private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length();
  8. protected DataSource dataSource;
  9. public UnpooledDataSourceFactory() {
  10. this.dataSource = new UnpooledDataSource();
  11. }
  12. @Override
  13. public void setProperties(Properties properties) {
  14. Properties driverProperties = new Properties();
  15. MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
  16. for (Object key : properties.keySet()) {
  17. String propertyName = (String) key;
  18. if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
  19. String value = properties.getProperty(propertyName);
  20. driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
  21. } else if (metaDataSource.hasSetter(propertyName)) {
  22. String value = (String) properties.get(propertyName);
  23. Object convertedValue = convertValue(metaDataSource, propertyName, value);
  24. metaDataSource.setValue(propertyName, convertedValue);
  25. } else {
  26. throw new DataSourceException("Unknown DataSource property: " + propertyName);
  27. }
  28. }
  29. if (driverProperties.size() > 0) {
  30. metaDataSource.setValue("driverProperties", driverProperties);
  31. }
  32. }
  33. @Override
  34. public DataSource getDataSource() {
  35. return dataSource;
  36. }
  37. private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {
  38. Object convertedValue = value;
  39. Class<?> targetType = metaDataSource.getSetterType(propertyName);
  40. if (targetType == Integer.class || targetType == int.class) {
  41. convertedValue = Integer.valueOf(value);
  42. } else if (targetType == Long.class || targetType == long.class) {
  43. convertedValue = Long.valueOf(value);
  44. } else if (targetType == Boolean.class || targetType == boolean.class) {
  45. convertedValue = Boolean.valueOf(value);
  46. }
  47. return convertedValue;
  48. }
  49. }

PooledDataSourceFactory\PooledDataSource 线程池

package org.apache.ibatis.datasource.pooled;

参数说明

  1. poolMaximumActiveConnections : 最大活跃数,默认值:10
  2. poolMaximumIdleConnections :最大空闲连接数,默认值:5
  3. poolMaximumCheckoutTime :获取连接超时等待最大(checked out)时间,默认值:20000 毫秒
  4. poolTimeToWait : 单次获取连接 最大等待时间 默认:20000 毫秒
  5. poolMaximumLocalBadConnectionTolerance 获取连接重试次数 默认:3
  6. poolPingQuery 用于检测连接是否断开的测试语句,默认值:”NO PING QUERY SET”,一般设置为”select 1”
  7. poolPingEnabled 是否通过执行poolPingQuery 语句做检测,默认值:false,必须为true才生效
  8. poolPingConnectionsNotUsedFor 连接检测间隔时间 ,默认0。必须大0才生效

这3个参数是一起使用poolPingQuery、poolPingEnabled、poolPingConnectionsNotUsedFor

  1. package org.apache.ibatis.datasource.pooled;
  2. /**
  3. * @author Clinton Begin
  4. */
  5. public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
  6. public PooledDataSourceFactory() {
  7. this.dataSource = new PooledDataSource();
  8. }
  9. }
  10. /**
  11. * This is a simple, synchronous, thread-safe database connection pool.
  12. *
  13. * @author Clinton Begin
  14. */
  15. public class PooledDataSource implements DataSource {
  16. private static final Log log = LogFactory.getLog(PooledDataSource.class);
  17. private final PoolState state = new PoolState(this);
  18. private final UnpooledDataSource dataSource;
  19. // OPTIONAL CONFIGURATION FIELDS
  20. protected int poolMaximumActiveConnections = 10;
  21. protected int poolMaximumIdleConnections = 5;
  22. protected int poolMaximumCheckoutTime = 20000;
  23. protected int poolTimeToWait = 20000;
  24. protected int poolMaximumLocalBadConnectionTolerance = 3;
  25. protected String poolPingQuery = "NO PING QUERY SET";
  26. protected boolean poolPingEnabled;
  27. protected int poolPingConnectionsNotUsedFor;
  28. private int expectedConnectionTypeCode;
  29. public PooledDataSource() {
  30. dataSource = new UnpooledDataSource();
  31. }
  32. public PooledDataSource(UnpooledDataSource dataSource) {
  33. this.dataSource = dataSource;
  34. }
  35. public PooledDataSource(String driver, String url, String username, String password) {
  36. dataSource = new UnpooledDataSource(driver, url, username, password);
  37. expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  38. }
  39. public PooledDataSource(String driver, String url, Properties driverProperties) {
  40. dataSource = new UnpooledDataSource(driver, url, driverProperties);
  41. expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  42. }
  43. public PooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {
  44. dataSource = new UnpooledDataSource(driverClassLoader, driver, url, username, password);
  45. expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  46. }
  47. public PooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
  48. dataSource = new UnpooledDataSource(driverClassLoader, driver, url, driverProperties);
  49. expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  50. }
  51. ...
  52. ...
  53. ...
  54. }

数据库连接池维护操作

核心两个方法:

pushConnection

这个方法简单一看就明白了。

  1. protected void pushConnection(PooledConnection conn) throws SQLException {
  2. synchronized (state) {
  3. state.activeConnections.remove(conn);
  4. if (conn.isValid()) {
  5. if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
  6. state.accumulatedCheckoutTime += conn.getCheckoutTime();
  7. if (!conn.getRealConnection().getAutoCommit()) {
  8. conn.getRealConnection().rollback();
  9. }
  10. PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
  11. state.idleConnections.add(newConn);
  12. newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
  13. newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
  14. conn.invalidate();
  15. if (log.isDebugEnabled()) {
  16. log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
  17. }
  18. // 线程唤醒
  19. state.notifyAll();
  20. } else {
  21. state.accumulatedCheckoutTime += conn.getCheckoutTime();
  22. if (!conn.getRealConnection().getAutoCommit()) {
  23. conn.getRealConnection().rollback();
  24. }
  25. conn.getRealConnection().close();
  26. if (log.isDebugEnabled()) {
  27. log.debug("Closed connection " + conn.getRealHashCode() + ".");
  28. }
  29. conn.invalidate();
  30. }
  31. } else {
  32. if (log.isDebugEnabled()) {
  33. log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
  34. }
  35. state.badConnectionCount++;
  36. }
  37. }
  38. }

popConnection

这个方法稍微复杂一点,这里涉及到synchronized锁机制(死循环取连接)、连接超时、超过本地错误最大连接报错

  1. private PooledConnection popConnection(String username, String password) throws SQLException {
  2. boolean countedWait = false;
  3. PooledConnection conn = null;
  4. long t = System.currentTimeMillis();
  5. int localBadConnectionCount = 0;
  6. // 重点看一下
  7. while (conn == null) {
  8. // 重点看一下
  9. synchronized (state) {
  10. if (!state.idleConnections.isEmpty()) {
  11. // Pool has available connection
  12. conn = state.idleConnections.remove(0);
  13. if (log.isDebugEnabled()) {
  14. log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
  15. }
  16. } else {
  17. // 未超过最大连接数,直接创建新的连接
  18. // Pool does not have available connection
  19. if (state.activeConnections.size() < poolMaximumActiveConnections) {
  20. // Can create new connection
  21. conn = new PooledConnection(dataSource.getConnection(), this);
  22. if (log.isDebugEnabled()) {
  23. log.debug("Created connection " + conn.getRealHashCode() + ".");
  24. }
  25. } else {
  26. // 连接池取一个连接,check是否超过最大连接时间,未超过就取出来使用,超过继续else
  27. // Cannot create new connection
  28. PooledConnection oldestActiveConnection = state.activeConnections.get(0);
  29. long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
  30. if (longestCheckoutTime > poolMaximumCheckoutTime) {
  31. // Can claim overdue connection
  32. state.claimedOverdueConnectionCount++;
  33. state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
  34. state.accumulatedCheckoutTime += longestCheckoutTime;
  35. state.activeConnections.remove(oldestActiveConnection);
  36. if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
  37. try {
  38. oldestActiveConnection.getRealConnection().rollback();
  39. } catch (SQLException e) {
  40. /*
  41. Just log a message for debug and continue to execute the following
  42. statement like nothing happened.
  43. Wrap the bad connection with a new PooledConnection, this will help
  44. to not interrupt current executing thread and give current thread a
  45. chance to join the next competition for another valid/good database
  46. connection. At the end of this loop, bad {@link @conn} will be set as null.
  47. */
  48. log.debug("Bad connection. Could not roll back");
  49. }
  50. }
  51. conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
  52. conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
  53. conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
  54. oldestActiveConnection.invalidate();
  55. if (log.isDebugEnabled()) {
  56. log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
  57. }
  58. } else {
  59. // 到这里说明mysql连接池已经打满已经达到最大数,必须等待等待其他线程唤醒,state.notifyAll();
  60. // 如果超过最大时间还未唤醒就累计错误连接,如果超过最大错误连接数服务会报错
  61. // Must wait
  62. try {
  63. if (!countedWait) {
  64. state.hadToWaitCount++;
  65. countedWait = true;
  66. }
  67. if (log.isDebugEnabled()) {
  68. log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
  69. }
  70. long wt = System.currentTimeMillis();
  71. state.wait(poolTimeToWait);
  72. state.accumulatedWaitTime += System.currentTimeMillis() - wt;
  73. } catch (InterruptedException e) {
  74. // set interrupt flag
  75. Thread.currentThread().interrupt();
  76. break;
  77. }
  78. }
  79. }
  80. }
  81. if (conn != null) {
  82. // ping to server and check the connection is valid or not
  83. if (conn.isValid()) {
  84. if (!conn.getRealConnection().getAutoCommit()) {
  85. conn.getRealConnection().rollback();
  86. }
  87. conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
  88. conn.setCheckoutTimestamp(System.currentTimeMillis());
  89. conn.setLastUsedTimestamp(System.currentTimeMillis());
  90. state.activeConnections.add(conn);
  91. state.requestCount++;
  92. state.accumulatedRequestTime += System.currentTimeMillis() - t;
  93. } else {
  94. if (log.isDebugEnabled()) {
  95. log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
  96. }
  97. state.badConnectionCount++;
  98. localBadConnectionCount++;
  99. conn = null;
  100. // 重点分析
  101. if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {
  102. if (log.isDebugEnabled()) {
  103. log.debug("PooledDataSource: Could not get a good connection to the database.");
  104. }
  105. throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
  106. }
  107. }
  108. }
  109. }
  110. }
  111. if (conn == null) {
  112. if (log.isDebugEnabled()) {
  113. log.debug("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
  114. }
  115. throw new SQLException("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
  116. }
  117. return conn;
  118. }

PoolState 线程池状态类

  1. package org.apache.ibatis.datasource.pooled;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. /**
  5. * @author Clinton Begin
  6. */
  7. public class PoolState {
  8. protected PooledDataSource dataSource;
  9. protected final List<PooledConnection> idleConnections = new ArrayList<>();
  10. protected final List<PooledConnection> activeConnections = new ArrayList<>();
  11. protected long requestCount = 0;
  12. protected long accumulatedRequestTime = 0;
  13. protected long accumulatedCheckoutTime = 0;
  14. protected long claimedOverdueConnectionCount = 0;
  15. protected long accumulatedCheckoutTimeOfOverdueConnections = 0;
  16. protected long accumulatedWaitTime = 0;
  17. protected long hadToWaitCount = 0;
  18. protected long badConnectionCount = 0;
  19. public PoolState(PooledDataSource dataSource) {
  20. this.dataSource = dataSource;
  21. }
  22. public synchronized long getRequestCount() {
  23. return requestCount;
  24. }
  25. public synchronized long getAverageRequestTime() {
  26. return requestCount == 0 ? 0 : accumulatedRequestTime / requestCount;
  27. }
  28. public synchronized long getAverageWaitTime() {
  29. return hadToWaitCount == 0 ? 0 : accumulatedWaitTime / hadToWaitCount;
  30. }
  31. public synchronized long getHadToWaitCount() {
  32. return hadToWaitCount;
  33. }
  34. public synchronized long getBadConnectionCount() {
  35. return badConnectionCount;
  36. }
  37. public synchronized long getClaimedOverdueConnectionCount() {
  38. return claimedOverdueConnectionCount;
  39. }
  40. public synchronized long getAverageOverdueCheckoutTime() {
  41. return claimedOverdueConnectionCount == 0 ? 0 : accumulatedCheckoutTimeOfOverdueConnections / claimedOverdueConnectionCount;
  42. }
  43. public synchronized long getAverageCheckoutTime() {
  44. return requestCount == 0 ? 0 : accumulatedCheckoutTime / requestCount;
  45. }
  46. public synchronized int getIdleConnectionCount() {
  47. return idleConnections.size();
  48. }
  49. public synchronized int getActiveConnectionCount() {
  50. return activeConnections.size();
  51. }
  52. @Override
  53. public synchronized String toString() {
  54. StringBuilder builder = new StringBuilder();
  55. builder.append("\n===CONFIGURATION==============================================");
  56. builder.append("\n jdbcDriver ").append(dataSource.getDriver());
  57. builder.append("\n jdbcUrl ").append(dataSource.getUrl());
  58. builder.append("\n jdbcUsername ").append(dataSource.getUsername());
  59. builder.append("\n jdbcPassword ").append(dataSource.getPassword() == null ? "NULL" : "************");
  60. builder.append("\n poolMaxActiveConnections ").append(dataSource.poolMaximumActiveConnections);
  61. builder.append("\n poolMaxIdleConnections ").append(dataSource.poolMaximumIdleConnections);
  62. builder.append("\n poolMaxCheckoutTime ").append(dataSource.poolMaximumCheckoutTime);
  63. builder.append("\n poolTimeToWait ").append(dataSource.poolTimeToWait);
  64. builder.append("\n poolPingEnabled ").append(dataSource.poolPingEnabled);
  65. builder.append("\n poolPingQuery ").append(dataSource.poolPingQuery);
  66. builder.append("\n poolPingConnectionsNotUsedFor ").append(dataSource.poolPingConnectionsNotUsedFor);
  67. builder.append("\n ---STATUS-----------------------------------------------------");
  68. builder.append("\n activeConnections ").append(getActiveConnectionCount());
  69. builder.append("\n idleConnections ").append(getIdleConnectionCount());
  70. builder.append("\n requestCount ").append(getRequestCount());
  71. builder.append("\n averageRequestTime ").append(getAverageRequestTime());
  72. builder.append("\n averageCheckoutTime ").append(getAverageCheckoutTime());
  73. builder.append("\n claimedOverdue ").append(getClaimedOverdueConnectionCount());
  74. builder.append("\n averageOverdueCheckoutTime ").append(getAverageOverdueCheckoutTime());
  75. builder.append("\n hadToWait ").append(getHadToWaitCount());
  76. builder.append("\n averageWaitTime ").append(getAverageWaitTime());
  77. builder.append("\n badConnectionCount ").append(getBadConnectionCount());
  78. builder.append("\n===============================================================");
  79. return builder.toString();
  80. }
  81. }