0
# Statement Execution
1
2
This document covers SQL statement execution including Statement, PreparedStatement, and CallableStatement interfaces, along with PostgreSQL-specific extensions.
3
4
## Capabilities
5
6
### PGStatement Interface
7
8
PostgreSQL-specific extensions to java.sql.Statement.
9
10
```java { .api }
11
package org.postgresql;
12
13
import java.sql.Statement;
14
import java.sql.SQLException;
15
16
/**
17
* PostgreSQL extensions to java.sql.Statement.
18
* All Statement objects returned by PostgreSQL connections implement this interface.
19
*/
20
public interface PGStatement extends Statement {
21
/**
22
* Constant representing positive infinity for date/timestamp values.
23
* Corresponds to PostgreSQL's 'infinity' date value.
24
*/
25
long DATE_POSITIVE_INFINITY = 9223372036825200000L;
26
27
/**
28
* Constant representing negative infinity for date/timestamp values.
29
* Corresponds to PostgreSQL's '-infinity' date value.
30
*/
31
long DATE_NEGATIVE_INFINITY = -9223372036832400000L;
32
33
/**
34
* Smaller positive infinity value for compatibility.
35
*/
36
long DATE_POSITIVE_SMALLER_INFINITY = 185543533774800000L;
37
38
/**
39
* Smaller negative infinity value for compatibility.
40
*/
41
long DATE_NEGATIVE_SMALLER_INFINITY = -185543533774800000L;
42
43
/**
44
* Returns the OID of the last row inserted by this statement.
45
* Only valid for INSERT statements that inserted exactly one row.
46
*
47
* @return OID of last inserted row, or 0 if not applicable
48
* @throws SQLException if statement has not been executed
49
*/
50
long getLastOID() throws SQLException;
51
52
/**
53
* Sets the threshold for when to use server-side prepared statements.
54
* The statement will be prepared on the server after it has been executed
55
* this many times.
56
*
57
* @param threshold Number of executions before server prepare:
58
* - Positive value N: Use server prepare on Nth and subsequent executions
59
* - 0: Never use server prepare
60
* - Negative: Reserved for internal use (forceBinary mode)
61
* - Default: 5 (configurable via prepareThreshold connection property)
62
* @throws SQLException if threshold cannot be set
63
*/
64
void setPrepareThreshold(int threshold) throws SQLException;
65
66
/**
67
* Gets the current prepare threshold for this statement.
68
*
69
* @return Current prepare threshold
70
*/
71
int getPrepareThreshold();
72
73
/**
74
* Turn on the use of prepared statements in the server.
75
* Server-side prepared statements are unrelated to JDBC PreparedStatements.
76
* As of build 302, this method is equivalent to setPrepareThreshold(1).
77
*
78
* @param flag use server prepare
79
* @throws SQLException if something goes wrong
80
* @deprecated As of build 302, replaced by setPrepareThreshold(int)
81
*/
82
@Deprecated
83
void setUseServerPrepare(boolean flag) throws SQLException;
84
85
/**
86
* Returns whether server-side prepare will be used for this statement.
87
* A return value of true indicates that the next execution will use
88
* a server-prepared statement, assuming the protocol supports it.
89
*
90
* @return true if next reuse will use server prepare
91
*/
92
boolean isUseServerPrepare();
93
94
/**
95
* Enables or disables adaptive fetch size adjustment.
96
* When enabled, the driver automatically adjusts fetch size based on
97
* available memory and result set characteristics.
98
*
99
* @param adaptiveFetch true to enable adaptive fetch
100
*/
101
void setAdaptiveFetch(boolean adaptiveFetch);
102
103
/**
104
* Returns whether adaptive fetch is enabled for this statement.
105
*
106
* @return true if adaptive fetch is enabled
107
*/
108
boolean getAdaptiveFetch();
109
}
110
```
111
112
**Usage Examples:**
113
114
```java
115
import org.postgresql.PGStatement;
116
import java.sql.Connection;
117
import java.sql.DriverManager;
118
import java.sql.Statement;
119
import java.sql.ResultSet;
120
import java.sql.SQLException;
121
122
// Example 1: Basic Statement usage
123
public class BasicStatementExample {
124
public static void executeQuery(Connection conn) throws SQLException {
125
try (Statement stmt = conn.createStatement()) {
126
// Execute a simple query
127
try (ResultSet rs = stmt.executeQuery("SELECT id, name FROM users")) {
128
while (rs.next()) {
129
System.out.println(rs.getInt("id") + ": " + rs.getString("name"));
130
}
131
}
132
}
133
}
134
}
135
136
// Example 2: Get last inserted OID
137
public class OIDExample {
138
public static void insertAndGetOID(Connection conn) throws SQLException {
139
try (Statement stmt = conn.createStatement()) {
140
// Insert a row
141
int rowsAffected = stmt.executeUpdate(
142
"INSERT INTO documents (title, content) VALUES ('Doc1', 'Content')");
143
144
// Get OID of inserted row (only works if table has OIDs)
145
PGStatement pgStmt = stmt.unwrap(PGStatement.class);
146
long oid = pgStmt.getLastOID();
147
System.out.println("Inserted row OID: " + oid);
148
}
149
}
150
}
151
152
// Example 3: Configure prepare threshold
153
public class PrepareThresholdExample {
154
public static void configurePrepare(Connection conn) throws SQLException {
155
try (Statement stmt = conn.createStatement()) {
156
PGStatement pgStmt = stmt.unwrap(PGStatement.class);
157
158
// Set to prepare immediately
159
pgStmt.setPrepareThreshold(0);
160
161
// Or never prepare (always use simple protocol)
162
// pgStmt.setPrepareThreshold(-1);
163
164
// Execute query
165
try (ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {
166
// process results
167
}
168
}
169
}
170
}
171
172
// Example 4: Statement with fetch size
173
public class FetchSizeExample {
174
public static void queryWithFetchSize(Connection conn) throws SQLException {
175
try (Statement stmt = conn.createStatement()) {
176
// Set fetch size to retrieve rows in batches of 100
177
stmt.setFetchSize(100);
178
179
// Execute large query
180
try (ResultSet rs = stmt.executeQuery("SELECT * FROM large_table")) {
181
while (rs.next()) {
182
// Process rows - driver fetches 100 at a time
183
}
184
}
185
}
186
}
187
}
188
189
// Example 5: Batch updates
190
public class BatchUpdateExample {
191
public static void batchInsert(Connection conn) throws SQLException {
192
try (Statement stmt = conn.createStatement()) {
193
// Add multiple statements to batch
194
stmt.addBatch("INSERT INTO logs (message) VALUES ('Log 1')");
195
stmt.addBatch("INSERT INTO logs (message) VALUES ('Log 2')");
196
stmt.addBatch("INSERT INTO logs (message) VALUES ('Log 3')");
197
198
// Execute all at once
199
int[] results = stmt.executeBatch();
200
System.out.println("Inserted " + results.length + " rows");
201
}
202
}
203
}
204
```
205
206
### PreparedStatement
207
208
Precompiled SQL statements with parameter support.
209
210
```java { .api }
211
import java.sql.PreparedStatement;
212
import java.sql.SQLException;
213
import java.sql.Date;
214
import java.sql.Timestamp;
215
import java.sql.Array;
216
import java.io.InputStream;
217
import java.io.Reader;
218
219
/**
220
* PreparedStatement represents a precompiled SQL statement.
221
* PostgreSQL driver implements full JDBC PreparedStatement interface
222
* with extensions from PGStatement.
223
*/
224
public interface PreparedStatement extends Statement {
225
/**
226
* Executes the SQL query and returns a ResultSet.
227
*
228
* @return ResultSet containing query results
229
* @throws SQLException if execution fails
230
*/
231
ResultSet executeQuery() throws SQLException;
232
233
/**
234
* Executes an SQL INSERT, UPDATE, or DELETE statement.
235
*
236
* @return Number of rows affected
237
* @throws SQLException if execution fails
238
*/
239
int executeUpdate() throws SQLException;
240
241
/**
242
* Executes any SQL statement.
243
*
244
* @return true if result is a ResultSet, false if update count or no result
245
* @throws SQLException if execution fails
246
*/
247
boolean execute() throws SQLException;
248
249
// Parameter setters
250
251
/**
252
* Sets a parameter to SQL NULL.
253
*
254
* @param parameterIndex Parameter index (1-based)
255
* @param sqlType SQL type code from java.sql.Types
256
* @throws SQLException if index is invalid
257
*/
258
void setNull(int parameterIndex, int sqlType) throws SQLException;
259
260
/**
261
* Sets a boolean parameter.
262
*/
263
void setBoolean(int parameterIndex, boolean x) throws SQLException;
264
265
/**
266
* Sets a byte parameter.
267
*/
268
void setByte(int parameterIndex, byte x) throws SQLException;
269
270
/**
271
* Sets a short parameter.
272
*/
273
void setShort(int parameterIndex, short x) throws SQLException;
274
275
/**
276
* Sets an int parameter.
277
*/
278
void setInt(int parameterIndex, int x) throws SQLException;
279
280
/**
281
* Sets a long parameter.
282
*/
283
void setLong(int parameterIndex, long x) throws SQLException;
284
285
/**
286
* Sets a float parameter.
287
*/
288
void setFloat(int parameterIndex, float x) throws SQLException;
289
290
/**
291
* Sets a double parameter.
292
*/
293
void setDouble(int parameterIndex, double x) throws SQLException;
294
295
/**
296
* Sets a BigDecimal parameter.
297
*/
298
void setBigDecimal(int parameterIndex, java.math.BigDecimal x) throws SQLException;
299
300
/**
301
* Sets a String parameter.
302
*/
303
void setString(int parameterIndex, String x) throws SQLException;
304
305
/**
306
* Sets a byte array parameter.
307
*/
308
void setBytes(int parameterIndex, byte[] x) throws SQLException;
309
310
/**
311
* Sets a Date parameter.
312
*/
313
void setDate(int parameterIndex, Date x) throws SQLException;
314
315
/**
316
* Sets a Time parameter.
317
*/
318
void setTime(int parameterIndex, java.sql.Time x) throws SQLException;
319
320
/**
321
* Sets a Timestamp parameter.
322
*/
323
void setTimestamp(int parameterIndex, Timestamp x) throws SQLException;
324
325
/**
326
* Sets a parameter from an InputStream (for large text/binary data).
327
*/
328
void setBinaryStream(int parameterIndex, InputStream x, int length)
329
throws SQLException;
330
331
/**
332
* Sets a parameter from a Reader (for large character data).
333
*/
334
void setCharacterStream(int parameterIndex, Reader reader, int length)
335
throws SQLException;
336
337
/**
338
* Sets an Object parameter with automatic type mapping.
339
*/
340
void setObject(int parameterIndex, Object x) throws SQLException;
341
342
/**
343
* Sets an Object parameter with target SQL type.
344
*/
345
void setObject(int parameterIndex, Object x, int targetSqlType)
346
throws SQLException;
347
348
/**
349
* Sets an Array parameter.
350
*/
351
void setArray(int parameterIndex, Array x) throws SQLException;
352
353
/**
354
* Clears all parameter values.
355
*/
356
void clearParameters() throws SQLException;
357
358
/**
359
* Adds a set of parameters to the batch.
360
* Call after setting all parameters for one execution.
361
*/
362
void addBatch() throws SQLException;
363
}
364
```
365
366
**Usage Examples:**
367
368
```java
369
import java.sql.Connection;
370
import java.sql.PreparedStatement;
371
import java.sql.ResultSet;
372
import java.sql.SQLException;
373
import java.sql.Date;
374
import java.sql.Timestamp;
375
376
// Example 1: Basic PreparedStatement
377
public class BasicPreparedStatement {
378
public static void queryWithParameters(Connection conn) throws SQLException {
379
String sql = "SELECT id, name, email FROM users WHERE active = ? AND created_date > ?";
380
381
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
382
// Set parameters (1-based indexing)
383
pstmt.setBoolean(1, true);
384
pstmt.setDate(2, Date.valueOf("2024-01-01"));
385
386
try (ResultSet rs = pstmt.executeQuery()) {
387
while (rs.next()) {
388
System.out.println(rs.getInt("id") + ": " + rs.getString("name"));
389
}
390
}
391
}
392
}
393
}
394
395
// Example 2: INSERT with PreparedStatement
396
public class InsertExample {
397
public static void insertUser(Connection conn, String name, String email)
398
throws SQLException {
399
String sql = "INSERT INTO users (name, email, created_at) VALUES (?, ?, ?)";
400
401
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
402
pstmt.setString(1, name);
403
pstmt.setString(2, email);
404
pstmt.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
405
406
int rowsAffected = pstmt.executeUpdate();
407
System.out.println("Inserted " + rowsAffected + " row(s)");
408
}
409
}
410
}
411
412
// Example 3: Batch INSERT with PreparedStatement
413
public class BatchInsertExample {
414
public static void batchInsert(Connection conn, List<User> users)
415
throws SQLException {
416
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
417
418
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
419
for (User user : users) {
420
pstmt.setString(1, user.getName());
421
pstmt.setString(2, user.getEmail());
422
pstmt.addBatch(); // Add to batch
423
}
424
425
// Execute all inserts at once
426
int[] results = pstmt.executeBatch();
427
System.out.println("Batch inserted " + results.length + " rows");
428
}
429
}
430
}
431
432
// Example 4: Handling NULL values
433
public class NullHandlingExample {
434
public static void insertWithNulls(Connection conn) throws SQLException {
435
String sql = "INSERT INTO products (name, description, price) VALUES (?, ?, ?)";
436
437
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
438
pstmt.setString(1, "Product A");
439
pstmt.setNull(2, java.sql.Types.VARCHAR); // NULL description
440
pstmt.setBigDecimal(3, new java.math.BigDecimal("19.99"));
441
442
pstmt.executeUpdate();
443
}
444
}
445
}
446
447
// Example 5: Reusing PreparedStatement
448
public class ReusePreparedStatement {
449
public static void insertMultiple(Connection conn) throws SQLException {
450
String sql = "INSERT INTO logs (level, message) VALUES (?, ?)";
451
452
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
453
// First execution
454
pstmt.setString(1, "INFO");
455
pstmt.setString(2, "Application started");
456
pstmt.executeUpdate();
457
458
// Clear and set new values
459
pstmt.clearParameters();
460
pstmt.setString(1, "DEBUG");
461
pstmt.setString(2, "Debug message");
462
pstmt.executeUpdate();
463
}
464
}
465
}
466
467
// Example 6: Binary data
468
public class BinaryDataExample {
469
public static void insertBinaryData(Connection conn, byte[] imageData)
470
throws SQLException {
471
String sql = "INSERT INTO images (name, data) VALUES (?, ?)";
472
473
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
474
pstmt.setString(1, "photo.jpg");
475
pstmt.setBytes(2, imageData);
476
pstmt.executeUpdate();
477
}
478
}
479
}
480
481
// Example 7: PostgreSQL arrays
482
public class ArrayExample {
483
public static void insertArray(Connection conn) throws SQLException {
484
String sql = "INSERT INTO documents (tags) VALUES (?)";
485
486
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
487
// Create PostgreSQL array
488
String[] tags = {"java", "postgresql", "jdbc"};
489
Array sqlArray = conn.createArrayOf("text", tags);
490
491
pstmt.setArray(1, sqlArray);
492
pstmt.executeUpdate();
493
494
sqlArray.free();
495
}
496
}
497
}
498
```
499
500
### CallableStatement
501
502
Support for calling PostgreSQL functions and procedures.
503
504
```java { .api }
505
import java.sql.CallableStatement;
506
import java.sql.SQLException;
507
import java.sql.Types;
508
509
/**
510
* CallableStatement is used to execute stored procedures and functions.
511
* PostgreSQL driver provides full support for calling database functions.
512
*/
513
public interface CallableStatement extends PreparedStatement {
514
/**
515
* Registers an OUT parameter for a stored procedure.
516
*
517
* @param parameterIndex Parameter index (1-based)
518
* @param sqlType SQL type code from java.sql.Types
519
* @throws SQLException if registration fails
520
*/
521
void registerOutParameter(int parameterIndex, int sqlType) throws SQLException;
522
523
/**
524
* Registers an OUT parameter with specific type name.
525
*
526
* @param parameterIndex Parameter index
527
* @param sqlType SQL type code
528
* @param typeName PostgreSQL type name
529
* @throws SQLException if registration fails
530
*/
531
void registerOutParameter(int parameterIndex, int sqlType, String typeName)
532
throws SQLException;
533
534
/**
535
* Indicates whether the last OUT parameter read had value SQL NULL.
536
*
537
* @return true if last parameter was NULL
538
* @throws SQLException if check fails
539
*/
540
boolean wasNull() throws SQLException;
541
542
/**
543
* Retrieves a String OUT parameter.
544
*/
545
String getString(int parameterIndex) throws SQLException;
546
547
/**
548
* Retrieves a boolean OUT parameter.
549
*/
550
boolean getBoolean(int parameterIndex) throws SQLException;
551
552
/**
553
* Retrieves an int OUT parameter.
554
*/
555
int getInt(int parameterIndex) throws SQLException;
556
557
/**
558
* Retrieves a long OUT parameter.
559
*/
560
long getLong(int parameterIndex) throws SQLException;
561
562
/**
563
* Retrieves a double OUT parameter.
564
*/
565
double getDouble(int parameterIndex) throws SQLException;
566
567
/**
568
* Retrieves an Object OUT parameter.
569
*/
570
Object getObject(int parameterIndex) throws SQLException;
571
572
/**
573
* Retrieves an Array OUT parameter.
574
*/
575
java.sql.Array getArray(int parameterIndex) throws SQLException;
576
}
577
```
578
579
**Usage Examples:**
580
581
```java
582
import java.sql.Connection;
583
import java.sql.CallableStatement;
584
import java.sql.Types;
585
import java.sql.SQLException;
586
587
// Example 1: Call function with return value
588
public class FunctionCallExample {
589
public static void callFunction(Connection conn) throws SQLException {
590
// PostgreSQL function: CREATE FUNCTION get_user_count() RETURNS integer
591
String sql = "{ ? = call get_user_count() }";
592
593
try (CallableStatement cstmt = conn.prepareCall(sql)) {
594
// Register OUT parameter for return value
595
cstmt.registerOutParameter(1, Types.INTEGER);
596
597
// Execute
598
cstmt.execute();
599
600
// Get return value
601
int count = cstmt.getInt(1);
602
System.out.println("User count: " + count);
603
}
604
}
605
}
606
607
// Example 2: Call function with IN and OUT parameters
608
public class InOutParametersExample {
609
public static void callWithParameters(Connection conn) throws SQLException {
610
// Function: CREATE FUNCTION calculate_discount(price numeric, OUT discount numeric)
611
String sql = "{ call calculate_discount(?, ?) }";
612
613
try (CallableStatement cstmt = conn.prepareCall(sql)) {
614
// Set IN parameter
615
cstmt.setBigDecimal(1, new java.math.BigDecimal("100.00"));
616
617
// Register OUT parameter
618
cstmt.registerOutParameter(2, Types.NUMERIC);
619
620
// Execute
621
cstmt.execute();
622
623
// Get OUT parameter
624
java.math.BigDecimal discount = cstmt.getBigDecimal(2);
625
System.out.println("Discount: " + discount);
626
}
627
}
628
}
629
630
// Example 3: Call function returning composite type
631
public class CompositeReturnExample {
632
public static void callReturningComposite(Connection conn) throws SQLException {
633
// Function returning user record
634
String sql = "SELECT * FROM get_user_by_id(?)";
635
636
try (CallableStatement cstmt = conn.prepareCall(sql)) {
637
cstmt.setInt(1, 123);
638
639
try (ResultSet rs = cstmt.executeQuery()) {
640
if (rs.next()) {
641
String name = rs.getString("name");
642
String email = rs.getString("email");
643
System.out.println(name + ": " + email);
644
}
645
}
646
}
647
}
648
}
649
650
// Example 4: Call procedure (PostgreSQL 11+)
651
public class ProcedureCallExample {
652
public static void callProcedure(Connection conn) throws SQLException {
653
// Procedure: CREATE PROCEDURE transfer_funds(from_id int, to_id int, amount numeric)
654
String sql = "{ call transfer_funds(?, ?, ?) }";
655
656
try (CallableStatement cstmt = conn.prepareCall(sql)) {
657
cstmt.setInt(1, 100); // from_id
658
cstmt.setInt(2, 200); // to_id
659
cstmt.setBigDecimal(3, new java.math.BigDecimal("50.00")); // amount
660
661
cstmt.execute();
662
System.out.println("Funds transferred");
663
}
664
}
665
}
666
```
667
668
### Query Execution Modes
669
670
PostgreSQL JDBC driver supports different query execution protocols.
671
672
```java { .api }
673
package org.postgresql.jdbc;
674
675
/**
676
* Query execution mode options.
677
* Controls which PostgreSQL protocol is used for query execution.
678
*
679
* Note: Invalid mode values default to EXTENDED.
680
*/
681
public enum PreferQueryMode {
682
/**
683
* Use simple query protocol (text-only).
684
* Sends queries as plain text, receives results as text.
685
* Does not support binary transfer or certain features.
686
*/
687
SIMPLE,
688
689
/**
690
* Always use extended query protocol.
691
* Supports binary transfer, prepared statements, and all features.
692
* Slightly more overhead than simple protocol.
693
* This is the default fallback for invalid/unknown mode values.
694
*/
695
EXTENDED,
696
697
/**
698
* Use extended protocol only for prepared statements.
699
* Simple statements use simple protocol.
700
* Default mode providing good balance of features and performance.
701
*/
702
EXTENDED_FOR_PREPARED,
703
704
/**
705
* Extended protocol with aggressive caching.
706
* Caches all statement plans, even for simple statements.
707
* Provides best performance for repeated queries.
708
*/
709
EXTENDED_CACHE_EVERYTHING;
710
711
/**
712
* Returns the string value of this mode for use in connection properties.
713
*
714
* @return String value (e.g., "simple", "extended", "extendedForPrepared", "extendedCacheEverything")
715
*/
716
public String value();
717
718
/**
719
* Parses a string mode value and returns the corresponding enum constant.
720
*
721
* @param mode String mode value
722
* @return PreferQueryMode enum constant, or EXTENDED if mode is invalid/unknown
723
*/
724
public static PreferQueryMode of(String mode);
725
}
726
```
727
728
**Usage Example:**
729
730
```java
731
import org.postgresql.PGProperty;
732
import java.sql.Connection;
733
import java.sql.DriverManager;
734
import java.util.Properties;
735
736
public class QueryModeExample {
737
public static Connection getConnectionWithMode(String mode) throws SQLException {
738
Properties props = new Properties();
739
props.setProperty("user", "postgres");
740
props.setProperty("password", "secret");
741
props.setProperty("preferQueryMode", mode); // simple, extended, etc.
742
743
return DriverManager.getConnection("jdbc:postgresql://localhost/mydb", props);
744
}
745
}
746
```
747
748
### Statement Performance Tips
749
750
**Best Practices:**
751
752
1. **Use PreparedStatement for repeated queries**
753
```java
754
// Good: PreparedStatement with parameter
755
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
756
pstmt.setInt(1, userId);
757
758
// Bad: Statement with concatenation (SQL injection risk!)
759
Statement stmt = conn.createStatement();
760
stmt.executeQuery("SELECT * FROM users WHERE id = " + userId);
761
```
762
763
2. **Use batch operations for bulk inserts**
764
```java
765
PreparedStatement pstmt = conn.prepareStatement("INSERT INTO logs (msg) VALUES (?)");
766
for (String msg : messages) {
767
pstmt.setString(1, msg);
768
pstmt.addBatch();
769
}
770
pstmt.executeBatch(); // Much faster than individual executes
771
```
772
773
3. **Configure prepare threshold appropriately**
774
```java
775
// For frequently executed statements, prepare immediately
776
PreparedStatement pstmt = conn.prepareStatement(sql);
777
((PGStatement) pstmt).setPrepareThreshold(0);
778
779
// For rarely executed statements, never prepare
780
((PGStatement) pstmt).setPrepareThreshold(-1);
781
```
782
783
4. **Set fetch size for large result sets**
784
```java
785
Statement stmt = conn.createStatement();
786
stmt.setFetchSize(100); // Fetch 100 rows at a time
787
ResultSet rs = stmt.executeQuery("SELECT * FROM large_table");
788
```
789
790
5. **Reuse PreparedStatement objects**
791
```java
792
// Good: Reuse prepared statement
793
PreparedStatement pstmt = conn.prepareStatement(sql);
794
for (User user : users) {
795
pstmt.setString(1, user.getName());
796
pstmt.executeUpdate();
797
pstmt.clearParameters();
798
}
799
pstmt.close();
800
```
801