[Java] Proxy pour la journalisation des résultats SQL et SQL

C'est une réinvention car il y a log4jdbc, mais ... C'est un prérequis pour slf4j.

Vous pouvez utiliser le proxy simplement en faisant cela


conn = ConnectionLogProxy.createProxy(conn);

Un journal apparaîtra comme ceci

2018-02-22 18:58:10:123 INFO ConnectionLogProxy - 
### [INFO] query ###
com.mysql.jdbc.JDBC4PreparedStatement@16d2da0: SELECT * from userTbl
### [TRACE] result ###
userId	userName
100	taro
200	yamad

### [INFO] execute time ###
0.91824 ms

Classe proxy à enregistrer

import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**Proxy de connexion qui génère des journaux SQL*/
public class ConnectionLogProxy {
    /**Nombre maximum d'enregistrements de résultats SELECT à afficher dans le journal*/
    private static final int MAX_LOG_RECORD = 100;

    private Connection connection;

    private final static Logger LOGGER = LoggerFactory.getLogger(ConnectionLogProxy.class);

    /**
     *Créer un proxy.
     */
    public static Connection createProxy(Connection connection) {
        ConnectionLogProxy proxy = new ConnectionLogProxy();
        proxy.connection = connection;
        return Connection.class.cast(Proxy.newProxyInstance(
                Connection.class.getClassLoader(),
                new Class[]{ Connection.class },
                proxy.new ConnectionHandler()));
    }

    private class ConnectionHandler implements InvocationHandler {

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object obj = method.invoke(connection, args);

            if (obj instanceof PreparedStatement) {
                obj = new Delegate<PreparedStatement>(PreparedStatement.class.cast(obj), PreparedStatement.class).proxy;
            } else if (obj instanceof Statement) {
                obj = new Delegate<Statement>(Statement.class.cast(obj), Statement.class).proxy;
            }

            return obj;
        }
    }

    private class Delegate<T extends Statement> implements InvocationHandler {

        private T original;
        private Object proxy;

        private Delegate(T original, Class<T> clazz) {
            this.original = original;
            this.proxy = Proxy.newProxyInstance(
                clazz.getClassLoader(),
                new Class[]{ clazz },
                this);
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Class<?>[] paramTypes = method.getParameterTypes();

            long start = System.nanoTime();
            if ( "executeUpdate".equals(method.getName()) ){
                String query;
                if (paramTypes.length > 0) {
                    query = original + ": " + args[0];
                } else {
                    query = original + "";
                }
                Object resObj = method.invoke(original, args);
                long end = System.nanoTime();
                LOGGER.info("\n### [INFO] query ###\n{}\n### [INFO] execute time ###\n{} ms", query, Integer.parseInt("" + resObj), (end - start) / 1000000f );
                return resObj;
            }
            if( "executeQuery".equals(method.getName()) ) {
                Object resObj = method.invoke(original, args);
                ResultSet rs = ResultSet.class.cast(resObj);

                long end = System.nanoTime();
                //Les résultats SQL ne sont disponibles que lorsque le journal de suivi est activé
                if( LOGGER.isTraceEnabled() ) {
                    String csv = rsToCSV(rs);
                    LOGGER.info("\n### [INFO] query ###\n{}\n### [TRACE] result ####\n{}\n### [INFO] execute time ###\n{}ms",
                            original, csv, (end - start) / 1000000f );
                }else{
                    LOGGER.info("\n### [INFO] query ###\n{}\n### [INFO] execute time ###\n{} ms",
                            original, (end - start) / 1000000f );
                }
                return rs;
            }
            return method.invoke(original, args);
        }
    }

    /**Renvoyer ResultSet au format CSV*/
    private String rsToCSV(ResultSet rs) throws SQLException, IOException {    
        ResultSetMetaData metaData = rs.getMetaData();    
        int columnCount = metaData.getColumnCount();    
        try (StringWriter pw = new StringWriter()) {
            final String SEPARATOR = "\t";            
            for (int i = 1; i <= columnCount; i++) {    
                pw.write(metaData.getColumnName(i));        
                if (i < columnCount) {
                    pw.write(SEPARATOR);
                    pw.flush();
                }    
                if (i == columnCount) {
                    pw.write("\n");
                    pw.flush();
                }
            }    
            int rowCnt = 0;
            while (rs.next() ) {
                if( rowCnt >= MAX_LOG_RECORD ) {
                    pw.write("ommit over " + MAX_LOG_RECORD + " record log...\n");
                    pw.flush();
                    break;
                }    
                for (int i = 1; i <= columnCount; i++) {
                    Object val = rs.getObject(i);
                    if( null == val ){
                        pw.write(""+val);
                    }else{
                        pw.write(val.toString().replaceAll("\r", "\\r").replaceAll("\n", "\\n"));
                    }        
                    if (i < columnCount) {
                        pw.write(SEPARATOR);
                        pw.flush();
                    }        
                    if (i == columnCount) {
                        pw.write("\n");
                        pw.flush();
                    }
                }
                rowCnt++;
            }
            rs.first();
            rs.previous();
            return pw.toString();
        }
    }

}

Recommended Posts

[Java] Proxy pour la journalisation des résultats SQL et SQL
Instructions Java while et for
SDK AWS pour Java 1.11.x et 2.x
Java pour les débutants, les expressions et les opérateurs 1
Java pour les débutants, les expressions et les opérateurs 2
Classes et instances Java pour les débutants
[Pour les débutants] Différence entre Java et Kotlin
[À propos de JDBC qui connecte Java et SQL]
Pour l'apprentissage JAVA (2018-03-16-01)
Étudiez pendant 3 semaines et réussissez Java Bronze
Proxy dynamique Java
IDE 2017 pour Java
Java et JavaScript
XXE et Java
Java pour instruction
Échantillon jusqu'à l'authentification OAuth 2.0 et l'acquisition de jetons d'accès (Java)
Apprentissage pour la première fois des expressions et opérateurs Java # 3
Ceci et cela pour éditer ini en Java. : inieditor-java
Liste des instructions Java fréquemment utilisées (pour les débutants et les débutants)
Préparer l'environnement pour java11 et javaFx avec Ubuntu 18.4
[Java] pour instruction, while instruction
Getter et Setter (Java)
[Java] Thread et exécutable
[Java] Package de gestion
Java vrai et faux
[Java] pour instruction / étendu pour instruction
[Java] Comparaison des chaînes de caractères et && et ||
Contre-mesures pour OutOfMemoryError en java
PNL pour Java (NLP4J) (2)
Java - Sérialisation et désérialisation
[Java] Arguments et paramètres
(Mémo) Java pour instruction
PNL pour Java (NLP4J) (1)
[Java] Branchement et répétition
[Java] Types de variables et types
java (classe et instance)
[Java] Surcharge et remplacement
Stocker dans une carte Java 2D et tourner avec pour instruction
[Pour les débutants] Explication des classes, des instances et des statiques en Java
[Java] Rendre les variables de l'instruction for étendue et de chaque instruction immuables
Problèmes facilement confondus avec Java et JavaScript
Difficile de gérer les valeurs minimales avec Java8 LocalDateTime et Java6 Date
Conseils d'utilisation de Salesforce SOAP et de l'API Bulk en Java
Paramètres de renforcement d'installation et de complétion de code d'Eclipse (développement Mac pour Java)
Outils et commandes pouvant être utiles pour le dépannage Java
Java définit la couleur et l'image d'arrière-plan des documents PowerPoint