7
7
*/
8
8
package utils ;
9
9
10
- import org .jetbrains .annotations .NotNull ;
11
- import org .junit .jupiter .api .Assertions ;
12
10
import java .io .Closeable ;
13
- import java .io .IOException ;
14
11
import java .sql .*;
15
12
import java .time .LocalDate ;
16
13
import java .util .*;
@@ -27,12 +24,13 @@ public final class SqlExecutor {
27
24
private final LocalDate someDate = LocalDate .parse ("2018-09-12" );
28
25
29
26
public static void main (final String [] args ) throws Exception {
27
+ System .out .println ("Arguments: " + List .of (args ));
30
28
try (Connection dbConnection = db .connection ()) {
31
- new SqlExecutor ().mainStart (dbConnection , args );
29
+ new SqlExecutor ().mainStart (dbConnection );
32
30
}
33
31
}
34
32
35
- void mainStart (Connection dbConnection , String ... args ) throws Exception {
33
+ void mainStart (Connection dbConnection ) throws Exception {
36
34
try (SqlParamBuilder builder = new SqlParamBuilder (dbConnection )) {
37
35
// CREATE TABLE:
38
36
builder .sql ("""
@@ -41,16 +39,15 @@ void mainStart(Connection dbConnection, String... args) throws Exception {
41
39
, name VARCHAR(256) DEFAULT 'test'
42
40
, code VARCHAR(1)
43
41
, created DATE NOT NULL
44
- )""" . stripLeading () )
42
+ )""" )
45
43
.execute ();
46
44
47
45
// SINGLE INSERT:
48
- System .out .println ("SINGLE INSERT" );
49
46
builder .sql ("""
50
47
INSERT INTO employee
51
48
( id, code, created ) VALUES
52
49
( :id, :code, :created )
53
- """ . stripLeading () )
50
+ """ )
54
51
.bind ("id" , 1 )
55
52
.bind ("code" , "T" )
56
53
.bind ("created" , someDate )
@@ -62,7 +59,7 @@ void mainStart(Connection dbConnection, String... args) throws Exception {
62
59
(id,code,created) VALUES
63
60
(:id1,:code,:created),
64
61
(:id2,:code,:created)
65
- """ . stripLeading () )
62
+ """ )
66
63
.bind ("id1" , 2 )
67
64
.bind ("id2" , 3 )
68
65
.bind ("code" , "T" )
@@ -80,28 +77,29 @@ void mainStart(Connection dbConnection, String... args) throws Exception {
80
77
WHERE t.id < :id
81
78
AND t.code IN (:code)
82
79
ORDER BY t.id
83
- """ . stripLeading () )
80
+ """ )
84
81
.bind ("id" , 10 )
85
82
.bind ("code" , "T" , "V" )
86
83
.streamMap (rs -> new Employee (
87
84
rs .getInt (1 ),
88
85
rs .getString (2 ),
89
86
rs .getObject (3 , LocalDate .class )))
90
87
.toList ();
91
- Assertions .assertEquals (3 , employees .size ());
92
- Assertions .assertEquals (1 , employees .get (0 ).id );
93
- Assertions .assertEquals ("test" , employees .get (0 ).name );
94
- Assertions .assertEquals (someDate , employees .get (0 ).created );
95
88
96
- // REUSE SELECT:
89
+ assertEquals (3 , employees .size ());
90
+ assertEquals (1 , employees .get (0 ).id );
91
+ assertEquals ("test" , employees .get (0 ).name );
92
+ assertEquals (someDate , employees .get (0 ).created );
93
+
94
+ // REUSE THE SELECT:
97
95
List <Employee > employees2 = builder
98
96
.bind ("id" , 100 )
99
97
.streamMap (rs -> new Employee (
100
98
rs .getInt (1 ),
101
99
rs .getString (2 ),
102
100
rs .getObject (3 , LocalDate .class )))
103
101
.toList ();
104
- Assertions . assertEquals (5 , employees2 .size ());
102
+ assertEquals (5 , employees2 .size ());
105
103
}
106
104
}
107
105
@@ -125,53 +123,85 @@ public static ConnectionProvider forH2(String user, String passwd) {
125
123
}
126
124
}
127
125
128
- /** A utility class from the Ujorm framework */
129
- static class SqlParamBuilder implements Closeable {
126
+ private <T > void assertEquals (T expected , T result ) {
127
+ if (!Objects .equals (expected , result )) {
128
+ throw new IllegalStateException ("Objects are not equals: '%s' <> '%s'" .formatted (expected , result ));
129
+ }
130
+ }
130
131
132
+ /**
133
+ * 170 lines long class to simplify working with JDBC.
134
+ * Original source: <a href="https://github.com/pponec/DirectoryBookmarks/blob/development/utils/SqlExecutor.java">GitHub</a>
135
+ * @version 1.0.5
136
+ */
137
+ static class SqlParamBuilder implements Closeable {
131
138
/** SQL parameter mark type of {@code :param} */
132
- private static final Pattern SQL_MARK = Pattern .compile (":(\\ w+)(?=[ \\ s,; \\ ])]|$) " );
139
+ private static final Pattern SQL_MARK = Pattern .compile (":(\\ w+)" );
133
140
private final Connection dbConnection ;
134
141
private final Map <String , Object > params = new HashMap <>();
135
142
private String sqlTemplate = null ;
136
143
private PreparedStatement preparedStatement = null ;
137
- private ResultSetWrapper rsWrapper = null ;
144
+ private ResultSet resultSet = null ;
138
145
139
146
public SqlParamBuilder (Connection dbConnection ) {
140
147
this .dbConnection = dbConnection ;
141
148
}
142
149
143
150
/** Close statement (if any) and set a new SQL template */
144
- public SqlParamBuilder sql (@ NotNull String ... sqlTemplates ) {
151
+ public SqlParamBuilder sql (String ... sqlTemplates ) {
145
152
close ();
153
+ this .params .clear ();
146
154
this .sqlTemplate = sqlTemplates .length == 1
147
155
? sqlTemplates [0 ] : String .join ("\n " , sqlTemplates );
148
156
return this ;
149
157
}
150
158
151
159
/** Assign a SQL value(s) */
152
- public SqlParamBuilder bind (@ NotNull String key , @ NotNull Object ... value ) {
160
+ public SqlParamBuilder bind (String key , Object ... value ) {
153
161
this .params .put (key , value .length == 1 ? value [0 ] : List .of (value ));
154
162
return this ;
155
163
}
156
164
157
- public Iterable < ResultSet > executeSelect () throws IllegalStateException , SQLException {
158
- try (Closeable rs = rsWrapper ) {
159
- } catch (IOException e ) {
165
+ private ResultSet executeSelect () throws IllegalStateException {
166
+ try (AutoCloseable rs = resultSet ) {
167
+ } catch (Exception e ) {
160
168
throw new IllegalStateException ("Closing fails" , e );
161
169
}
162
- rsWrapper = new ResultSetWrapper (prepareStatement ().executeQuery ());
163
- return rsWrapper ;
170
+ try {
171
+ resultSet = prepareStatement ().executeQuery ();
172
+ return resultSet ;
173
+ } catch (Exception ex ) {
174
+ throw (ex instanceof RuntimeException re ) ? re : new IllegalStateException (ex );
175
+ }
176
+ }
177
+
178
+ /** Use a {@link #streamMap(SqlFunction)} rather */
179
+ private Stream <ResultSet > stream () {
180
+ final var resultSet = executeSelect ();
181
+ final var iterator = new Iterator <ResultSet >() {
182
+ @ Override
183
+ public boolean hasNext () {
184
+ try {
185
+ return resultSet .next ();
186
+ } catch (SQLException e ) {
187
+ throw new IllegalStateException (e );
188
+ }
189
+ }
190
+ @ Override
191
+ public ResultSet next () {
192
+ return resultSet ;
193
+ }
194
+ };
195
+ return StreamSupport .stream (Spliterators .spliteratorUnknownSize (iterator , Spliterator .ORDERED ), false );
164
196
}
165
197
166
198
/** Iterate executed select */
167
199
public void forEach (SqlConsumer <ResultSet > consumer ) throws IllegalStateException , SQLException {
168
- for (ResultSet rs : executeSelect ()) {
169
- consumer .accept (rs );
170
- }
200
+ stream ().forEach (consumer );
171
201
}
172
202
173
- public <R > Stream <R > streamMap (SqlFunction <ResultSet , ? extends R > mapper ) throws SQLException {
174
- return StreamSupport . stream (executeSelect (). spliterator (), false ).map (mapper );
203
+ public <R > Stream <R > streamMap (SqlFunction <ResultSet , ? extends R > mapper ) {
204
+ return stream ().map (mapper );
175
205
}
176
206
177
207
public int execute () throws IllegalStateException , SQLException {
@@ -185,11 +215,11 @@ public Connection getConnection() {
185
215
/** The method closes a PreparedStatement object with related objects, not the database connection. */
186
216
@ Override
187
217
public void close () {
188
- try (Closeable c1 = rsWrapper ; PreparedStatement c2 = preparedStatement ) {
218
+ try (AutoCloseable c1 = resultSet ; PreparedStatement c2 = preparedStatement ) {
189
219
} catch (Exception e ) {
190
220
throw new IllegalStateException ("Closing fails" , e );
191
221
} finally {
192
- rsWrapper = null ;
222
+ resultSet = null ;
193
223
preparedStatement = null ;
194
224
}
195
225
}
@@ -245,80 +275,29 @@ public String toString() {
245
275
return buildSql (new ArrayList <>(), true );
246
276
}
247
277
248
- /** Based on the {@code RowIterator} class of Ujorm framework. */
249
- static final class ResultSetWrapper implements Iterable <ResultSet >, Iterator <ResultSet >, Closeable {
250
- private ResultSet resultSet ;
251
- /** It the cursor ready for reading? After a row reading the value will be set to false */
252
- private boolean cursorReady = false ;
253
- /** Has a resultset a next row? */
254
- private boolean hasNext = false ;
255
-
256
- public ResultSetWrapper ( final ResultSet resultSet ) {
257
- this .resultSet = resultSet ;
258
- }
259
-
260
- @ Override
261
- public Iterator <ResultSet > iterator () {
262
- return this ;
263
- }
264
-
265
- /** The last checking closes all resources. */
266
- @ Override
267
- public boolean hasNext () throws IllegalStateException {
268
- if (!cursorReady ) try {
269
- hasNext = resultSet .next ();
270
- if (!hasNext ) {
271
- close ();
272
- }
273
- cursorReady = true ;
274
- } catch (SQLException e ) {
275
- throw new IllegalStateException (e );
276
- }
277
- return hasNext ;
278
- }
279
-
280
- @ Override
281
- public ResultSet next () {
282
- if (hasNext ()) {
283
- cursorReady = false ;
284
- return resultSet ;
285
- }
286
- throw new NoSuchElementException ();
287
- }
288
-
289
- @ Override
290
- public void close () {
291
- try (ResultSet rs = resultSet ) {
292
- cursorReady = true ;
293
- hasNext = false ;
294
- } catch (SQLException e ) {
295
- throw new IllegalStateException (e );
296
- }
297
- }
298
- }
299
-
300
-
301
278
@ FunctionalInterface
302
279
public interface SqlFunction <T , R > extends Function <T , R > {
303
280
default R apply (T resultSet ) {
304
- try { return applyRs (resultSet ); } catch (SQLException ex ) {
305
- throw new IllegalStateException (ex );
281
+ try {
282
+ return applyRs (resultSet );
283
+ } catch (Exception ex ) {
284
+ throw (ex instanceof RuntimeException re ) ? re : new IllegalStateException (ex );
306
285
}
307
286
}
308
-
309
287
R applyRs (T resultSet ) throws SQLException ;
310
288
}
311
289
312
290
@ FunctionalInterface
313
291
public interface SqlConsumer <T > extends Consumer <T > {
314
292
@ Override
315
293
default void accept (final T t ) {
316
- try { acceptResultSet (t ); } catch (SQLException e ) {
317
- throw new IllegalStateException (e );
294
+ try {
295
+ acceptResultSet (t );
296
+ } catch (Exception ex ) {
297
+ throw (ex instanceof RuntimeException re ) ? re : new IllegalStateException (ex );
318
298
}
319
299
}
320
-
321
- void acceptResultSet (T t ) throws SQLException ;
300
+ void acceptResultSet (T t ) throws Exception ;
322
301
}
323
302
}
324
303
}
0 commit comments