An implementation of Spring JdbcTemplate that generates Flight Recorder events.
This project requires Java 11 based on OpenJDK or later.
<dependency>
<groupId>com.github.marschall</groupId>
<artifactId>jfr-jdbctemplate</artifactId>
<version>2.0.0</version>
</dependency>
Versions 1.x are intended for Spring 5.x / Java 11, versions 2.x are intended for Spring 6.x / Java 17.
Compared to approaches based on DataSource
an approach based on JdbcTemplate
has the advantage that it captures a complete database interaction. For example if you process many rows the initial PreparedStatement#execute()
might be fast but most of the time may be spent in ResultSet#next()
. A JdbcTemplate
based approach generates a single JFR event for the entire interaction that involves several JDBC method invocations.
Spring Class | JFR Class |
---|---|
org.springframework.jdbc.core.JdbcOperations |
com.github.marschall.jfr.jdbctemplate.JfrJdbcOperations |
org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations |
com.github.marschall.jfr.jdbctemplate.JfrNamedParameterJdbcOperations |
- operationName
- The name of the execute JDBC operation, this corresponds to the method name on
JdbcOperations
/JdbcTemplate
. - query
- The SQL query string passed to the JDBC driver. May be missing especially if custom
org.springframework.jdbc.core.PreparedStatementCreator
fail to implementorg.springframework.jdbc.core.SqlProvider
. - rowCount
- In the case of a
SELECT
the number of rows returned. In the case of anUPDATE
orDELETE
the number of rows affected.-1
for a statement that does not return anything like a DDL.-2
when no information about the number of rows is available.
We try to keep overhead to a minimum and have no additional allocations besides the JFR events. Besides the overhead of the event the only additional overhead is:
- a wrapper around
JdbcTemplate
- a few
instanceof
operations and casts - a
finally
block - a capturing lambda for
#queryForStream
methods to recordStream#close
as the end time of the event - a small wrapper around every
RowCallbackHandler
We assume org.springframework.jdbc.core.SqlProvider#getSql()
is a simple getter.
@Configuration
public class JdbcConfiguration {
@Autowired
private DataSource dataSource;
@Bean
public JdbcOperations jdbcOperations() {
return new JfrJdbcOperations(new JdbcTemplate(this.dataSource));
}
@Bean
public NamedParameterJdbcOperations namedParameterJdbcOperations() {
return new JfrNamedParameterJdbcOperations(new NamedParameterJdbcTemplate(this.jdbcOperations()));
}
}
You need something like the following JVM options to run Flight Recorder
-XX:StartFlightRecording:filename=recording.jfr
-XX:FlightRecorderOptions:stackdepth=128
- When the SQL query is not provided as a
String
but as aPreparedStatementCreator
orCallableStatementCreator
it has to implementSqlProvider
for the query string to show up in the flight recording. JdbcTemplate#query(PreparedStatementCreator, PreparedStatementSetter, ResultSetExtractor)
is not available because it is defined onJdbcTemplate
and notJdbcOperations
.- Several spring-jdbc classes
AbstractJdbcCall
,SimpleJdbcCall
,StoredProcedure
,RdbmsOperation
,AbstractJdbcInsert
,SimpleJdbcInsert
but alsoJdbcTestUtils
andJdbcBeanDefinitionReader
require aJdbcTemplate
and do not work withJdbcOperations
. We have a pull request open for this but it has not been merged yet. JdbcOperations#execute(ConnectionCallback)
can not provide any insight into what is executed inside, that would require integration with marschall/jfr-jdbc