[Java] Proxy zum Protokollieren von SQL und SQL-Ergebnissen

Es ist eine Neuerfindung, weil es log4jdbc gibt, aber ... Dies ist eine Voraussetzung für slf4j.

Sie können den Proxy einfach so verwenden


conn = ConnectionLogProxy.createProxy(conn);

Ein Protokoll wird so angezeigt

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

Zu protokollierende Proxy-Klasse

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;

/**Verbindungsproxy, der SQL-Protokolle ausgibt*/
public class ConnectionLogProxy {
    /**Maximale Anzahl von SELECT-Ergebnisdatensätzen, die in das Protokoll ausgegeben werden sollen*/
    private static final int MAX_LOG_RECORD = 100;

    private Connection connection;

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

    /**
     *Proxy erstellen.
     */
    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();
                //SQL-Ergebnisse sind nur verfügbar, wenn das Ablaufverfolgungsprotokoll aktiviert ist
                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);
        }
    }

    /**ResultSet als CSV zurückgeben*/
    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 zum Protokollieren von SQL und SQL-Ergebnissen
Java während und für Anweisungen
AWS SDK für Java 1.11.x und 2.x.
Java für Anfänger, Ausdrücke und Operatoren 1
Java für Anfänger, Ausdrücke und Operatoren 2
Klassen und Instanzen Java für Anfänger
[Für Anfänger] Unterschied zwischen Java und Kotlin
[Über JDBC, das Java und SQL verbindet]
Für JAVA-Lernen (2018-03-16-01)
Studiere 3 Wochen und bestehe Java Bronze
Java Dynamic Proxy
2017 IDE für Java
Java und JavaScript
XXE und Java
Java für Anweisung
Beispiel für eine OAuth 2.0-Authentifizierung und Zugriffstokenerfassung (Java)
Zum ersten Mal lernen Java # 3 Ausdrücke und Operatoren
Dies und das zum Bearbeiten von ini in Java. : inieditor-java
Java Häufig verwendete Anweisungsliste (für Anfänger und Anfänger)
Bereiten Sie die Umgebung für Java11 und JavaFx mit Ubuntu 18.4 vor
[Java] für Anweisung, während Anweisung
Getter und Setter (Java)
[Java] Thread und ausführbar
[Java] Paket für die Verwaltung
Java wahr und falsch
[Java] für Anweisung / erweitert für Anweisung
[Java] Vergleich von Zeichenketten und && und ||
Gegenmaßnahmen für OutOfMemoryError in Java
NLP für Java (NLP4J) (2)
Java - Serialisierung und Deserialisierung
[Java] Argumente und Parameter
(Memo) Java für Anweisung
NLP für Java (NLP4J) (1)
[Java] Verzweigen und Wiederholen
[Java] Variablen- und Typtypen
Java (Klasse und Instanz)
[Java] Überladen und überschreiben
In Java 2D-Karte speichern und mit for-Anweisung drehen
[Für Anfänger] Erläuterung von Klassen, Instanzen und Statik in Java
[Java] Machen Sie die Variablen der erweiterten for-Anweisung und für jede Anweisung unveränderlich
Probleme, die leicht mit Java und JavaScript verwechselt werden können
Mit Java8 LocalDateTime und Java6 Date ist es schwierig, Mindestwerte zu verarbeiten
Tipps zur Verwendung von Salesforce SOAP und Bulk API in Java
Einstellungen für die Eclipse-Installation und die Verstärkung der Codevervollständigung (Mac für Java-Entwicklung)
Tools und Befehle, die für die Fehlerbehebung in Java hilfreich sein können
Java legt die Hintergrundfarbe und das Hintergrundbild für PowerPoint-Dokumente fest