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>3.0.0</version>
</dependency>- versions 3.x are intended for Spring 7.x / Java 17
- versions 2.x are intended for Spring 6.x / Java 17
- versions 1.x are intended for Spring 5.x / Java 11
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.PreparedStatementCreatorfail to implementorg.springframework.jdbc.core.SqlProvider. - rowCount
- In the case of a
SELECTthe number of rows returned. In the case of anUPDATEorDELETEthe number of rows affected.-1for a statement that does not return anything like a DDL.-2when 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
instanceofoperations and casts - a
finallyblock - a capturing lambda for
#queryForStreammethods to recordStream#closeas 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
Stringbut as aPreparedStatementCreatororCallableStatementCreatorit has to implementSqlProviderfor the query string to show up in the flight recording. JdbcTemplate#query(PreparedStatementCreator, PreparedStatementSetter, ResultSetExtractor)is not available because it is defined onJdbcTemplateand notJdbcOperations.- Several spring-jdbc classes
AbstractJdbcCall,SimpleJdbcCall,StoredProcedure,RdbmsOperation,AbstractJdbcInsert,SimpleJdbcInsertbut alsoJdbcTestUtilsandJdbcBeanDefinitionReaderrequire aJdbcTemplateand 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
