-
Notifications
You must be signed in to change notification settings - Fork 74
Detail
Documentation has moved to own html from wiki.
Leaving old wiki just for reference.
ProxyDataSource
is a java class that implements javax.sql.DataSource
.
This class works as an entry point for handling(intercepting) all invocations to java.sql.Statement
, java.sql.PreparedStatement
,
and java.sql.CallableStatement
.
When constructing ProxyDataSource
, it takes QueryExecutionListener
and actual datasource.
In your application, it behaves as a regular DataSource, but under the hood, it creats proxies and intercept the query executions and invoke QueryExecutionListener
before
and after calling the actual DB.
To construct ProxyDataSource
, ProxyDataSourceBuilder
class provides fluent API for not only programmatic
construction, but also for enabling built-in QueryExecutionListener
(e.g. query and parameter logging).
If you use XML based configuration in spring framework, ProxyDataSource
as well as listener classes are also
designed to be used in xml directly.
ProxyDataSource
can also be constructed via JNDI.
The container, for example tomcat, needs to have datasource-proxy.jar
, but your application may not need to have jar
dependency because actual implementation class of datasource may not matter to application. For example, if you want
to log query executions, your application doesn't have any dependency to classes in datasource-proxy.jar
.
Java
DataSource dataSource =
ProxyDataSourceBuilder
.create(actualDataSource)
.logQueryToSysOut()
.asJson()
.build();
XML
<bean id="dataSource" class="net.ttddyy.dsproxy.support.ProxyDataSource">
<property name="dataSource" ref="[ACTUAL DATASOURCE BEAN]"/>
<property name="listener" ref="listener"/>
</bean>
<bean id="listener" class="net.ttddyy.dsproxy.listener.CommonsQueryLoggingListener">
<property name="logLevel" value="INFO"/> <!-- Default: DEBUG -->
<property name="writeAsJson" value="true"/> <!-- Default: false -->
</bean>
<!-- For multiple listeners -->
<!--
<bean id="listener" class="net.ttddyy.dsproxy.listener.ChainListener">
<property name="listeners">
<list>
<bean class="net.ttddyy.dsproxy.listener.CommonsQueryLoggingListener">
<bean class="net.ttddyy.dsproxy.listener.SLF4JQueryLoggingListener">
<bean class="net.ttddyy.dsproxy.listener.SystemOutQueryLoggingListener">
<bean class="net.ttddyy.dsproxy.listener.DataSourceQueryCountListener"/>
</list>
</property>
</bean>
-->
Note:
You can usenet.ttddyy.dsproxy.listener.ChainListener
for injecting multiple listeners to theProxyDataSource
.ChainListener
implementsQueryExecutionListener
interface and takes list of listeners. (composition pattern)
JNDI
<Resource name="jdbc/global/myProxy"
auth="Container"
type="net.ttddyy.dsproxy.support.ProxyDataSource"
factory="net.ttddyy.dsproxy.support.jndi.ProxyDataSourceObjectFactory"
description="ds"
listeners="commons,org.example.SampleListener"
proxyName="MY-PROXY"
format="json"
dataSource="[REFERENCE_TO_ACTUAL_DATASOURCE_RESOURCE]" <!-- ex: java:jdbc/global/myDS -->
/>
QueryExecutionListener
is an interface class that you can implement your own logic which will be performed before/after the actual query call.
datasource-proxy comes with QueryExecutionListener
implementation classes for logging query executions(query and parameters) and query metrics.
public interface QueryExecutionListener {
void beforeQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList);
void afterQuery(ExecutionInfo execInfo, List<QueryInfo> queryInfoList);
}
ProxyDataSource
takes QueryExecutionListener
.
Based on how queries are executed, beforeQuery
/afterQuery
methods parameters become followings:
For Normal Statement
- one
QueryInfo
inqueryInfoList
JDBC API sample:
statement.executeQuery(...);
For Batch Statement
- multiple
QueryInfo
inqueryInfoList
JDBC API sample:
statement.addBatch(...);
statement.addBatch(...);
statement.executeBatch(...);
For Normal Prepared/Callable Statement
- one
QueryInfo
inqueryInfoList
- one entry in
queryArgsList
inQueryInfo
JDBC API sample:
PreparedStatement ps = connection.prepareStatement(...);
ps.setInt(1, ...);
ps.executeQuery();
For Batch Prepared/Callable Statement
- one
QueryInfo
inqueryInfoList
- multiple entries in
queryArgsList
inQueryInfo
JDBC API sample:
PreparedStatement ps = connection.prepareStatement(...);
ps.setInt(1, ...);
ps.addBatch();
ps.setInt(1, ...);
ps.addBatch();
ps.executeBatch();
These listeners implement QueryExecutionListener
and write out queries and query parameters using commons, slf4j or sysout.
net.ttddyy.dsproxy.listener.CommonsQueryLoggingListener
net.ttddyy.dsproxy.listener.SLF4JQueryLoggingListener
net.ttddyy.dsproxy.listener.SystemOutQueryLoggingListener
"output log level", "output format(JSON)", etc. are configurable.
Output example
Name:MyDS, Time:1, Success:True, Type:Prepared, Batch:True, QuerySize:1, BatchSize:2, Query:["INSERT INTO users (id, name) VALUES (?, ?)"], Params:[(1=1,2=foo),(1=2,2=bar)]
As JSON
{"name":"MyDS", "time":1, "success":true, "type":"Prepared", "batch":true, "querySize":1, "batchSize":2, "query":["INSERT INTO users (id, name) VALUES (?, ?)"], "params":[{"1":"1","2":"foo"},("1":"2","2":"bar")]}
datasource-proxy collects following query metrics:
- num of queries by query-type(select, insert, update, delete)
- num of queries by statement-type(statement, prepared, callable)
- num of database calls (total, success, failure)
- total time to run queries
To log query metrics, you need to configure 1) DataSourceQueryCountListener
to collect query execution metrics,
and 2) QueryCountLogger to write out metrics to logger(commons, slf4j, sysout, etc).
net.ttddyy.dsproxy.QueryCount
holds all query metrics per datasource.
net.ttddyy.dsproxy.QueryCountHolder
provides static method access to QueryCount
.
Default implementation uses thread local to store the query counts.
Output example
Name:"MyProxy", Time:6, Total:1, Success:1, Failure:0, Select:1, Insert:0, Update:0, Delete:0, Other:0, Statement:1, Prepared:0, Callable:0
As JSON
{"name":"MyProxy", "time":6, "total":1, "success":1, "failure":0, "select":1, "insert":0, "update":0, "delete":0, "other":0, "statement":1, "prepared":0, "callable":0}
Every database call, DataSourceQueryCountListener
collects query metrics.
Add the DataSourceQueryCountListener
to ProxyDataSource
.
DataSource dataSource =
ProxyDataSourceBuilder
.create(actualDataSource)
.count() // add `DataSourceQueryCountListener` to ProxyDataSource
.build();
There are multiple implementations to output query metrics into loggers(commons, log4j) or system-out.
Builder class net.ttddyy.dsproxy.support.QueryCountLoggerBuilder
is also available for creating QueryCountLogger
.
Servlet Filter (javax.servlet.Filter
):
net.ttddyy.dsproxy.support.CommonsQueryCountLoggingServletFilter
net.ttddyy.dsproxy.support.SLF4JQueryCountLoggingServletFilter
net.ttddyy.dsproxy.support.SystemOutQueryCountLoggingServletFilter
Servlet Request Listener (javax.servlet.ServletRequestListener
):
net.ttddyy.dsproxy.support.CommonsQueryCountLoggingRequestListener
net.ttddyy.dsproxy.support.SLF4JQueryCountLoggingRequestListener
Spring HandlerInterceptor (org.springframework.web.servlet.HandlerInterceptor
):
net.ttddyy.dsproxy.support.CommonsQueryCountLoggingHandlerInterceptor
net.ttddyy.dsproxy.support.SLF4JQueryCountLoggingHandlerInterceptor
net.ttddyy.dsproxy.support.SystemOutQueryCountLoggingHandlerInterceptor
To access query metrics in jsp, there is a custom tag.
<%@ taglib prefix="dsp" uri="http://www.ttddyy.net/dsproxy/tags" %>
<dsp:metrics metric="select"/> - Select
<dsp:metrics metric="update" dataSource="FOO" /> - Num of update queries for datasource FOO
<dsp:metrics metric="total"/> - Total Queries
<dsp:metrics metric="call"/> - Num of DB Call
<dsp:metrics metric="elapsedTime"/> - Total TIme
net.ttddyy.dsproxy.transform.QueryTransformer
and net.ttddyy.dsproxy.transform.ParameterTransformer
allows you to
modify executing query statement and/or parameters.
ProxyDataSourceBuilder
.create(actualDataSource)
.queryTransformer(myQueryTransformer)
.parameterTransformer(myParameterTransformer)
.build();
<bean id="dataSource" class="net.ttddyy.dsproxy.support.ProxyDataSource">
<property name="dataSource" ref="[ACTUAL DATASOURCE BEAN]"/>
<property name="interceptorHolder" ref="interceptorHolder"/>
</bean>
<bean id="interceptorHolder" class="net.ttddyy.dsproxy.proxy.InterceptorHolder">
<property name="listener" ref="..."/>
<property name="queryTransformer" ref="..."/>
<property name="parameterTransformer" ref="..."/>
</bean>
There are example projects here: https://github.com/ttddyy/datasource-proxy-examples