Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
CallableStatement в Java JDBC
CallableStatement — это интерфейс JDBC для вызова хранимых процедур в базе данных. Он расширяет функциональность PreparedStatement, добавляя возможность работы с OUT и INOUT параметрами.
Основная концепция
В отличие от обычного SQL запроса, хранимая процедура — это код, скомпилированный на стороне СУБД. CallableStatement позволяет:
- Вызывать хранимые процедуры из Java приложения
- Передавать параметры (IN, OUT, INOUT)
- Получать результаты из OUT параметров и ResultSet
- Получать коды возврата (Return value)
Синтаксис вызова
Для вызова хранимой процедуры используется синтаксис CALL:
// Синтаксис: {CALL procedure_name(?,?,?)}
// Для функций: {? = CALL function_name(?,?)}
Connection conn = DriverManager.getConnection(url, user, password);
// Вызов хранимой процедуры
String sql = "{CALL GetUserById(?)}"; // (?) для входного параметра
CallableStatement cstmt = conn.prepareCall(sql);
// Вызов функции, возвращающей значение
String sqlFunc = "{? = CALL CalculateBonus(?)}";
CallableStatement cstmtFunc = conn.prepareCall(sqlFunc);
Типы параметров
IN параметры — данные передаются в процедуру:
String sql = "{CALL InsertUser(?, ?)}"; // Две IN переменные
CallableStatement cstmt = conn.prepareCall(sql);
cstmt.setString(1, "John Doe"); // Первый параметр
cstmt.setInt(2, 30); // Второй параметр
cstmt.execute();
OUT параметры — данные возвращаются из процедуры:
String sql = "{CALL GetUserCount(?)}";
CallableStatement cstmt = conn.prepareCall(sql);
// Регистрируем OUT параметр
cstmt.registerOutParameter(1, Types.INTEGER); // Параметр 1 — INT результат
cstmt.execute();
// Получаем результат
int count = cstmt.getInt(1);
System.out.println("Всего пользователей: " + count);
INOUT параметры — передача и получение данных:
String sql = "{CALL ProcessBonus(?)}";
CallableStatement cstmt = conn.prepareCall(sql);
// Передаём начальное значение
cstmt.setDouble(1, 1000.0);
// Регистрируем как OUT (будет обновлено)
cstmt.registerOutParameter(1, Types.DOUBLE);
cstmt.execute();
// Получаем изменённое значение
double bonusAmount = cstmt.getDouble(1);
System.out.println("Бонус: " + bonusAmount);
Практический пример
Предположим, в БД есть хранимая процедура:
CREATE PROCEDURE GetEmployeeById(
IN emp_id INT,
OUT emp_name VARCHAR(100),
OUT emp_salary DECIMAL(10,2)
)
BEGIN
SELECT name, salary
INTO emp_name, emp_salary
FROM employees
WHERE id = emp_id;
END;
Вызов из Java:
public void getEmployeeInfo(int employeeId) throws SQLException {
String sql = "{CALL GetEmployeeById(?, ?, ?)}"; // 1 IN, 2 OUT
try (Connection conn = getConnection();
CallableStatement cstmt = conn.prepareCall(sql)) {
// Устанавливаем IN параметр
cstmt.setInt(1, employeeId);
// Регистрируем OUT параметры
cstmt.registerOutParameter(2, Types.VARCHAR); // name
cstmt.registerOutParameter(3, Types.DECIMAL); // salary
// Выполняем процедуру
cstmt.execute();
// Получаем результаты
String name = cstmt.getString(2);
BigDecimal salary = cstmt.getBigDecimal(3);
System.out.println("Имя: " + name + ", Зарплата: " + salary);
}
}
Работа с ResultSet
Хранимая процедура может возвращать несколько результирующих наборов:
String sql = "{CALL GetAllUsers()}"; // Процедура возвращает ResultSet
CallableStatement cstmt = conn.prepareCall(sql);
boolean isResultSet = cstmt.execute();
while (isResultSet) {
try (ResultSet rs = cstmt.getResultSet()) {
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.println(id + ": " + name);
}
}
isResultSet = cstmt.getMoreResults();
}
Обработка исключений
public void callProcedureSafely() {
String sql = "{CALL ProcessOrder(?)}";
try (Connection conn = getConnection();
CallableStatement cstmt = conn.prepareCall(sql)) {
cstmt.setInt(1, 123);
cstmt.execute();
} catch (SQLException e) {
System.err.println("Ошибка вызова процедуры: " + e.getMessage());
throw new RuntimeException("Ошибка БД", e);
}
}
Преимущества и недостатки
Преимущества:
- Производительность — код выполняется на СУБД
- Безопасность — бизнес-логика защищена на уровне БД
- Переиспользуемость — процедура доступна для разных приложений
Недостатки:
- Зависимость от СУБД — синтаксис отличается (MySQL, PostgreSQL, Oracle)
- Сложность тестирования — нужна БД для тестов
- Усложнение архитектуры — логика распределена между слоями
Современные альтернативы
В современных приложениях часто используют ORM (Hibernate, JPA) или SQL-фреймворки, которые упрощают работу:
// С помощью JPA
@NamedStoredProcedureQuery(
name = "GetUserCount",
procedureName = "GetUserCount",
outputParameters = @StoredProcedureParameter(name = "count", type = Integer.class)
)
public class User {
// ...
}
CallableStatement остаётся актуальным инструментом для сложной работы с БД и внедрения функционала на уровне СУБД.