A Stored Procedure is a precompiled group of one or more SQL statements stored in the database. It can accept input parameters (
IN), return output values (OUT), or both (INOUT). Stored procedures allow applications to delegate logic to the database server, leading to:
- Reduced application complexity
- Improved performance due to precompiled execution
- Easier maintenance and reuse of SQL logic
Stored procedures can simplify Java application logic by offloading reusable SQL code to the database layer. With JDBC’s CallableStatement, interacting with IN, OUT, and INOUT parameters is straightforward. By mastering this integration, Java developers can write more efficient, maintainable, and scalable database-driven applications.
JDBC call syntax:
{CALL procedure_name(?, ?, ?)}
Each ? corresponds to a parameter (IN, OUT, or INOUT).
IN Parameters
An IN parameter is used to pass a value from Java to the stored procedure.
Example: MySQL/PostgreSQL
CREATE OR REPLACE PROCEDURE add_student(IN student_name VARCHAR(255))
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO student(name) VALUES (student_name);
END;
$$;
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
CallableStatement stmt = conn.prepareCall("{CALL add_student(?)}")) {
stmt.setString(1, "John Doe");
stmt.execute();
System.out.println("Student added.");
}
OUT Parameters
An OUT parameter is used to return a value from the stored procedure to Java.
CREATE OR REPLACE PROCEDURE get_student_name(
IN student_id INT,
OUT student_name VARCHAR(255)
)
LANGUAGE plpgsql
AS $$
BEGIN
SELECT name INTO student_name
FROM student
WHERE id = student_id;
END;
$$;
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
CallableStatement stmt = conn.prepareCall("{CALL get_student_name(?, ?)}")) {
stmt.setInt(1, 1); // student_id
stmt.registerOutParameter(2, Types.VARCHAR);
stmt.execute();
String name = stmt.getString(2);
System.out.println("Student name: " + name);
}
INOUT Parameters
An INOUT parameter takes a value from Java, possibly modifies it in the stored procedure, and returns the modified value back.
CREATE OR REPLACE PROCEDURE append_suffix(INOUT student_name VARCHAR(255))
LANGUAGE plpgsql
AS $$
BEGIN
student_name := student_name || ' - Verified';
END;
$$;
try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
CallableStatement stmt = conn.prepareCall("{CALL append_suffix(?)}")) {
stmt.setString(1, "Jane Doe");
stmt.registerOutParameter(1, Types.VARCHAR);
stmt.execute();
String updatedName = stmt.getString(1);
System.out.println("Updated name: " + updatedName);
}
Batch Execution with CallableStatement
You can also perform batch inserts/updates using stored procedures via CallableStatement.
This works best with only IN parameters—OUT/INOUT parameters are not batch-friendly.
PostgreSQL Example:
CREATE OR REPLACE PROCEDURE insert_student_in(
IN p_name TEXT
)
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO student (name)
VALUES (p_name);
END;
$$;
Java Batch Call:
try (Connection conn = dataSource.getConnection()) {
conn.setAutoCommit(false);
try (CallableStatement stmt = conn.prepareCall("{CALL insert_student_in(?)}")) {
stmt.setString(1, "Alice");
stmt.addBatch();
stmt.setString(1, "Bob");
stmt.addBatch();
stmt.setString(1, "Charlie");
stmt.addBatch();
int[] results = stmt.executeBatch();
conn.commit();
System.out.println("Inserted rows: " + results.length);
} catch (SQLException e) {
conn.rollback();
throw e;
}
}
CallableStatement vs PreparedStatement
| Use Case | JDBC API | Notes |
|---|---|---|
| Stored Procedure | CallableStatement |
Supports IN, OUT, INOUT |
| Stored Function (returns value) | PreparedStatement |
Simpler and cleaner for PostgreSQL |
CallableStatement is ideal for calling stored procedures:
CallableStatement stmt = conn.prepareCall("{call my_procedure(?, ?, ?)}");
stmt.setString(1, "val");
stmt.registerOutParameter(2, Types.INTEGER);
stmt.execute();
PreparedStatement is better for stored functions in PostgreSQL:
PreparedStatement stmt = conn.prepareStatement("SELECT my_function(?, ?)");
Best Practices
- Always close JDBC resources (use try-with-resources).
- Disable autocommit for batch calls and commit explicitly.
- Avoid business logic in stored procedures unless performance dictates.
- Use descriptive procedure and parameter names.
- Catch and handle SQL exceptions both in Java and SQL.