-
Notifications
You must be signed in to change notification settings - Fork 4
SQL annotations
How to promote performance with SQL annotations?
Guidelines to use the SQL annotations
To promote performance at the beginning of application development, you can
-
Limit JDBC roundtrips
-
Detect N+1 selects by using @ExpectSelect, @ExpectMaxSelect or @DisableSameSelectTypesWithDifferentParamValues
-
Detect JDBC batching disabled by using @ExpectJdbcBatching
-
Detect the same selects by using @DisableSameSelects
-
-
Limit fetched data
-
Detect too many selected columns by using @ExpectSelectedColumn or @ExpectMaxSelectedColumn
Why limit the number of selected columns?
-
Detect too many selected columns by using @ExpectSelectedColumn or @ExpectMaxSelectedColumn
-
Reduce SQL queries execution time
- Spot long queries with @ExpectMaxQueryExecutionTime
- Avoid SQL statements without bind parameters. Detect them by using @DisableQueriesWithoutBindParameters
- Avoid SQL statements having a LIKE pattern starting with a wildcard. Identify them by using @DisableLikeWithLeadingWildcard
-
Avoid database connection leaks by using @ExpectNoConnectionLeak
-
...
π Β Spring (JUnit 4, JUnit 5) Β Β Β Β Β Β Β Β Β π JUnit 4
π Β Micronaut Β Preview/Example Β Β Β Β Β π JUnit 5 example
π Β Quarkus Β Preview/Example Β Β Β Β Β Β :point_right: TestNG example
You can add an annotation on a test method to make it fail. For example, add @ExpectSelect(0) on a test method that is supposed to send one or several selects to the database.
You can use SQL annotations with a global scope, a class scope, or a method scope.
The SQL annotations automatically detect the presence of Hibernate and Spring frameworks. These annotations can propose solutions to get the expected behavior with these frameworks.
For example, QuickPerf displays the following message when an N+1 select is presumed, and Spring Data JPA is detected:
* With Spring Data JPA, you may fix it by adding
@EntityGraph(attributePaths = { "..." }) on repository method.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.entity-graph
Disable global annotations at the method level
Apply SQL annotations on methods
You may want to quickly promote performance without adding some SQL annotations on each test method. Or let's suppose that you add QuickPerf to an application having automatic tests.
Annotations with a global scope, also called global annotations, apply to each test. Configuring SQL global annotations can allow you to detect and fix some performance-sensitive properties promptly.
We recommend configuring the following SQL global annotations:
Annotation | Short description |
---|---|
@DisableSameSelectTypesWithDifferentParamValues | Disables same SELECT statements with different parameter values => N+1 select detection |
@ExpectJdbcBatching | JDBC batching is enabled |
@ExpectMaxQueryExecutionTime | Max query execution time |
@DisableStatements | Disables java.sql.Statement |
@DisableQueriesWithoutBindParameters | Disables queries without bind variables |
@DisableLikeWithLeadingWildcard | Disables like with leading wildcard |
@ExpectNoConnectionLeak | Detects database connection leaks |
A SqlAnnotationBuilder class allows you to configure SQL global annotations easily.
β The class implementing SpecifiableGlobalAnnotations
has to be in the org.quickperf
package.
package org.quickperf;
import org.quickperf.config.SpecifiableGlobalAnnotations;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collection;
import static org.quickperf.sql.annotation.SqlAnnotationBuilder.*;
public class QuickPerfConfiguration implements SpecifiableGlobalAnnotations {
public Collection<Annotation> specifyAnnotationsAppliedOnEachTest() {
return Arrays.asList(
// Can reveal some N+1 selects
// https://blog.jooq.org/2017/12/18/the-cost-of-jdbc-server-roundtrips/
disableSameSelectTypesWithDifferentParamValues()
// Sometimes, JDBC batching can be disabled with Hibernate:
// https://abramsm.wordpress.com/2008/04/23/hibernate-batch-processing-why-you-may-not-be-using-it-even-if-you-think-you-are/
// https://stackoverflow.com/questions/27697810/hibernate-disabled-insert-batching-when-using-an-identity-identifier
, expectJdbcBatching()
// https://use-the-index-luke.com/sql/where-clause/searching-for-ranges/like-performance-tuning
, disableLikeWithLeadingWildcard()
// Not relevant with an in-memory database used for testing purpose
, expectMaxQueryExecutionTime(30)
, disableStatements()
, disableQueriesWithoutBindParameters()
, expectNoConnectionLeak()
);
}
}
In some specific cases, you may want to disable some global annotations. You can use the following annotations to disable some recommended global annotations for some test methods:
Annotation | Short description |
---|---|
@EnableSameSelectTypesWithDifferentParamValues | Cancels behavior of @DisableSameSelectTypesWithDifferentParamValues |
@ExpectJdbcBatching(batchSize=0) | Cancels behavior of @ExpectJdbcBatching |
@EnableStatements | Cancels behavior of @DisableStatements |
@EnableQueriesWithoutBindParameters | Cancels behavior of @DisableQueriesWithoutBindParameters |
@EnableLikeWithLeadingWildcard | Cancels behavior of @DisableLikeWithLeadingWildcard |
Suppose you are developing a new feature, perhaps with the help of Test-Driven Development (TDD). The test may fail because the business property does not work. The test may also fail because of failing performance-related properties. We think it is easier to focus on the business behavior (the functional property), and after that to work on performance-related properties, to do one step at a time. You can temporarily disable QuickPerf global annotations by applying @FunctionalIteration, or @DisableQuickPerf, or @DisableGlobalAnnotations at the method level.
In addition to the performance properties verified by the global annotations, you can check others for some test methods.
The annotations added to the test methods can help to document the code. For example, by reading @ExpectSelect(1)
annotation applied on a test method, you know that you expect exactly one select sent to the database.
Among all the SQL annotations, we suggest using the following ones on the test methods:
Annotation | Short description |
---|---|
@ExpectJdbcQueryExecution | JDBC query execution number |
@ExpectMaxQueryExecutionTime | Max query execution time |
@ExpectSelectedColumn | Selected columns number |
@ExpectUpdatedColumn | Updated columns number |
@ExpectMaxSelectedColumn | Max selected columns number |
@ExpectMaxUpdatedColumn | Max updated columns |
@ExpectSelect | SELECT number |
@ExpectInsert | INSERT number |
@ExpectUpdate | UPDATE number |
@ExpectDelete | DELETE number |
Testcontainers is useful to write tests with the same type of database used in production.
QuickPerf can work with Testcontainers.
Several project examples combining Testcontainers and QuickPerf are available here.
Annotation | Short description |
---|---|
@ExpectSelect | SELECT number |
@ExpectMaxSelect | Max SELECT number |
@ExpectSelectedColumn | Selected columns number |
@ExpectMaxSelectedColumn | Max selected columns number |
@DisableSameSelects | Disables exactly same SELECT statements |
@EnableSameSelects | Enables exactly same SELECT statements |
@DisableSameSelectTypesWithDifferentParamValues | Disables same SELECT statements with different parameter values |
@EnableSameSelectTypesWithDifferentParamValues | Enables same SELECT statements with different parameter values |
Annotation | Short description |
---|---|
@ExpectInsert | INSERT number |
@ExpectMaxInsert | Max INSERT number |
Annotation | Short description |
---|---|
@ExpectDelete | DELETE number |
@ExpectMaxDelete | Max DELETE number |
Annotation | Short description |
---|---|
@ExpectUpdate | UPDATE number |
@ExpectMaxUpdate | Max UPDATE number |
@ExpectUpdatedColumn | Updated columns number |
@ExpectMaxUpdatedColumn | Max updated columns number |
Annotation | Short description |
---|---|
@DisplaySql | Displays SQL |
@DisplaySqlOfTestMethodBody | Displays SQL executed in the test method body |
You can also use @DisplayAppliedAnnotations in debug activity.
Annotation | Short description |
---|---|
@ExpectJdbcQueryExecution | JDBC query execution number |
@ExpectMaxJdbcQueryExecution | Max JDBC query execution number |
@ExpectJdbcBatching | JDBC batching is enabled |
@ExpectMaxQueryExecutionTime | Max query execution time |
@DisableStatements | Disables java.sql.Statement |
@EnableStatements | Enablesjava.sql.Statement |
@DisableLikeWithLeadingWildcard | Disables like with leading wildcard |
@EnableLikeWithLeadingWildcard | Enables like with leading wildcard |
@DisableQueriesWithoutBindParameters | Disables queries without bind variables |
@EnableQueriesWithoutBindParameters | Enables queries without bind variables |
@ExpectNoConnectionLeak | Detects database connection leaks |
@ProfileConnection | Profiles connection |
@AnalyzeSql | Builds an analysis of the SQL executed in the test method |
π Β Core
π Β JVM
π Β SQL
π Β Scopes
π Β Create an annotation
π Β JUnit 4
π Β JUnit 5
π Β TestNG
π Β Spring
π Β Detect and fix N+1 SELECT
π Β Maven performance
π Β Spring Boot - JUnit 4
π Β Spring Boot - JUnit 5
π Β Micronaut Data - JUnit 5
π Β Micronaut - Spring - JUnit 5
π Β Quarkus - JUnit 5
π Β FAQ
π Β QuickPerf code