Skip to content

About

A lightweight, compile-time static-proxy based JDBC state-change listener library.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

Repository files navigation

Maven Central Javadoc License

🇬🇧 English | 🇨🇳 中文

jdbc-proxy

A lightweight, compile-time static-proxy based JDBC interception library. Provides interception capabilities for side-effect methods of objects such as Driver, Connection, Statement, and ResultSet.

✔ Intercept side-effect methods
✔ No bytecode enhancement
✔ No Java Agent
✔ No runtime dependencies
✔ Java 8+


One-line summary

Using static proxy classes generated at build time, JDBC method calls are intercepted at runtime and the corresponding listener interfaces receive the delegate and original parameters. Listeners are responsible for invoking the delegate, handling timing, exceptions, and return values as needed — the library no longer collects or forwards elapsed/exception/return information on the caller's behalf.


Maven coordinates

Maven:

<dependency>
  <groupId>io.github.arowa-z</groupId>
  <artifactId>jdbc-proxy</artifactId>
  <version>0.0.2</version>
</dependency>

Gradle:

implementation("io.github.arowa-z:jdbc-proxy:0.0.2")

The published package already includes all proxy and listener implementation code; consumers do not need to enable an annotationProcessor.


Quick start: listen to Statement.executeQuery

import io.github.arowa.z.JdbcProxy;
import io.github.arowa.z.JdbcStatement;
import io.github.arowa.z.OnStatementExecuteQuery;

import java.sql.*;

public class Main {
    public static void main(String[] args) throws Throwable {
        JdbcProxy proxy = JdbcProxy.of("proxy", new OnStatementExecuteQuery() {

            @Override
            public ResultSet onExecuteQuery(JdbcStatement source, Statement delegate, String sql) throws SQLException {
                Throwable error = null;
                long elapsed = System.nanoTime();
                try {
                    return delegate.executeQuery(sql);
                } catch (Throwable throwable) {
                    error = throwable;
                    throw throwable;
                } finally {
                    if (error != null) {
                        System.err.println("SQL failed: " + sql + ", err=" + error);
                    } else {
                        System.out.println("SQL succeeded: " + sql + ", elapsed (ms)=" + ((System.nanoTime() - elapsed)
                                / 1_000_000));
                    }
                }
            }
        });

        String proxyUrl = proxy.proxyDriver("jdbc:h2:mem:test");

        try (Connection connection = DriverManager.getConnection(proxyUrl);
             Statement statement = connection.createStatement();
             ResultSet resultSet = statement.executeQuery("SELECT 1")) {

            while (resultSet.next()) {
                System.out.println(resultSet.getObject(1));
            }
        }
    }
}

This example demonstrates the interception model: the listener receives the real delegate and must call it (and may measure elapsed time, catch exceptions, inspect/modify results, etc.) — the library does not perform or forward timing/exception/result bookkeeping for you.


Design goals

  • Strong interception capability for side-effect methods
  • Zero intrusion for callers
  • Zero intrusion for the JVM
  • Predictable behavior
  • Controllable performance

Key features

1️⃣ Static proxies (no dynamic enhancement)

  • No bytecode transformation
  • No classes generated at runtime
  • No reflection-based proxy chains
  • No Java Agent

All proxy code is generated at build time, and the final published package contains the complete implementation.

2️⃣ Interception design

Listeners now receive the delegate object and the original method parameters and are expected to invoke the delegate themselves (if appropriate). The library no longer provides or forwards helper metadata such as method elapsed time, exception objects, or return summaries. This gives consumers full control: measure time, handle exceptions, transform or wrap return values — everything is in your hands.

Note: because listeners can control invocation, use caution to preserve expected semantics and resource handling.

3️⃣ Listener naming convention

Listener interfaces follow the naming format On<ClassName><MethodName>, for example OnStatementExecuteQuery, OnConnectionClose, etc.

4️⃣ Zero runtime dependencies

Depends only on the standard JDBC API; no reliance on ASM, ByteBuddy, Spring, or other extra frameworks/configuration.


Use cases

  • SQL execution latency monitoring
  • Slow query aggregation
  • Error logging collection
  • Auditing records
  • Connection lifecycle statistics
  • Database call tracing / instrumentation

Compatibility

  • Java 8+
  • Compatible with mainstream JDBC drivers
  • Framework-agnostic

Performance notes

Because it uses static proxies, the base overhead is limited to method dispatch and listener invocation. The overall cost depends on listener implementations (for example, if listeners perform timing, synchronous I/O, or heavy processing). Keep listener work minimal for high-frequency scenarios.


Contributing

Issues and PRs are welcome. Please include reproducible examples, affected JDBC methods, and Java version information when possible.


License

Apache License 2.0


Contact

Author: Arowa_Z — Email: Arowa_Z@outlook.com

About

A lightweight, compile-time static-proxy based JDBC state-change listener library.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages