99import java .util .List ;
1010import java .util .Objects ;
1111
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 ;
1315
1416import org .junit .jupiter .api .Test ;
15- import org .junit .jupiter .api .extension .RegisterExtension ;
1617
1718import io .vertx .junit5 .Timeout ;
1819import io .vertx .junit5 .VertxTestContext ;
1920import jakarta .persistence .Entity ;
2021import jakarta .persistence .Id ;
2122import jakarta .persistence .Table ;
23+ import org .assertj .core .api .Condition ;
2224
2325import static java .util .concurrent .TimeUnit .MINUTES ;
2426import 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 ;
3128
3229/**
3330 * Same as Hibernate ORM org.hibernate.orm.test.stateless.UpsertTest
3431 * <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}.
3734 * </p>
3835 */
3936@ Timeout (value = 10 , timeUnit = MINUTES )
4037public class UpsertTest extends BaseReactiveTest {
4138
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+ }
4768
4869 @ Override
4970 protected Collection <Class <?>> annotatedEntities () {
@@ -55,19 +76,26 @@ public void testMutinyUpsert(VertxTestContext context) {
5576 test ( context , getMutinySessionFactory ().withStatelessTransaction ( ss -> ss
5677 .upsert ( new Record ( 123L , "hello earth" ) )
5778 .call ( () -> ss .upsert ( new Record ( 456L , "hello mars" ) ) )
79+ .invoke ( this ::assertQueries )
5880 )
5981 .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+ .createSelectionQuery ( "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+ } )
6590 )
6691 .call ( () -> getMutinySessionFactory ().withStatelessTransaction ( ss -> ss
6792 .upsert ( new Record ( 123L , "goodbye earth" ) )
6893 ) )
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+ .createSelectionQuery ( "from Record order by id" , Record .class )
98+ .getResultList () )
7199 .invoke ( results -> assertThat ( results ).containsExactly (
72100 new Record ( 123L , "goodbye earth" ),
73101 new Record ( 456L , "hello mars" )
@@ -81,6 +109,7 @@ public void testMutinyUpsertWithEntityName(VertxTestContext context) {
81109 test ( context , getMutinySessionFactory ().withStatelessTransaction ( ss -> ss
82110 .upsert ( Record .class .getName (), new Record ( 123L , "hello earth" ) )
83111 .call ( () -> ss .upsert ( Record .class .getName (), new Record ( 456L , "hello mars" ) ) )
112+ .invoke ( this ::assertQueries )
84113 )
85114 .call ( v -> getMutinySessionFactory ().withStatelessTransaction ( ss -> ss
86115 .createSelectionQuery ( "from Record order by id" , Record .class ).getResultList () )
@@ -92,6 +121,7 @@ public void testMutinyUpsertWithEntityName(VertxTestContext context) {
92121 .call ( () -> getMutinySessionFactory ().withStatelessTransaction ( ss -> ss
93122 .upsert ( Record .class .getName (), new Record ( 123L , "goodbye earth" ) )
94123 ) )
124+ .invoke ( this ::assertQueries )
95125 .call ( v -> getMutinySessionFactory ().withStatelessTransaction ( ss -> ss
96126 .createSelectionQuery ( "from Record order by id" , Record .class ).getResultList () )
97127 .invoke ( results -> assertThat ( results ).containsExactly (
@@ -108,6 +138,7 @@ public void testStageUpsert(VertxTestContext context) {
108138 .upsert ( new Record ( 123L , "hello earth" ) )
109139 .thenCompose ( v -> ss .upsert ( new Record ( 456L , "hello mars" ) ) )
110140 )
141+ .thenAccept ( v -> this .assertQueries () )
111142 .thenCompose ( v -> getSessionFactory ().withStatelessTransaction ( ss -> ss
112143 .createSelectionQuery ( "from Record order by id" , Record .class ).getResultList () )
113144 .thenAccept ( results -> assertThat ( results ).containsExactly (
@@ -118,6 +149,7 @@ public void testStageUpsert(VertxTestContext context) {
118149 .thenCompose ( v -> getSessionFactory ().withStatelessTransaction ( ss -> ss
119150 .upsert ( new Record ( 123L , "goodbye earth" ) )
120151 ) )
152+ .thenAccept ( v -> this .assertQueries () )
121153 .thenCompose ( v -> getSessionFactory ().withStatelessTransaction ( ss -> ss
122154 .createSelectionQuery ( "from Record order by id" , Record .class ).getResultList () )
123155 .thenAccept ( results -> assertThat ( results ).containsExactly (
@@ -134,6 +166,7 @@ public void testStageUpsertWithEntityName(VertxTestContext context) {
134166 .upsert ( Record .class .getName (), new Record ( 123L , "hello earth" ) )
135167 .thenCompose ( v -> ss .upsert ( Record .class .getName (), new Record ( 456L , "hello mars" ) ) )
136168 )
169+ .thenAccept ( v -> this .assertQueries () )
137170 .thenCompose ( v -> getSessionFactory ().withStatelessTransaction ( ss -> ss
138171 .createSelectionQuery ( "from Record order by id" , Record .class ).getResultList () )
139172 .thenAccept ( results -> assertThat ( results ).containsExactly (
@@ -144,6 +177,7 @@ public void testStageUpsertWithEntityName(VertxTestContext context) {
144177 .thenCompose ( v -> getSessionFactory ().withStatelessTransaction ( ss -> ss
145178 .upsert ( Record .class .getName (), new Record ( 123L , "goodbye earth" ) )
146179 ) )
180+ .thenAccept ( v -> this .assertQueries () )
147181 .thenCompose ( v -> getSessionFactory ().withStatelessTransaction ( ss -> ss
148182 .createSelectionQuery ( "from Record order by id" , Record .class ).getResultList () )
149183 .thenAccept ( results -> assertThat ( results ).containsExactly (
@@ -154,6 +188,28 @@ public void testStageUpsertWithEntityName(VertxTestContext context) {
154188 );
155189 }
156190
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+
157213 @ Entity (name = "Record" )
158214 @ Table (name = "Record" )
159215 public static class Record {
0 commit comments