9
9
import java .util .List ;
10
10
import java .util .Objects ;
11
11
12
- import org .hibernate .reactive .testing .DBSelectionExtension ;
12
+ import org .hibernate .boot .registry .StandardServiceRegistryBuilder ;
13
+ import org .hibernate .cfg .Configuration ;
14
+ import org .hibernate .reactive .testing .SqlStatementTracker ;
13
15
14
16
import org .junit .jupiter .api .Test ;
15
- import org .junit .jupiter .api .extension .RegisterExtension ;
16
17
17
18
import io .vertx .junit5 .Timeout ;
18
19
import io .vertx .junit5 .VertxTestContext ;
19
20
import jakarta .persistence .Entity ;
20
21
import jakarta .persistence .Id ;
21
22
import jakarta .persistence .Table ;
23
+ import org .assertj .core .api .Condition ;
22
24
23
25
import static java .util .concurrent .TimeUnit .MINUTES ;
24
26
import static org .assertj .core .api .Assertions .assertThat ;
25
- import static org .hibernate .reactive .containers .DatabaseConfiguration .DBType .COCKROACHDB ;
26
- import static org .hibernate .reactive .containers .DatabaseConfiguration .DBType .DB2 ;
27
- import static org .hibernate .reactive .containers .DatabaseConfiguration .DBType .MARIA ;
28
- import static org .hibernate .reactive .containers .DatabaseConfiguration .DBType .MYSQL ;
29
- import static org .hibernate .reactive .containers .DatabaseConfiguration .DBType .ORACLE ;
30
- import static org .hibernate .reactive .testing .DBSelectionExtension .skipTestsFor ;
27
+ import static org .hibernate .reactive .containers .DatabaseConfiguration .dbType ;
31
28
32
29
/**
33
30
* Same as Hibernate ORM org.hibernate.orm.test.stateless.UpsertTest
34
31
* <p>
35
- * These tests are in a separate class because we need to skip the execution on some databases,
36
- * but once this has been resolved, they could be in {@link ReactiveStatelessSessionTest}.
32
+ * These tests are in a separate class because we need to skip the execution on some databases,
33
+ * but once this has been resolved, they could be in {@link ReactiveStatelessSessionTest}.
37
34
* </p>
38
35
*/
39
36
@ Timeout (value = 10 , timeUnit = MINUTES )
40
37
public class UpsertTest extends BaseReactiveTest {
41
38
42
- /**
43
- * Something is missing in HR to make it work for these databases.
44
- */
45
- @ RegisterExtension
46
- public DBSelectionExtension dbSelection = skipTestsFor ( COCKROACHDB , DB2 , MARIA , MYSQL , ORACLE );
39
+ private static SqlStatementTracker sqlTracker ;
40
+
41
+ // A condition to check that entities are persisted using a merge operator when the database actually supports it.
42
+ private final static Condition <String > IS_USING_MERGE = new Condition <>(
43
+ s -> s .toLowerCase ().startsWith ( "merge into" ),
44
+ "insertions or updates without using the merge operator"
45
+ );
46
+
47
+ @ Override
48
+ protected Configuration constructConfiguration () {
49
+ Configuration configuration = super .constructConfiguration ();
50
+ sqlTracker = new SqlStatementTracker ( UpsertTest ::filter , configuration .getProperties () );
51
+ return configuration ;
52
+ }
53
+
54
+ @ Override
55
+ protected void addServices (StandardServiceRegistryBuilder builder ) {
56
+ sqlTracker .registerService ( builder );
57
+ }
58
+
59
+ private static boolean filter (String s ) {
60
+ String [] accepted = {"insert " , "update " , "merge " };
61
+ for ( String valid : accepted ) {
62
+ if ( s .toLowerCase ().startsWith ( valid ) ) {
63
+ return true ;
64
+ }
65
+ }
66
+ return false ;
67
+ }
47
68
48
69
@ Override
49
70
protected Collection <Class <?>> annotatedEntities () {
@@ -55,19 +76,26 @@ public void testMutinyUpsert(VertxTestContext context) {
55
76
test ( context , getMutinySessionFactory ().withStatelessTransaction ( ss -> ss
56
77
.upsert ( new Record ( 123L , "hello earth" ) )
57
78
.call ( () -> ss .upsert ( new Record ( 456L , "hello mars" ) ) )
79
+ .invoke ( this ::assertQueries )
58
80
)
59
81
.call ( v -> getMutinySessionFactory ().withStatelessTransaction ( ss -> ss
60
- .createSelectionQuery ( "from Record order by id" , Record .class ).getResultList () )
61
- .invoke ( results -> assertThat ( results ).containsExactly (
62
- new Record ( 123L , "hello earth" ),
63
- new Record ( 456L , "hello mars" )
64
- ) )
82
+ .createQuery ( "from Record order by id" , Record .class )
83
+ .getResultList () )
84
+ .invoke ( results -> {
85
+ assertThat ( results ).containsExactly (
86
+ new Record ( 123L , "hello earth" ),
87
+ new Record ( 456L , "hello mars" )
88
+ );
89
+ } )
65
90
)
66
91
.call ( () -> getMutinySessionFactory ().withStatelessTransaction ( ss -> ss
67
92
.upsert ( new Record ( 123L , "goodbye earth" ) )
68
93
) )
69
- .call ( v -> getMutinySessionFactory ().withStatelessTransaction ( ss -> ss
70
- .createSelectionQuery ( "from Record order by id" , Record .class ).getResultList () )
94
+ .invoke ( this ::assertQueries )
95
+ .call ( v -> getMutinySessionFactory ()
96
+ .withStatelessTransaction ( ss -> ss
97
+ .createQuery ( "from Record order by id" , Record .class )
98
+ .getResultList () )
71
99
.invoke ( results -> assertThat ( results ).containsExactly (
72
100
new Record ( 123L , "goodbye earth" ),
73
101
new Record ( 456L , "hello mars" )
@@ -81,9 +109,10 @@ public void testMutinyUpsertWithEntityName(VertxTestContext context) {
81
109
test ( context , getMutinySessionFactory ().withStatelessTransaction ( ss -> ss
82
110
.upsert ( Record .class .getName (), new Record ( 123L , "hello earth" ) )
83
111
.call ( () -> ss .upsert ( Record .class .getName (), new Record ( 456L , "hello mars" ) ) )
112
+ .invoke ( this ::assertQueries )
84
113
)
85
114
.call ( v -> getMutinySessionFactory ().withStatelessTransaction ( ss -> ss
86
- .createSelectionQuery ( "from Record order by id" , Record .class ).getResultList () )
115
+ .createQuery ( "from Record order by id" , Record .class ).getResultList () )
87
116
.invoke ( results -> assertThat ( results ).containsExactly (
88
117
new Record ( 123L , "hello earth" ),
89
118
new Record ( 456L , "hello mars" )
@@ -92,8 +121,9 @@ public void testMutinyUpsertWithEntityName(VertxTestContext context) {
92
121
.call ( () -> getMutinySessionFactory ().withStatelessTransaction ( ss -> ss
93
122
.upsert ( Record .class .getName (), new Record ( 123L , "goodbye earth" ) )
94
123
) )
124
+ .invoke ( this ::assertQueries )
95
125
.call ( v -> getMutinySessionFactory ().withStatelessTransaction ( ss -> ss
96
- .createSelectionQuery ( "from Record order by id" , Record .class ).getResultList () )
126
+ .createQuery ( "from Record order by id" , Record .class ).getResultList () )
97
127
.invoke ( results -> assertThat ( results ).containsExactly (
98
128
new Record ( 123L , "goodbye earth" ),
99
129
new Record ( 456L , "hello mars" )
@@ -108,8 +138,9 @@ public void testStageUpsert(VertxTestContext context) {
108
138
.upsert ( new Record ( 123L , "hello earth" ) )
109
139
.thenCompose ( v -> ss .upsert ( new Record ( 456L , "hello mars" ) ) )
110
140
)
141
+ .thenAccept ( v -> this .assertQueries () )
111
142
.thenCompose ( v -> getSessionFactory ().withStatelessTransaction ( ss -> ss
112
- .createSelectionQuery ( "from Record order by id" , Record .class ).getResultList () )
143
+ .createQuery ( "from Record order by id" , Record .class ).getResultList () )
113
144
.thenAccept ( results -> assertThat ( results ).containsExactly (
114
145
new Record ( 123L , "hello earth" ),
115
146
new Record ( 456L , "hello mars" )
@@ -118,8 +149,9 @@ public void testStageUpsert(VertxTestContext context) {
118
149
.thenCompose ( v -> getSessionFactory ().withStatelessTransaction ( ss -> ss
119
150
.upsert ( new Record ( 123L , "goodbye earth" ) )
120
151
) )
152
+ .thenAccept ( v -> this .assertQueries () )
121
153
.thenCompose ( v -> getSessionFactory ().withStatelessTransaction ( ss -> ss
122
- .createSelectionQuery ( "from Record order by id" , Record .class ).getResultList () )
154
+ .createQuery ( "from Record order by id" , Record .class ).getResultList () )
123
155
.thenAccept ( results -> assertThat ( results ).containsExactly (
124
156
new Record ( 123L , "goodbye earth" ),
125
157
new Record ( 456L , "hello mars" )
@@ -134,8 +166,9 @@ public void testStageUpsertWithEntityName(VertxTestContext context) {
134
166
.upsert ( Record .class .getName (), new Record ( 123L , "hello earth" ) )
135
167
.thenCompose ( v -> ss .upsert ( Record .class .getName (), new Record ( 456L , "hello mars" ) ) )
136
168
)
169
+ .thenAccept ( v -> this .assertQueries () )
137
170
.thenCompose ( v -> getSessionFactory ().withStatelessTransaction ( ss -> ss
138
- .createSelectionQuery ( "from Record order by id" , Record .class ).getResultList () )
171
+ .createQuery ( "from Record order by id" , Record .class ).getResultList () )
139
172
.thenAccept ( results -> assertThat ( results ).containsExactly (
140
173
new Record ( 123L , "hello earth" ),
141
174
new Record ( 456L , "hello mars" )
@@ -144,8 +177,9 @@ public void testStageUpsertWithEntityName(VertxTestContext context) {
144
177
.thenCompose ( v -> getSessionFactory ().withStatelessTransaction ( ss -> ss
145
178
.upsert ( Record .class .getName (), new Record ( 123L , "goodbye earth" ) )
146
179
) )
180
+ .thenAccept ( v -> this .assertQueries () )
147
181
.thenCompose ( v -> getSessionFactory ().withStatelessTransaction ( ss -> ss
148
- .createSelectionQuery ( "from Record order by id" , Record .class ).getResultList () )
182
+ .createQuery ( "from Record order by id" , Record .class ).getResultList () )
149
183
.thenAccept ( results -> assertThat ( results ).containsExactly (
150
184
new Record ( 123L , "goodbye earth" ),
151
185
new Record ( 456L , "hello mars" )
@@ -154,6 +188,28 @@ public void testStageUpsertWithEntityName(VertxTestContext context) {
154
188
);
155
189
}
156
190
191
+ private void assertQueries () {
192
+ if ( hasMergeOperator () ) {
193
+ assertThat ( sqlTracker .getLoggedQueries () ).have ( IS_USING_MERGE );
194
+ }
195
+ else {
196
+ // This might be overkill, but it's still helpful in case more databases are going to support
197
+ // the merge operator, and we need to update the documentation or warn people about it.
198
+ assertThat ( sqlTracker .getLoggedQueries () ).doNotHave ( IS_USING_MERGE );
199
+ }
200
+ }
201
+
202
+ private boolean hasMergeOperator () {
203
+ switch ( dbType () ) {
204
+ case SQLSERVER :
205
+ case ORACLE :
206
+ case POSTGRESQL :
207
+ return true ;
208
+ default :
209
+ return false ;
210
+ }
211
+ }
212
+
157
213
@ Entity (name = "Record" )
158
214
@ Table (name = "Record" )
159
215
public static class Record {
0 commit comments