/** {@inheritDoc} */
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   // performance : on évite method.invoke pour equals & hashCode
   final String methodName = method.getName();
   if (isEqualsMethod(methodName, args)) {
     return areConnectionsEquals(args[0]);
   } else if (isHashCodeMethod(methodName, args)) {
     return connection.hashCode();
   }
   try {
     Object result = method.invoke(connection, args);
     if (result instanceof Statement) {
       final String requestName;
       if ("prepareStatement".equals(methodName) || "prepareCall".equals(methodName)) {
         // la méthode est du type prepareStatement(String) ou prepareCall(String),
         // alors la requête sql est le premier argument
         requestName = (String) args[0];
       } else {
         requestName = null;
       }
       result = createStatementProxy(requestName, (Statement) result);
     }
     return result;
   } finally {
     if ("close".equals(methodName) && !alreadyClosed) {
       USED_CONNECTION_COUNT.decrementAndGet();
       USED_CONNECTION_INFORMATIONS.remove(
           ConnectionInformations.getUniqueIdOfConnection(connection));
       alreadyClosed = true;
     }
   }
 }
 void init() {
   // on limite la taille pour éviter une éventuelle saturation mémoire
   if (isConnectionInformationsEnabled()
       && USED_CONNECTION_INFORMATIONS.size() < MAX_USED_CONNECTION_INFORMATIONS) {
     USED_CONNECTION_INFORMATIONS.put(
         ConnectionInformations.getUniqueIdOfConnection(connection),
         new ConnectionInformations());
   }
   USED_CONNECTION_COUNT.incrementAndGet();
   TRANSACTION_COUNT.incrementAndGet();
 }