Class StatementCache

All Implemented Interfaces:
InvocationHandler, StatementCacheMBean

public class StatementCache extends StatementDecoratorInterceptor implements StatementCacheMBean
Interceptor that caches PreparedStatement and/or CallableStatement instances on a connection.

A cache hit is answered by this interceptor alone: the rest of the chain is not invoked for it, and a recycled statement is reset to driver defaults. Interceptors listed after this one therefore do not see reused statements, so QueryTimeoutInterceptor and StatementFinalizer must be listed before the cache. A tracker such as StatementFinalizer beneath the cache would close a parked statement at connection return, so the cache then refuses to park at all.

With JMX enabled, a cache publishes an MBean below its connection's MBean. It is released when the connection goes out of service (the chain teardown reset(null, null)), when the pool closes, or when the pool MBean is deregistered - not on an ordinary return and re-borrow. A pool abandoned without closing keeps its registrations, and through them the pool, its sessions and its driver class loader reachable.

  • Field Details

    • ALL_TYPES

      protected static final String[] ALL_TYPES
      All statement types (prepared and callable).
    • CALLABLE_TYPE

      protected static final String[] CALLABLE_TYPE
      Callable statement type only.
    • PREPARED_TYPE

      protected static final String[] PREPARED_TYPE
      Prepared statement type only.
    • NO_TYPE

      protected static final String[] NO_TYPE
      No statement types.
    • STATEMENT_CACHE_ATTR

      protected static final String STATEMENT_CACHE_ATTR
      Attribute key for storing the statement cache in connection attributes.
  • Constructor Details

    • StatementCache

      public StatementCache()
      Default constructor.
  • Method Details

    • isCachePrepared

      public boolean isCachePrepared()
      Description copied from interface: StatementCacheMBean
      Checks if prepared statements are being cached.
      Specified by:
      isCachePrepared in interface StatementCacheMBean
      Returns:
      true if prepared statement caching is enabled
    • isCacheCallable

      public boolean isCacheCallable()
      Description copied from interface: StatementCacheMBean
      Checks if callable statements are being cached.
      Specified by:
      isCacheCallable in interface StatementCacheMBean
      Returns:
      true if callable statement caching is enabled
    • getMaxCacheSize

      public int getMaxCacheSize()
      Description copied from interface: StatementCacheMBean
      Returns the maximum size of the statement cache.
      Specified by:
      getMaxCacheSize in interface StatementCacheMBean
      Returns:
      maximum cache size
    • getTypes

      public String[] getTypes()
      Returns the statement types being cached.

      A copy is returned because the arrays a cache matches against are shared by every instance in the class loader; mutating the array behind the accessor would change what every pool in the JVM caches, not just this one. This is an extension accessor: the pool itself does not read it.

      Returns:
      array of statement type strings
    • getCacheSize

      public int getCacheSize()
      The pool-wide count of statements parked in the caches of all connections. Parked statements are physically open until the cache is disposed, so this is not the number of statements ready to run.
      Specified by:
      getCacheSize in interface StatementCacheMBean
      Returns:
      the count, or -1 once this interceptor has been disposed and the pool-wide counter is gone
    • setProperties

      public void setProperties(Map<String, PoolProperties.InterceptorProperty> properties)
      Description copied from class: JdbcInterceptor
      Called during the creation of an interceptor, once per instance: once for each instance created to build a pooled connection's interceptor chain, and once on the throwaway instance of each configured class that receives JdbcInterceptor.poolStarted(ConnectionPool) at pool start and JdbcInterceptor.poolClosed(ConnectionPool) at pool close. The pool passes the live map of the interceptor definition, shared across all these instances, so it must be treated as read-only (see JdbcInterceptor.getProperties()). Override this method to perform type casts between string values and object properties.
      Overrides:
      setProperties in class JdbcInterceptor
      Parameters:
      properties - The properties
    • poolStarted

      public void poolStarted(ConnectionPool pool)
      Description copied from class: JdbcInterceptor
      This method is invoked by a connection pool when the pool is first started up, usually when the first connection is requested. Interceptor classes can override this method if they keep static variables or other tracking means around. This method is only invoked on a single instance of the interceptor, and not on every instance created. That instance is a throwaway created solely for this notification: it shares no state with the instances in any connection's chain or with the instance that later receives JdbcInterceptor.poolClosed(ConnectionPool), so any state this hook needs must be static or keyed on the pool. An exception thrown here aborts pool initialization: the pool is closed and the failure is reported to the caller that triggered the start-up.
      Overrides:
      poolStarted in class JdbcInterceptor
      Parameters:
      pool - - the pool that is being started up.
    • poolClosed

      public void poolClosed(ConnectionPool pool)
      Description copied from class: JdbcInterceptor
      This method is invoked by a connection pool when the pool is closed. Interceptor classes can override this method if they keep static variables or other tracking means around. This method is only invoked on a single instance of the interceptor, and not on every instance created. That instance is a throwaway created solely for this notification: it shares no state with the instances in any connection's chain, so any state this hook needs must be static or keyed on the pool. The callback is delivered for every definition the pool told about its start, whatever the interceptor configuration lists by the time the pool closes, and additionally for any definition the configuration has gained since the start. An override that registers state at the start must therefore find its release delivered even after its definition has been removed from a live pool's configuration, and must tolerate a delivery it did not pair with its own start. An exception thrown here is logged at debug level and otherwise ignored; the pool close continues.
      Overrides:
      poolClosed in class JdbcInterceptor
      Parameters:
      pool - - the pool that is being closed.
    • reset

      public void reset(ConnectionPool parent, PooledConnection con)
      Description copied from class: AbstractCreateStatementInterceptor
      no-op for this interceptor. no state is stored.
      Overrides:
      reset in class AbstractCreateStatementInterceptor
      Parameters:
      parent - - the connection pool owning the connection
      con - - the pooled connection
    • unregisterMBean

      public void unregisterMBean()
      Description copied from class: JdbcInterceptor
      Releases any MBean this interceptor published for the connection it serves, without tearing down the connection state. The pool calls this when it releases its own MBean while the pool stays live; the connection and its chain remain usable, and a later borrow can publish the interceptor again. The default does nothing; an interceptor that publishes an MBean overrides it.
      Overrides:
      unregisterMBean in class JdbcInterceptor
    • disconnected

      public void disconnected(ConnectionPool parent, PooledConnection con, boolean finalizing)
      Description copied from class: JdbcInterceptor
      Called when Connection.close() is called on the underlying connection. This is to notify the interceptors, that the physical connection has been released. Implementation of this method should be thought through with care, as no actions should trigger an exception. An interceptor that nevertheless throws is isolated: the exception is logged and ignored, the remaining interceptors in the chain are still notified, and the physical connection is closed as it would have been. As for JdbcInterceptor.reset(ConnectionPool, PooledConnection), a hook must not rewire the chain from this callback: chain topology is owned by the pool.
      Overrides:
      disconnected in class JdbcInterceptor
      Parameters:
      parent - - the connection pool that this connection belongs to
      con - - the pooled connection that holds this connection
      finalizing - - if this connection is finalizing. True means that the pooled connection will not reconnect the underlying connection
    • closeStatement

      public void closeStatement(StatementCache.CachedStatement st)
      Closes the given cached statement by forcing it to close.
      Parameters:
      st - the cached statement to close
    • createDecorator

      protected Object createDecorator(Object proxy, Method method, Object[] args, Object statement, Constructor<?> constructor, String sql) throws InstantiationException, IllegalAccessException, InvocationTargetException
      Description copied from class: StatementDecoratorInterceptor
      Creates a proxy for a Statement.
      Overrides:
      createDecorator in class StatementDecoratorInterceptor
      Parameters:
      proxy - The proxy object on which the method that triggered the creation of the statement was called.
      method - The method that was called on the proxy
      args - The arguments passed as part of the method call to the proxy
      statement - The statement object that is to be proxied
      constructor - The constructor for the desired proxy
      sql - The sql of the statement
      Returns:
      A new proxy for the Statement
      Throws:
      InstantiationException - Couldn't instantiate object
      IllegalAccessException - Inaccessible constructor
      InvocationTargetException - Exception thrown from constructor
    • invoke

      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
      Description copied from class: JdbcInterceptor
      Gets invoked each time an operation on Connection is invoked.

      The base implementation passes the call to the next interceptor in the chain. When there is no next interceptor, the call fails fast with a NullPointerException: a chain built by the pool always ends in the ProxyConnection terminal, so reaching this case means a chain was hand-built without a terminal link. Interceptors that do not answer a call themselves continue it with super.invoke(proxy, method, args).

      Specified by:
      invoke in interface InvocationHandler
      Overrides:
      invoke in class AbstractCreateStatementInterceptor
      Throws:
      Throwable
    • isCached

      public StatementCache.CachedStatement isCached(Method method, Object[] args)
      Checks if a statement for the given method and arguments is currently cached.
      Parameters:
      method - the SQL method (prepareStatement or prepareCall)
      args - the method arguments
      Returns:
      the cached statement, or null if not cached
    • cacheStatement

      public boolean cacheStatement(StatementCache.CachedStatement proxy)
      Caches the given statement proxy if there is room in the cache.

      The insertion is atomic: of two statements closed concurrently under one key exactly one is parked, and the other is refused as a duplicate - a park can never overwrite, and so can never orphan, a statement another close has already parked. A proxy that is refused was never in the map, and the caller that receives false must close the statement it offered.

      Parameters:
      proxy - the statement proxy to cache
      Returns:
      true if the statement was cached, false otherwise
    • removeStatement

      public boolean removeStatement(StatementCache.CachedStatement proxy)
      Removes the given statement from the cache. The removal is identity-aware: it only takes effect while the entry under the statement's key still holds this very proxy, so a proxy that was never parked - or is no longer the cached statement - cannot evict a different statement parked under an equal key.
      Parameters:
      proxy - the statement proxy to remove
      Returns:
      true if the statement was removed, false otherwise
    • getCache

      protected ConcurrentHashMap<org.apache.tomcat.jdbc.pool.interceptor.StatementCache.CacheKey, StatementCache.CachedStatement> getCache()
      Returns the statement cache for the current pooled connection.
      Returns:
      the cache map, or null if the connection has been closed
    • getCacheSizePerConnection

      public int getCacheSizePerConnection()
      Description copied from interface: StatementCacheMBean
      Returns the cache size for the current connection: the statements parked in this connection's cache. Parked statements are physically open until the cache is disposed, so this is not the number of statements ready to run. A read against an interceptor that is not bound to a pooled connection, for instance one made while a disposal is in progress, is answered quietly rather than logged.
      Specified by:
      getCacheSizePerConnection in interface StatementCacheMBean
      Returns:
      number of cached statements for this connection, or 0 if this interceptor instance is not bound to a pooled connection
    • createCacheKey

      protected org.apache.tomcat.jdbc.pool.interceptor.StatementCache.CacheKey createCacheKey(Method method, Object[] args)
      Creates a cache key from the given method and arguments.
      Parameters:
      method - the SQL method
      args - the method arguments
      Returns:
      the cache key
    • createCacheKey

      protected org.apache.tomcat.jdbc.pool.interceptor.StatementCache.CacheKey createCacheKey(String methodName, Object[] args)
      Creates a cache key from the given method name and arguments.
      Parameters:
      methodName - the SQL method name
      args - the method arguments
      Returns:
      the cache key, or null if the method name doesn't match a cached type