1
- [ ![ Language grade: Java] ( https://img.shields.io/lgtm/grade/java/g/Lemick/hibernate-spring-sql-query-count.svg?logo=lgtm&logoWidth=18 )] ( https://lgtm.com/projects/g/Lemick/hibernate-spring-sql-query-count/context:java )
2
1
# Hibernate SQL Query Assertions for Spring
3
2
4
3
Hibernate is a powerful ORM, but you need to have control over the executed SQL queries to avoid ** huge performance problems** (N+1 selects, batch insert not working...)
@@ -15,7 +14,7 @@ A full-working demo of the examples below [is available here](https://github.com
15
14
16
15
* Tested versions* : Hibernate 5 & 6
17
16
18
- ### Assert SQL statements
17
+ ### Assert SQL statements declaratively
19
18
20
19
You just have to add the ` @AssertHibernateSQLCount ` annotation to your test and it will verify the SQL statements (SELECT, UPDATE, INSERT, DELETE) count at the end of the test :
21
20
@@ -37,67 +36,93 @@ void create_two_blog_posts() {
37
36
```
38
37
If the actual count is different, an exception is thrown with the executed statements:
39
38
```
40
- com.mickaelb.assertions.HibernateAssertCountException:
41
- Expected 5 INSERT but got 6:
42
- => '/* insert com.lemick.demo.entity.BlogPost */ insert into blog_post (id, title) values (default, ?)'
43
- => '/* insert com.lemick.demo.entity.PostComment */ insert into post_comment (id, blog_post_id, content) values (default, ?, ?)'
44
- => '/* insert com.lemick.demo.entity.PostComment */ insert into post_comment (id, blog_post_id, content) values (default, ?, ?)'
45
- => '/* insert com.lemick.demo.entity.BlogPost */ insert into blog_post (id, title) values (default, ?)'
46
- => '/* insert com.lemick.demo.entity.PostComment */ insert into post_comment (id, blog_post_id, content) values (default, ?, ?)'
47
- => '/* insert com.lemick.demo.entity.PostComment */ insert into post_comment (id, blog_post_id, content) values (default, ?, ?)'
39
+ com.mickaelb.assertions.HibernateAssertCountException:
40
+ Expected 5 INSERT but got 6:
41
+ => '/* insert com.lemick.demo.entity.BlogPost */ insert into blog_post (id, title) values (default, ?)'
42
+ => '/* insert com.lemick.demo.entity.PostComment */ insert into post_comment (id, blog_post_id, content) values (default, ?, ?)'
43
+ => '/* insert com.lemick.demo.entity.PostComment */ insert into post_comment (id, blog_post_id, content) values (default, ?, ?)'
44
+ => '/* insert com.lemick.demo.entity.BlogPost */ insert into blog_post (id, title) values (default, ?)'
45
+ => '/* insert com.lemick.demo.entity.PostComment */ insert into post_comment (id, blog_post_id, content) values (default, ?, ?)'
46
+ => '/* insert com.lemick.demo.entity.PostComment */ insert into post_comment (id, blog_post_id, content) values (default, ?, ?)'
48
47
```
49
- ### Assert L2C statistics
48
+
49
+ ### Assert SQL statements programmatically
50
+
51
+ You can also assert statements from several transactions in your test using the programmatic API:
52
+ ``` java
53
+ @Test
54
+ void multiple_assertions_using_programmatic_api() {
55
+ QueryAssertions . assertInsertCount(2 , () - > {
56
+ BlogPost post_1 = new BlogPost (" Blog post 1" );
57
+ post_1. addComment(new PostComment (" Good article" ));
58
+ blogPostRepository. save(post_1);
59
+ });
60
+
61
+ QueryAssertions . assertSelectCount(1 , () - > blogPostRepository. findById(1L ));
62
+
63
+ // Or even multiple asserts at once
64
+ QueryAssertions . assertStatementCount(Map . of(INSERT , 2 , SELECT , 1 ), () - > {
65
+ BlogPost post_1 = new BlogPost (" Blog post 1" );
66
+ post_1. addComment(new PostComment (" Good article" ));
67
+ blogPostRepository. save(post_1);
68
+
69
+ blogPostRepository. findById(1L );
70
+ });
71
+ }
72
+ ```
73
+
74
+ ### Assert L2C statistics declaratively
50
75
51
76
It supports assertions on Hibernate level two cache statistics, useful for checking that your entities are cached correctly and that they will stay forever:
52
77
``` java
53
- @Test
54
- @AssertHibernateL2CCount (misses = 1 , puts = 1 , hits = 1 )
55
- void create_one_post_and_read_it() {
56
- doInTransaction(() - > {
57
- BlogPost post_1 = new BlogPost (" Blog post 1" );
58
- blogPostRepository. save(post_1);
59
- });
60
-
61
- doInTransaction(() - > {
62
- blogPostRepository. findById(1L ); // 1 MISS + 1 PUT
63
- });
64
-
65
- doInTransaction(() - > {
66
- blogPostRepository. findById(1L ); // 1 HIT
67
- });
68
- }
78
+ @Test
79
+ @AssertHibernateL2CCount (misses = 1 , puts = 1 , hits = 1 )
80
+ void create_one_post_and_read_it() {
81
+ doInTransaction(() - > {
82
+ BlogPost post_1 = new BlogPost (" Blog post 1" );
83
+ blogPostRepository. save(post_1);
84
+ });
85
+
86
+ doInTransaction(() - > {
87
+ blogPostRepository. findById(1L ); // 1 MISS + 1 PUT
88
+ });
89
+
90
+ doInTransaction(() - > {
91
+ blogPostRepository. findById(1L ); // 1 HIT
92
+ });
93
+ }
69
94
```
70
95
## How to integrate
71
96
1 . Import the dependency
72
- ```xml
73
- <dependency>
74
- <groupId>com.mickaelb</groupId>
75
- <artifactId>hibernate-query-asserts</artifactId>
76
- <version>2.0.0</version>
77
- </dependency>
78
- ```
97
+ ``` xml
98
+ <dependency >
99
+ <groupId >com.mickaelb</groupId >
100
+ <artifactId >hibernate-query-asserts</artifactId >
101
+ <version >2.0.0</version >
102
+ </dependency >
103
+ ```
79
104
2. Register the integration with Hibernate, you just need to add this key in your configuration (here for yml):
80
105
81
106
spring:
82
- jpa:
83
- properties:
84
- hibernate.session_factory.statement_inspector: com.mickaelb.integration.hibernate.HibernateStatementInspector
107
+ jpa:
108
+ properties:
109
+ hibernate.session_factory.statement_inspector: com.mickaelb.integration.hibernate.HibernateStatementInspector
85
110
86
111
3. Register the Spring TestListener that will launch the SQL inspection if the annotation is present:
87
112
88
113
* By adding the listener on each of your integration test:
89
114
```java
90
- @SpringBootTest
91
- @TestExecutionListeners(
92
- listeners = HibernateAssertTestListener.class,
93
- mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
94
- )
95
- class MySpringIntegrationTest {
96
- ...
97
- }
115
+ @SpringBootTest
116
+ @TestExecutionListeners(
117
+ listeners = HibernateAssertTestListener.class,
118
+ mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS
119
+ )
120
+ class MySpringIntegrationTest {
121
+ ...
122
+ }
98
123
```
99
124
100
125
* **OR** by adding a **META-INF/spring.factories** file that contains the definition, that will register the listener for all your tests:
101
126
```
102
- org.springframework.test.context.TestExecutionListener=com.mickaelb.integration.spring.HibernateAssertTestListener
127
+ org.springframework.test.context.TestExecutionListener=com.mickaelb.integration.spring.HibernateAssertTestListener
103
128
```
0 commit comments