An overview of Java Database Community
Java Database Connectivity (JDBC) is a crucial Java API through which different kinds of Java applications can be connected to databases. The primary purpose of JDBC is to provide a standard mechanism by which database operations, such as the execution of SQL queries, retrieval of data, and management of transactions, are performed by a Java application to a relational database.
JDBC abstracts the database communication with the underlying database; it enables developers to communicate with databases using Java without any need for knowledge of native protocols of the database. The usage of the JDBC API enables a Java application to do the following:
- Establish a connection to a database
- Run SQL queries – SELECT, INSERT, UPDATE, DELETE
- Process results of the query
- Manage database transactions-commit, rollback
- Handle SQL exceptions
JDBC is especially helpful in the making of enterprise applications that require database interaction to store, manipulate, and retrieve data. Besides that, several databases like MySQL, PostgreSQL, Oracle are supported with the API by utilizing various JDBC drivers particular to each database system.
Auto-Increment/Generated Keys
As the name suggests, in a relational database, an auto-increment column is a column where, upon insertion of a new record, the database automatically generates a unique value. The main use of auto-increment columns is for primary keys since they guarantee that every, single row will have an identifier without the need for a user to create one.
Purpose of Auto-Increment Columns
Auto-increment columns make it very easy to insert unique keys for each record, especially when one needs to insert a large number of records quite frequently. Many such columns are used for primary keys, which essentially identify each row in a table, hence guaranteeing referential integrity across the database.
Database-Specific Auto-Increment Mechanisms
- MySQL:
MySQL uses the AUTO_INCREMENT keyword to automatically generate sequential values for a primary key column. For example:
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100)
);
When a new row is inserted without specifying the id, the database will assign the next available integer in sequence to the id column.
- PostgreSQL:
PostgreSQL uses the SERIAL keyword for the same purpose. A SERIAL column automatically increments the value for each new row.
Example:
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
name VARCHAR(100)
);
Under the hood, SERIAL is shorthand for creating a sequence and setting it as the default value for the column.
- SQLite:
SQLite supports an INTEGER PRIMARY KEY column, which automatically behaves as an auto-increment column if no value is
provided during insertion.
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
name TEXT
);
- SQL Server:
In SQL Server, the IDENTITY keyword is used to define auto-increment behavior for a column.
CREATE TABLE employees (
id INT IDENTITY(1,1) PRIMARY KEY,
name NVARCHAR(100)
);
Behaviour of Auto-Increment Columns
When a record is inserted without specifying a value for the auto-increment column, the database automatically assigns the next available integer. It is common to have auto-increment values start at 1 and then increase by 1 for each new row, although the starting value and increment amount are commonly adjustable.
Auto-increment values are unique per table, and even if several rows are deleted from a table, the value continues incrementing, thus leaving gaps in the sequence. But again, this will guarantee that each row has an identifier distinct from the others for some time.
Use Case for Auto-Increment Columns
Auto-increment columns are significant in applications where each row requires a unique identifier, such as user management systems, order tracking, or any other data-driven applications. Inserting records into the database becomes very easy since the database automatically manages the key generation, hence efficient and reducing the chance of duplicate keys.
How to Execute an Insert Statement Using executeUpdate()!
To execute an INSERT statement with PreparedStatement, follow these steps:
1. Create a Connection:
First, establish a connection to the database using DriverManager.
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "user", "password");
2. Define an SQL Insert Query
Prepare an SQL query with placeholders (?) for the parameters you want to insert dynamically. For example:
String sql = "INSERT INTO employees (name, position, salary) VALUES (?, ?, ?)";
3. Create a PreparedStatement
Use the Connection object to create a PreparedStatement for the SQL query.
PreparedStatement pstmt = connection.prepareStatement(sql);
4.Set the Dynamic Values
Use the appropriate setter methods to provide values for each placeholder (?
). For instance:
pstmt.setString(1, "John Doe"); // Set the name (first placeholder)
pstmt.setString(2, "Manager"); // Set the position (second placeholder)
pstmt.setDouble(3, 75000.00); // Set the salary (third placeholder)
5.Execute the Insert Statement
Execute the SQL insert query using executeUpdate(). This method is used to execute DML statements (INSERT, UPDATE, DELETE), and it returns an integer representing the number of affected rows.
int rowsAffected = pstmt.executeUpdate();
In this case, if the insertion is successful, rowsAffected will be 1
, indicating that one row has been inserted into the employees table.
6.Close Resources
After the query execution, always close the PreparedStatement and Connection to free up resources.
pstmt.close();
connection.close();
Example: Full Code for Inserting Data Using PreparedStatement
Here is a complete example of an insert operation using PreparedStatement:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class InsertEmployee {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "user";
String password = "password";
String sql = "INSERT INTO employees (name, position, salary) VALUES (?, ?, ?)";
try (Connection connection = DriverManager.getConnection(url, user, password);
PreparedStatement pstmt = connection.prepareStatement(sql)) {
// Set the dynamic values for the insert query
pstmt.setString(1, "Jane Smith");
pstmt.setString(2, "Developer");
pstmt.setDouble(3, 65000.00);
// Execute the insert statement
int rowsAffected = pstmt.executeUpdate();
System.out.println("Rows inserted: " + rowsAffected);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Overview Key points to remember:
- Efficiency: PreparedStatement compiles the SQL query just once and executes it many times with different parameters, hence enhancing efficiency.
- Security: This would prevent SQL injection because the user’s input is treated as data and not as part of the SQL query.
- Flexibility: It grants passing dynamic query parameters at runtime using different setter methods, such as setString(), setInt() amongst others.
Retrieving Generated Keys with getGeneratedKeys()
Whenever a record is inserted into a table, that has an auto-increment, or serial primary key, it is often useful to get the key generated for the inserted row. By using JDBC, the auto-generated keys from the database after the execution of an INSERT statement can be retrieved by calling getGeneratedKeys().
Statement.RETURN_GENERATED_KEYS Constant
To retrieve the generated keys after an INSERT operation, the PreparedStatement needs to be created with the Statement.RETURN_GENERATED_KEYS flag. This tells the JDBC driver to return the generated keys for the insert operation.
PreparedStatement pstmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
This constant ensures that after executing the INSERT statement, the generated key (e.g., the auto-incremented ID) can be fetched using the getGeneratedKeys() method.
How getGeneratedKeys() Works!
- Insert the Row:
After the new record has been inserted, the database generates a key, typically a primary key, because of its auto-increment property. - Get the Key:
Once the insertion is complete, you call getGeneratedKeys() on the PreparedStatement object. This will return a ResultSet containing the generated keys(s). - Retrieve the Generated Key:
The standard method next(), and then getLong() or getInt() depending on key type will return the generated key from the result set.
Example: Using getGeneratedKeys() to Retrieve an Auto-Generated Key
Here is an example demonstrating how to use the getGeneratedKeys() for retrieving an auto-incremented key created after an operation of type INSERT in a table:
1. Declaring an Insert Query
Insert SQL into a table-structure which has an auto-incrementing primary key. Employees would be an example:
String sql = "INSERT INTO employees (name, position, salary) VALUES (?, ?, ?)";
2.Create the PreparedStatement with RETURN_GENERATED_KEYS
When creating the PreparedStatement, use the Statement.RETURN_GENERATED_KEYS flag:
PreparedStatement pstmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
3.Set the Values and Execute the Query
Set the values dynamically for the placeholders and execute the insert operation using executeUpdate():
pstmt.setString(1, "Alice Johnson");
pstmt.setString(2, "HR Manager");
pstmt.setDouble(3, 90000.00);
int rowsAffected = pstmt.executeUpdate();
4. Retrieve the Generated Key
After executing the insert statement, retrieve the generated key using the getGeneratedKeys() method:
ResultSet rs = pstmt.getGeneratedKeys();
if (rs.next()) {
long generatedId = rs.getLong(1); // Retrieve the generated key
System.out.println("Generated ID: " + generatedId);
}
5. Close Resources
Always close the ResultSet, PreparedStatement, and Connection when done:
rs.close();
pstmt.close();
connection.close();
Complete Example: Retrieving Auto-Generated Keys in JDBC
Here’s the complete code demonstrating how to insert a record and retrieve the auto-generated primary key:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class InsertWithGeneratedKey {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "user";
String password = "password";
String sql = "INSERT INTO employees (name, position, salary) VALUES (?, ?, ?)";
try (Connection connection = DriverManager.getConnection(url, user, password);
PreparedStatement pstmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
// Set the dynamic values for the insert query
pstmt.setString(1, "Alice Johnson");
pstmt.setString(2, "HR Manager");
pstmt.setDouble(3, 90000.00);
// Execute the insert statement
int rowsAffected = pstmt.executeUpdate();
System.out.println("Rows inserted: " + rowsAffected);
// Retrieve the auto-generated keys
try (ResultSet rs = pstmt.getGeneratedKeys()) {
if (rs.next()) {
long generatedId = rs.getLong(1); // Retrieve the generated key
System.out.println("Generated ID: " + generatedId);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Explanations of Key Steps in the Code:
- Making the PreparedStatement:
The second argument, Statement.RETURN_GENERATED_KEYS, ensures that after the insert, generated keys are returned. - Execute the Insert:
The executeUpdate() method executes the INSERT query; the count of rows affected is captured in rowsAffected. - Get Generated Key:
getGeneratedKeys() returns a ResultSet that will contain generated key(s). The key is retrieved using getLong(1) (use getInt(1) if the key is an integer).
Use Case Scenarios
- User Registration Systems:
When any new user is being registered into the system, it inserts the user information and retrieves the auto-inserted user ID to link the user with other records. - Order Management:
In the case of a new order being placed, most of the time, an order ID generated is used for logging or referring the order in other tables, say order items. - Invoice Generation:
In this regard, whenever there is an invoicing, it retrieves a unique number regarding this invoice, known as ID for linking transaction records.
In the use of getGeneratedKeys(), you are able to manage and fetch auto-incremented keys after doing INSERT operations in an efficient manner that shall streamline the flow of your database interactions.
Handling Multiple Keys Generated
When you execute a batch insert operation, or an INSERT operation that affects multiple rows, more than one key may be generated by the database-one for each inserted row. For retrieving all generated keys, JDBC offers a method to get all keys by iteration on the result set of getGeneratedKeys().
This is needed in some process for batch processing where there are a number of multiple records inserted at once, and you want to capture IDs generated for each in order for you to do more operations on these-for example, linking it with related records in another table.
How to Handle Multiple Generated Keys!
When you insert multiple rows and call getGeneratedKeys(), it returns a ResultSet containing the generated keys, one per inserted row. You can iterate over this result set to retrieve each generated key individually.
Steps to Handle Multiple Generated Keys–
- Insert Multiple Rows:
You can insert multi-record insert either by using batch processing-addBatch() and executeBatch() or single INSERT query with multiple value set. - Retrieve the generated keys:
After the execution of the insert statement, call getGeneratedKeys() to retrieve a ResultSet that contains all generated keys. - Result Set Iterator:
To access every key, use the standard Result Set iteration methods next(), getInt(), getLong() etc. Each line in the result set represents a generated key for an inserted row.
Example: Handling Multiple Generated Keys with Batch Insert
1. Define a Batch Insert Query
Here’s how to use PreparedStatement to insert multiple rows into a table and retrieve their generated keys:
String sql = "INSERT INTO employees (name, position, salary) VALUES (?, ?, ?)";
2. Create the PreparedStatement with RETURN_GENERATED_KEYS
As in the case of a single insert, specify Statement.RETURN_GENERATED_KEYS when preparing the statement:
PreparedStatement pstmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
3. Add Multiple Batches
Add multiple sets of parameters to the batch using addBatch():
pstmt.setString(1, "Alice Johnson");
pstmt.setString(2, "HR Manager");
pstmt.setDouble(3, 90000.00);
pstmt.addBatch(); // Add first row
pstmt.setString(1, "Bob Smith");
pstmt.setString(2, "Developer");
pstmt.setDouble(3, 85000.00);
pstmt.addBatch(); // Add second row
pstmt.setString(1, "Charlie Brown");
pstmt.setString(2, "Analyst");
pstmt.setDouble(3, 80000.00);
pstmt.addBatch(); // Add third row
4. Execute the Batch Insert
Execute all the batched insert statements using executeBatch():
pstmt.executeBatch();
5. Retrieve the Generated Keys
Use getGeneratedKeys() to retrieve the keys for all the inserted rows. Then, iterate over the result set to get each generated key:
ResultSet rs = pstmt.getGeneratedKeys();
while (rs.next()) {
long generatedId = rs.getLong(1); // Retrieve each generated key
System.out.println("Generated ID: " + generatedId);
}
Complete Example: Retrieving Multiple Generated Keys
Here is the complete code that demonstrates inserting multiple rows in a batch and retrieving their generated keys:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class BatchInsertWithGeneratedKeys {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "user";
String password = "password";
String sql = "INSERT INTO employees (name, position, salary) VALUES (?, ?, ?)";
try (Connection connection = DriverManager.getConnection(url, user, password);
PreparedStatement pstmt = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
// Add multiple rows to the batch
pstmt.setString(1, "Alice Johnson");
pstmt.setString(2, "HR Manager");
pstmt.setDouble(3, 90000.00);
pstmt.addBatch(); // Add first row
pstmt.setString(1, "Bob Smith");
pstmt.setString(2, "Developer");
pstmt.setDouble(3, 85000.00);
pstmt.addBatch(); // Add second row
pstmt.setString(1, "Charlie Brown");
pstmt.setString(2, "Analyst");
pstmt.setDouble(3, 80000.00);
pstmt.addBatch(); // Add third row
// Execute the batch insert
pstmt.executeBatch();
// Retrieve the auto-generated keys
try (ResultSet rs = pstmt.getGeneratedKeys()) {
while (rs.next()) {
long generatedId = rs.getLong(1); // Retrieve each generated key
System.out.println("Generated ID: " + generatedId);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Explanation of the Process
- Batch Insert:
We are using addBatch() to add multiple rows to the PreparedStatement. The values for each row are set using setString(), setDouble(), etc. - Executing the Batch:
The executeBatch() method is used to insert all the rows at once. This reduces the overhead of executing multiple individual insert statements. - Retrieving Generated Keys:
After the batch insert, getGeneratedKeys() returns a ResultSet with the generated keys for each inserted row. We then iterate over the result set using while (rs.next()) and retrieve each key using getLong(1).
Sample Use Case Scenarios in Handling Various Keys that Are Generated:
- Order Processing Systems:
In highly articulate orders, these can be inserted en masse into the system, and their keys obtained for linking them with the order record. - Inventory Management:
When a user is adding a number of different products into an inventory, performing bulk inserts of such information, it can retrieve generated IDs for each product to link them to other tables, for example, to suppliers or categories. - Batch User Registration:
Where the application supports the batch registration of users, it can insert multiple user records and retrieve the generated IDs to proceed further with the processing, like assigning user roles or permissions.
By handling multiple generated keys efficiently, you ensure seamless integrations between related records in different tables, hence improving the performance of batch operations.