@@ -38,8 +38,8 @@ void mainStart(Connection dbConnection) throws Exception {
38
38
( id INTEGER PRIMARY KEY
39
39
, name VARCHAR(256) DEFAULT 'test'
40
40
, code VARCHAR(1)
41
- , created DATE NOT NULL
42
- ) """ )
41
+ , created DATE NOT NULL )
42
+ """ )
43
43
.execute ();
44
44
45
45
System .out .println ("# SINGLE INSERT" );
@@ -81,26 +81,24 @@ AND t.code IN (:code)
81
81
.bind ("id" , 10 )
82
82
.bind ("code" , "T" , "V" )
83
83
.streamMap (rs -> new Employee (
84
- rs .getInt (1 ),
85
- rs .getString (2 ),
86
- rs .getObject (3 , LocalDate .class )))
84
+ rs .getInt ("id" ),
85
+ rs .getString ("name" ),
86
+ rs .getObject ("created" , LocalDate .class )))
87
87
.toList ();
88
+ System .out .printf ("# PRINT RESULT OF: %s%n" , builder .toStringLine ());
89
+ employees .stream ().forEach (System .out ::println );
88
90
89
91
assertEquals (3 , employees .size ());
90
92
assertEquals (1 , employees .get (0 ).id );
91
93
assertEquals ("test" , employees .get (0 ).name );
92
94
assertEquals (someDate , employees .get (0 ).created );
93
-
94
- System .out .println ("# REUSE THE SELECT\n " );
95
- List <Employee > employees2 = builder
96
- .bind ("id" , 100 )
97
- .streamMap (rs -> new Employee (
98
- rs .getInt (1 ),
99
- rs .getString (2 ),
100
- rs .getObject (3 , LocalDate .class )))
101
- .toList ();
102
- assertEquals (5 , employees2 .size ());
103
- employees2 .stream ().forEach (System .out ::println );
95
+ assertEquals ( """
96
+ SELECT t.id, t.name, t.created
97
+ FROM employee t
98
+ WHERE t.id < [10]
99
+ AND t.code IN ([T],[V])
100
+ ORDER BY t.id
101
+ """ , builder .toString ());
104
102
}
105
103
}
106
104
@@ -132,16 +130,16 @@ private <T> void assertEquals(T expected, T result) {
132
130
133
131
/**
134
132
* Less than 170 lines long class to simplify work with JDBC.
135
- * Original source: <a href="https://github.com/pponec/DirectoryBookmarks/blob/development/utils /SqlExecutor.java">GitHub</a>
133
+ * Original source: <a href="https://github.com/pponec/DirectoryBookmarks/blob/development/src/main/java/net/ponec/script /SqlExecutor.java">GitHub</a>
136
134
* Licence: Apache License, Version 2.0
137
135
* @author Pavel Ponec, https://github.com/pponec
138
- * @version 1.0.6
136
+ * @version 1.0.7
139
137
*/
140
138
static class SqlParamBuilder implements Closeable {
141
139
/** SQL parameter mark type of {@code :param} */
142
140
private static final Pattern SQL_MARK = Pattern .compile (":(\\ w+)" );
143
141
private final Connection dbConnection ;
144
- private final Map <String , Object > params = new HashMap <>();
142
+ private final Map <String , Object [] > params = new HashMap <>();
145
143
private String sqlTemplate = null ;
146
144
private PreparedStatement preparedStatement = null ;
147
145
@@ -152,14 +150,13 @@ public SqlParamBuilder(Connection dbConnection) {
152
150
/** Close statement (if any) and set a new SQL template */
153
151
public SqlParamBuilder sql (String ... sqlLines ) {
154
152
close ();
155
- this .params .clear ();
156
- this .sqlTemplate = sqlLines .length == 1 ? sqlLines [0 ] : String .join ("\n " , sqlLines );
153
+ sqlTemplate = sqlLines .length == 1 ? sqlLines [0 ] : String .join ("\n " , sqlLines );
157
154
return this ;
158
155
}
159
156
160
- /** Assign a SQL value(s) */
161
- public SqlParamBuilder bind (String key , Object ... value ) {
162
- this . params .put (key , value .length == 1 ? value [ 0 ] : List . of ( value ) );
157
+ /** Assign a SQL value(s). In case a reused statement set the same number of parameters items. */
158
+ public SqlParamBuilder bind (String key , Object ... values ) {
159
+ params .put (key , values .length > 0 ? values : new Object []{ null } );
163
160
return this ;
164
161
}
165
162
@@ -218,34 +215,32 @@ public void close() {
218
215
throw new IllegalStateException ("Closing fails" , e );
219
216
} finally {
220
217
preparedStatement = null ;
218
+ params .clear ();
221
219
}
222
220
}
223
221
224
222
public PreparedStatement prepareStatement () throws SQLException {
225
- final var sqlValues = new ArrayList <>(params . size () );
223
+ final var sqlValues = new ArrayList <>();
226
224
final var sql = buildSql (sqlValues , false );
227
225
final var result = preparedStatement != null
228
226
? preparedStatement
229
227
: dbConnection .prepareStatement (sql );
230
228
for (int i = 0 , max = sqlValues .size (); i < max ; i ++) {
231
229
result .setObject (i + 1 , sqlValues .get (i ));
232
230
}
233
- this . preparedStatement = result ;
231
+ preparedStatement = result ;
234
232
return result ;
235
233
}
236
234
237
235
private String buildSql (List <Object > sqlValues , boolean toLog ) {
238
236
final var result = new StringBuilder (256 );
239
237
final var matcher = SQL_MARK .matcher (sqlTemplate );
240
238
final var missingKeys = new HashSet <>();
241
- final var singleValue = new Object [1 ];
242
239
while (matcher .find ()) {
243
240
final var key = matcher .group (1 );
244
- final var value = params .get (key );
245
- if (value != null ) {
241
+ final var values = params .get (key );
242
+ if (values != null ) {
246
243
matcher .appendReplacement (result , "" );
247
- singleValue [0 ] = value ;
248
- final Object [] values = value instanceof List ? ((List <?>) value ).toArray () : singleValue ;
249
244
for (int i = 0 ; i < values .length ; i ++) {
250
245
if (i > 0 ) result .append (',' );
251
246
result .append (Matcher .quoteReplacement (toLog ? "[" + values [i ] + "]" : "?" ));
@@ -272,6 +267,10 @@ public String toString() {
272
267
return buildSql (new ArrayList <>(), true );
273
268
}
274
269
270
+ public String toStringLine () {
271
+ return toString ().replaceAll ("\\ s*\\ R+\\ s*" , " " );
272
+ }
273
+
275
274
@ FunctionalInterface
276
275
public interface SqlFunction <T , R > extends Function <T , R > {
277
276
default R apply (T resultSet ) {
0 commit comments