code: private void setUpDefaultSqlAuditorExecutorIfNecessary() {
          if (sqlAuditor != null && sqlAuditorExecutor == null) {
              sqlAuditorExecutor = createCustomExecutorService(1,
                      "setUpDefaultSqlAuditorExecutorIfNecessary");
              // 1. register executor for disposing later explicitly
              internalExecutorServiceRegistry.add(sqlAuditorExecutor);
              // 2. dispose executor implicitly 
              Runtime.getRuntime().addShutdownHook(new Thread() {
                  @Override
                  public void run() {
                      if (sqlAuditorExecutor == null) {
                          return;
                      }
                      try {
                          sqlAuditorExecutor.shutdown();
                          sqlAuditorExecutor.awaitTermination(5, TimeUnit.MINUTES);
                      } catch (InterruptedException e) {
                          logger.warn("interrupted when shuting down the query executor:\n{}", e);
                      }
                  }
              });
          }
      }
docs: if a SqlAuditor is injected and a sqlAuditorExecutor is NOT provided
  together, we need to setup a sqlAuditorExecutor so that the SQL auditing
  actions can be performed asynchronously. <br>
  otherwise, the data access process may be blocked by auditing SQL.<br>
  Although an external ExecutorService can be injected for use, normally,
  it's not so necessary.<br>
  Most of the time, you should inject an proper {@link ISqlAuditor} which
  will do SQL auditing in a asynchronous way.<br>