How do I batch insert data with JDBC?

To batch insert data with JDBC, you typically use the addBatch() and executeBatch() methods. This is much more efficient than executing individual INSERT statements because it reduces the number of round-trips between your application and the database.

The most common and secure way to do this is with a PreparedStatement.

Batch Insert with PreparedStatement

Using PreparedStatement allows you to define a template query and then add multiple sets of parameters to a single batch.

package org.kodejava.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class JDBCBatchInsert {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/your_db";
        String user = "user";
        String password = "password";

        String sql = "INSERT INTO employees (name, department) VALUES (?, ?)";

        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            // 1. Disable auto-commit for better performance and transaction control
            conn.setAutoCommit(false);

            try (PreparedStatement pstmt = conn.prepareStatement(sql)) {

                // Add first record to batch
                pstmt.setString(1, "Alice");
                pstmt.setString(2, "Engineering");
                pstmt.addBatch();

                // Add second record to batch
                pstmt.setString(1, "Bob");
                pstmt.setString(2, "Marketing");
                pstmt.addBatch();

                // 2. Execute the batch
                int[] results = pstmt.executeBatch();

                // 3. Commit the transaction
                conn.commit();
                System.out.println("Batch executed. Rows affected: " + results.length);

            } catch (SQLException e) {
                // Rollback in case of error
                conn.rollback();
                e.printStackTrace();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Key Considerations

  1. setAutoCommit(false): By default, JDBC commits every statement individually. Turning this off allows the entire batch to be treated as a single transaction, which significantly boosts performance.
  2. addBatch(): Adds the current set of parameters to the internal list of commands.
  3. executeBatch(): Sends all the gathered commands to the database. It returns an int[] where each element represents the update count for the corresponding command in the batch.
  4. Batch Size: For very large datasets (e.g., thousands of rows), don’t add everything to a single batch. Instead, execute the batch every 500–1000 rows to avoid memory issues:
    if (count % 1000 == 0) {
        pstmt.executeBatch();
        conn.commit(); // Optional: commit periodically
    }
    

Using Statement

While possible, using Statement.addBatch(String sql) is generally discouraged for inserts involving variables because it is vulnerable to SQL injection and harder for the database to optimize. Use PreparedStatement whenever possible.

How do I delete rows with JDBC?

To delete rows from a database using JDBC, you use the executeUpdate() method. This method is used for SQL statements that modify data (like DELETE, INSERT, or UPDATE) and returns an integer representing the number of rows affected.

While you can use a simple Statement, it is highly recommended to use a PreparedStatement to prevent SQL injection and handle parameters safely.

Example: Deleting a Row with PreparedStatement

Here is a typical implementation using the try-with-resources pattern to ensure database connections are closed properly:

package org.kodejava.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class DeleteRowExample {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/your_database";
        String user = "username";
        String password = "password";

        // SQL query with a placeholder (?) for the ID
        String sql = "DELETE FROM users WHERE id = ?";

        try (Connection conn = DriverManager.getConnection(url, user, password);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            // Set the value for the placeholder
            int idToDelete = 101;
            pstmt.setInt(1, idToDelete);

            // Execute the delete operation
            int rowsDeleted = pstmt.executeUpdate();

            if (rowsDeleted > 0) {
                System.out.println("Successfuly deleted " + rowsDeleted + " row(s).");
            } else {
                System.out.println("No record found with the specified ID.");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Key Steps:

  1. Prepare the SQL: Use the DELETE syntax. Use ? as placeholders for dynamic values.
  2. Create a PreparedStatement: Call connection.prepareStatement(sql).
  3. Bind Parameters: Use setter methods like setInt(), setString(), or setLong() to provide values for the placeholders (indices start at 1).
  4. Execute Update: Call executeUpdate(). It returns the count of deleted rows.
  5. Handle Exceptions: Wrap the code in a try-catch block to handle SQLException.

Which method should I use?

  • executeUpdate(): Use this for DELETE statements. It returns the number of rows removed.
  • Statement: Use only for static SQL with no user input.
  • PreparedStatement: Always preferred for security and performance when using variables in your WHERE clause.

How do I insert rows with JDBC?

To insert rows into a database using JDBC, you typically use the executeUpdate(String sql) method of a Statement or PreparedStatement object.

Here are the two primary ways to do it:

1. Using PreparedStatement (Recommended)

This is the standard approach because it prevents SQL Injection and is more efficient for repeated inserts.

package org.kodejava.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class InsertRecordExample {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/kodejava";
        String user = "root";
        String password = "password";

        String sql = "INSERT INTO users (username, email) VALUES (?, ?)";

        try (Connection conn = DriverManager.getConnection(url, user, password);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            // Set the values for the placeholders (?)
            pstmt.setString(1, "john_doe");
            pstmt.setString(2, "[email protected]");

            // executeUpdate returns the number of rows affected
            int rowsInserted = pstmt.executeUpdate();
            if (rowsInserted > 0) {
                System.out.println("A new user was inserted successfully!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

2. Using Statement

Use this only for simple, static SQL queries where no user input is involved.

// ... existing code ...
    try (Connection conn = DriverManager.getConnection(url, user, password);
         Statement stmt = conn.createStatement()) {

        String sql = "INSERT INTO users (username, email) VALUES ('jane_doe', '[email protected]')";
        int rows = stmt.executeUpdate(sql);

        System.out.println("Rows affected: " + rows);
    } catch (SQLException e) {
        e.printStackTrace();
    }
// ... existing code ...

Key Takeaways:

  • executeUpdate(): Unlike executeQuery() (which returns a ResultSet), executeUpdate() returns an int representing how many rows were added, changed, or deleted.
  • Try-with-resources: Always wrap your Connection, Statement, or PreparedStatement in a try-with-resources block to ensure they are closed automatically, even if an error occurs.
  • Placeholders: In a PreparedStatement, indices for ? parameters start at 1.

How do I update rows with executeUpdate?

In JDBC, the executeUpdate method is used for SQL statements that modify data, such as UPDATE, INSERT, or DELETE. Unlike executeQuery, which returns a ResultSet, executeUpdate returns an int representing the number of rows affected by the operation.

Here is how you can update rows using PreparedStatement (the recommended way) and Statement.

1. Using PreparedStatement (Recommended)

This approach is more secure as it prevents SQL injection and is more efficient for queries used multiple times.

package org.kodejava.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class UpdateExample {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/musicdb";
        String user = "root";
        String password = "password";

        String sql = "UPDATE album SET title = ? WHERE id = ?";

        try (Connection conn = DriverManager.getConnection(url, user, password);
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            // Bind values to the placeholders (?)
            pstmt.setString(1, "The White Album");
            pstmt.setLong(2, 10L);

            // Execute the update
            int rowsAffected = pstmt.executeUpdate();

            System.out.println("Rows updated: " + rowsAffected);

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

2. Using Statement

Use this only for simple, static SQL queries where no user input is involved.

String sql = "UPDATE album SET title = 'Rubber Soul' WHERE id = 5";

try (Connection conn = DriverManager.getConnection(url, user, password);
     Statement stmt = conn.createStatement()) {

    int rowsAffected = stmt.executeUpdate(sql);
    System.out.println("Rows updated: " + rowsAffected);
}

Key Points to Remember:

  • Return Value: The method returns the number of rows changed. If it returns 0, it means no rows matched your WHERE clause.
  • Transaction Control: If you have set connection.setAutoCommit(false), you must call connection.commit() after executeUpdate() to persist the changes to the database.
  • Resource Management: Always use a try-with-resources block to ensure the Connection and Statement are closed automatically, which prevents database connection leaks.

How do I fetch results with ResultSet?

To fetch results using a ResultSet in Java JDBC, you follow a standard pattern of executing a query, iterating through the rows, and extracting data using “getter” methods.

Basic Fetching Pattern

  1. Execute the Query: Use stmt.executeQuery(sql) (for Statement) or pstmt.executeQuery() (for PreparedStatement).
  2. Iterate through Rows: Use a while (rs.next()) loop. The next() method moves the cursor to the next row and returns false when there are no more rows.
  3. Retrieve Values: Use methods like getString(), getInt(), getLong(), or getBigDecimal() based on the column type.

Example

try (Connection conn = DriverManager.getConnection(url, user, pass);
     Statement stmt = conn.createStatement();
     ResultSet rs = stmt.executeQuery("SELECT id, name, created_at FROM users")) {

    while (rs.next()) {
        // 1. Fetch by Column Name (Recommended for readability)
        int id = rs.getInt("id");
        String name = rs.getString("name");

        // 2. Fetch by Column Index (1-based, often faster)
        java.sql.Timestamp date = rs.getTimestamp(3);

        System.out.println("User: " + id + " | Name: " + name);
    }
} catch (SQLException e) {
    e.printStackTrace();
}

Key Considerations

  • 1-Based Indexing: If you use column indices (e.g., rs.getString(1)), remember that JDBC indices start at 1, not 0.
  • Column Names vs. Indices:
    • Names: More maintainable and easier to read.
    • Indices: Slightly better performance as the driver doesn’t have to look up the column mapping.
  • Try-with-Resources: Always wrap the ResultSet in a try-with-resources block (or ensure it is closed in a finally block) to prevent database cursor leaks.
  • Data Types: Use the getter that matches your database schema:
    • rs.getInt() for INTEGER
    • rs.getString() for VARCHAR/TEXT
    • rs.getBigDecimal() for DECIMAL/NUMERIC (best for currency)
    • rs.getBoolean() for BIT/BOOLEAN
  • Handling Nulls: If a column allows NULL, primitive getters (like getInt) will return 0. To check if the value was actually null in the database, call rs.wasNull() immediately after the getter.