5
5
*/
6
6
package org .hibernate .reactive ;
7
7
8
+ import org .hibernate .LockMode ;
9
+ import org .hibernate .boot .registry .StandardServiceRegistryBuilder ;
10
+ import org .hibernate .cfg .Configuration ;
11
+ import org .hibernate .reactive .testing .SqlStatementTracker ;
12
+
8
13
import java .util .ArrayList ;
9
14
import java .util .Collection ;
10
15
import java .util .List ;
11
16
import java .util .concurrent .TimeUnit ;
12
17
18
+ import org .junit .jupiter .api .BeforeEach ;
13
19
import org .junit .jupiter .api .Test ;
14
20
15
21
import io .vertx .junit5 .Timeout ;
22
28
import jakarta .persistence .OneToMany ;
23
29
24
30
import static org .assertj .core .api .Assertions .assertThat ;
31
+ import static org .hibernate .reactive .containers .DatabaseConfiguration .dbType ;
25
32
26
33
@ Timeout (value = 10 , timeUnit = TimeUnit .MINUTES )
27
34
public class FindByIdWithLockTest extends BaseReactiveTest {
28
35
private static final Long CHILD_ID = 1L ;
29
36
37
+ private static SqlStatementTracker sqlTracker ;
38
+
39
+ @ Override
40
+ protected Configuration constructConfiguration () {
41
+ Configuration configuration = super .constructConfiguration ();
42
+
43
+ // Construct a tracker that collects query statements via the SqlStatementLogger framework.
44
+ // Pass in configuration properties to hand off any actual logging properties
45
+ sqlTracker = new SqlStatementTracker ( FindByIdWithLockTest ::selectQueryFilter , configuration .getProperties () );
46
+ return configuration ;
47
+ }
48
+
49
+ @ BeforeEach
50
+ public void clearTracker () {
51
+ sqlTracker .clear ();
52
+ }
53
+
54
+ @ Override
55
+ protected void addServices (StandardServiceRegistryBuilder builder ) {
56
+ sqlTracker .registerService ( builder );
57
+ }
58
+
59
+ private static boolean selectQueryFilter (String s ) {
60
+ return s .toLowerCase ().startsWith ( "select " );
61
+ }
62
+
30
63
@ Override
31
64
protected Collection <Class <?>> annotatedEntities () {
32
65
return List .of ( Parent .class , Child .class );
@@ -50,6 +83,44 @@ context, getMutinySessionFactory()
50
83
);
51
84
}
52
85
86
+ @ Test
87
+ public void testFindUpgradeNoWait (VertxTestContext context ) {
88
+ Child child = new Child ( CHILD_ID , "And" );
89
+ test (
90
+ context , getMutinySessionFactory ()
91
+ .withTransaction ( session -> session .persistAll ( child ) )
92
+ .invoke ( () -> sqlTracker .clear () )
93
+ .chain ( () -> getMutinySessionFactory ().withTransaction ( session -> session
94
+ .find ( Child .class , CHILD_ID , LockMode .UPGRADE_NOWAIT )
95
+ .invoke ( c -> {
96
+ assertThat ( c ).isNotNull ();
97
+ assertThat ( c .getId () ).isEqualTo ( CHILD_ID );
98
+ String selectQuery = sqlTracker .getLoggedQueries ().get ( 0 );
99
+ assertThat ( sqlTracker .getLoggedQueries () ).hasSize ( 1 );
100
+ assertThat ( selectQuery )
101
+ .matches ( this ::noWaitLockingPredicate , "SQL query with nowait lock for " + dbType ().name () );
102
+ }
103
+ ) ) )
104
+ );
105
+ }
106
+
107
+ /**
108
+ * @return true if the query contains the expected nowait keyword for the selected database
109
+ */
110
+ private boolean noWaitLockingPredicate (String selectQuery ) {
111
+ return switch ( dbType () ) {
112
+ case POSTGRESQL -> selectQuery .endsWith ( "for no key update of c1_0 nowait" );
113
+ case COCKROACHDB -> selectQuery .endsWith ( "for update of c1_0 nowait" );
114
+ case SQLSERVER -> selectQuery .contains ( "with (updlock,holdlock,rowlock,nowait)" );
115
+ case ORACLE -> selectQuery .contains ( "for update of c1_0.id nowait" );
116
+ // DB2 does not support nowait
117
+ case DB2 -> selectQuery .contains ( "for read only with rs use and keep update locks" );
118
+ case MARIA -> selectQuery .contains ( "for update nowait" );
119
+ case MYSQL -> selectQuery .contains ( "for update of c1_0 nowait" );
120
+ default -> throw new IllegalArgumentException ( "Database not recognized: " + dbType ().name () );
121
+ };
122
+ }
123
+
53
124
@ Entity (name = "Parent" )
54
125
public static class Parent {
55
126
@@ -59,7 +130,7 @@ public static class Parent {
59
130
private String name ;
60
131
61
132
@ OneToMany (fetch = FetchType .EAGER )
62
- public List <Child > children ;
133
+ public List <Child > children = new ArrayList <>() ;
63
134
64
135
public Parent () {
65
136
}
@@ -70,9 +141,6 @@ public Parent(Long id, String name) {
70
141
}
71
142
72
143
public void add (Child child ) {
73
- if ( children == null ) {
74
- children = new ArrayList <>();
75
- }
76
144
children .add ( child );
77
145
}
78
146
@@ -89,7 +157,6 @@ public List<Child> getChildren() {
89
157
}
90
158
}
91
159
92
-
93
160
@ Entity (name = "Child" )
94
161
public static class Child {
95
162
@@ -109,13 +176,6 @@ public Child(Long id, String name) {
109
176
this .name = name ;
110
177
}
111
178
112
- public Child (Long id , String name , Parent parent ) {
113
- this .id = id ;
114
- this .name = name ;
115
- this .parent = parent ;
116
- parent .add ( this );
117
- }
118
-
119
179
public Long getId () {
120
180
return id ;
121
181
}
@@ -128,6 +188,4 @@ public Parent getParent() {
128
188
return parent ;
129
189
}
130
190
}
131
-
132
-
133
191
}
0 commit comments