-
Notifications
You must be signed in to change notification settings - Fork 4
SQL annotations
Take control of SQL statements sent to the database
Recommended global annotations
Cancel the behavior of global annotations
Recommended method annotations
To check that the configuration is properly done, you can try to add an annotation on a test method in order 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. If you seem to have twice more (or several times more) statements than expected, check that you don't have this messsage in the console:
[WARNING] QuickPerf has built several datasource proxies
For example, you may have declared twice a QuickPerfProxyBeanPostProcessor bean in your Spring configuration:
@Bean
public QuickPerfProxyBeanPostProcessor quickPerfProxyBeanPostProcessor1() {
return new QuickPerfProxyBeanPostProcessor();
}
@Bean
public QuickPerfProxyBeanPostProcessor quickPerfProxyBeanPostProcessor2() {
return new QuickPerfProxyBeanPostProcessor();
}
Use global annotations or method annotations. See the workflow part to see ways to work with SQL annotations.
The SQL annotations automatically detect if Hibernate or Spring frameworks are used. You don't have any configuration to do. If a SQL property is not respected, the SQL annotations can suggest you solutions to fix it with these frameworks.
For example, the following message is diplayed when a 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
You can take care of several things about SQL statements to promote performance and scalability at the beginning of application development.
-
JDBC roundtrips
- Detect N+1 selects by using @ExpectSelect, @ExpectMaxSelect or @DisableSameSelectTypesWithDifferentParams
- Detect JDBC batching disabled by using @ExpectJdbcBatching
- Detect exactly same selects by using @DisableExactlySameSelects
- Detect N+1 selects by using @ExpectSelect, @ExpectMaxSelect or @DisableSameSelectTypesWithDifferentParams
-
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
-
SQL statements having a LIKE pattern starting with a wildcard by using @DisableLikeWithLeadingWildcard
-
Avoid cross join generated by Criteria API by using @DisableCrossJoin
-
...
A SqlAnnotationBuilder class is available to easily implement SpecifiableGlobalAnnotations.
package org.quickperf;
import org.quickperf.config.user.SpecifiableGlobalAnnotations;
import org.quickperf.sql.annotation.SqlAnnotationBuilder;
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( disableSameSelectTypesWithDifferentParams() // can reveal some N+1 selects
, disableExactlySameSelects() // can reveal a bad use of Hibernate session
, expectJdbcBatching()
//, disableCrossJoin() // if you use JPA Criteria API
, disableLikeWithLeadingWildcard()
);
}
}
The class implementing SpecifiableGlobalAnnotations has to be in org.quickperf package.
Verify that SQL statements do not contain LIKE with leading wildcard (% or _).
You can read this article explaining why LIKE with leading wildcard could be a bad idea in term of performance.
A code sending to the database a like with leading wilcard may be fast in a test having a few data but very slow with the data volume of production.
Verify that inserts, deletes and updates are processed in JDBC batches having batchSize elements. You may sometimes think that you are using JDBC batching but in fact not (Paper 1, Paper 2)! Batching of inserts, updates and deletes allows to reduce the number of roundtrips to the database which can dramatically impact application performance. You can decide to batch all inserts, updates, delete. Prior to Hibernate 5.2, batching, when enabled with a hibernate.jdbc.batch_size property stricly positive, was applied to all inserts, updates and deletes (from Hibernate 5.2 it is also possible to override the batch size value for a given session).
Parameter | Type | Meaning | Default value |
---|---|---|---|
batchSize | int | JDBC batch size | - |
batchSize is optional.
A 0 batch size means that JDBC batching is disabled.
@ExpectJdbcBatching(batchSize = 30)
Cross join can be generated by JPA Criteria API and can impact performance.
Cancel behavior of @DisableExactlySameSelects.
Parameter | Type | Meaning | Default value |
---|---|---|---|
comment | String | Comment why exactly same selects are enabled | - |
Cancel behavior of @DisableSameSelectTypesWithDifferentParams.
Parameter | Type | Meaning | Default value |
---|---|---|---|
comment | String | Comment why exactly same select types with different parameters are enabled | - |
Cancel behavior of @DisableCrossJoin.
Parameter | Type | Meaning | Default value |
---|---|---|---|
comment | String | Comment why cross join is enabled | - |
Cancel behavior of @DisableLikeWithLeadingWildcard.
Parameter | Type | Meaning | Default value |
---|---|---|---|
comment | String | Comment why like with leading wildcard is enabled | - |
Indicate disabling of JDBC batching.
Parameter | Type | Meaning | Default value |
---|---|---|---|
value | int | Number of select statements | 0 |
@ExpectSelect(1)
@Test
public void should_retrieve_all_cars() {
//...
}
With this annotation, the test will fail if the number of SELECT statements is greater than expected.
Parameter | Type | Meaning | Default value |
---|---|---|---|
value | int | Maximum number of selects | 0 |
@ExpectMaxSelect(1)
@Test
public void should_retrieve_all_cars() {
//...
}
Verifies the number of selected columns.
Why limit the number of selected columns?
Parameter | Type | Meaning | Default value |
---|---|---|---|
value | int | Number of selected columns | 0 |
@ExpectSelectedColumn(3)
With this annotation, the test will fail if the number of returned columns is greater than expected.
Why limit the number of selected columns?
Parameter | Type | Meaning | Default value |
---|---|---|---|
value | int | Maximum number of returned columns | 0 |
@ExpectMaxSelectedColumn(5)
Parameter | Type | Meaning | Default value |
---|---|---|---|
value | int | Number of insert statements | 0 |
Parameter | Type | Meaning | Default value |
---|---|---|---|
value | int | Number of update statements | 0 |
Parameter | Type | Meaning | Default value |
---|---|---|---|
value | int | Number of delete statements | 0 |
With this annotation the SQL statements are diplayed in the console during the execution of the test method body.
Compared to @DisplaySql, this annotation does not diplay SQL statements before (JUnit 4: @Before, @BeforeClass) and after (JUnit 4: @After, @AfterClass) the execution of the test method body.
With this annotation the SQL statements are diplayed in the console during the test execution.
Compared to @DisplaySqlOfTestMethodBody, this annotation also diplays SQL statements before (JUnit 4: @Before, @BeforeClass) and after (JUnit 4: @After, @AfterClass) the execution of the test method body.
π Β 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