Что такое CallableStatement в JDBC?

Ответ

CallableStatement — это интерфейс в JDBC API, предназначенный для выполнения хранимых процедур и функций, вызываемых из базы данных. Он наследует от PreparedStatement и поддерживает параметры типов IN (входные), OUT (выходные) и INOUT (входо-выходные).

Пример вызова хранимой процедуры с OUT-параметром:

// Предположим, есть процедура: CREATE PROCEDURE get_employee_name(IN emp_id INT, OUT emp_name VARCHAR(100))
String sql = "{call get_employee_name(?, ?)}"; // Синтаксис вызова процедуры

try (Connection conn = dataSource.getConnection();
     CallableStatement stmt = conn.prepareCall(sql)) {

    // Установка IN-параметра
    stmt.setInt(1, 123);

    // Регистрация OUT-параметра (указание его типа)
    stmt.registerOutParameter(2, Types.VARCHAR);

    // Выполнение процедуры
    stmt.execute();

    // Получение значения OUT-параметра после выполнения
    String employeeName = stmt.getString(2);
    System.out.println("Employee name: " + employeeName);

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

Основные особенности и преимущества:

  • Производительность: Хранимые процедуры часто выполняются быстрее, так как компилируются и кэшируются на стороне СУБД.
  • Бизнес-логика на стороне БД: Позволяет инкапсулировать сложную логику работы с данными внутри базы.
  • Безопасность: Может помочь предотвратить SQL-инъекции и предоставить более строгий контроль доступа.
  • Работа с REF CURSOR: Поддерживает возврат результирующих наборов через курсоры.
  • Множественные результаты: Может возвращать несколько результирующих наборов (используется stmt.getMoreResults()).

Ответ 18+ 🔞

Слушай, а вот этот ваш CallableStatement — это же, блядь, такая специальная штука в JDBC, чтобы с хранимыми процедурами в базе общаться! Ну, типа, не просто тупой SELECT гонять, а вызвать там какую-нибудь навороченную логику, которую админы-садисты прямо в недрах СУБД записали. Наследует он от PreparedStatement, так что все плюшки вроде защиты от инъекций на месте, но ещё умеет параметры IN, OUT и INOUT обрабатывать. Представляешь? Ты ему номер сотрудника суёшь (IN), а он тебе после выполнения имя из базы вытаскивает (OUT) — магия, ёпта!

Вот смотри, как это выглядит в коде, если не хочешь, чтобы тебя потом за такое на хуй послали:

// Допустим, в базе валяется процедура: CREATE PROCEDURE get_employee_name(IN emp_id INT, OUT emp_name VARCHAR(100))
String sql = "{call get_employee_name(?, ?)}"; // Вот этот фигурный синтаксис, блядь, обязателен!

try (Connection conn = dataSource.getConnection();
     CallableStatement stmt = conn.prepareCall(sql)) {

    // Засовываем входной параметр (IN) — ID сотрудника
    stmt.setInt(1, 123);

    // А вот это важно, ёбта! Говорим джаве: "Слушай, второй параметр — выходной, текстовый, готовься его ловить!"
    stmt.registerOutParameter(2, Types.VARCHAR);

    // Поехали! Запускаем процедуру. База там своё колдует...
    stmt.execute();

    // Всё, процедура отработала. Теперь можно вытащить результат из OUT-параметра.
    String employeeName = stmt.getString(2);
    System.out.println("Employee name: " + employeeName); // Ну вот и имя, сука!

} catch (SQLException e) {
    e.printStackTrace(); // А тут, как водится, может всё накрыться медным тазом.
}

И зачем этот геморрой, спросишь? А вот зачем, хитрая жопа:

  • Скорость, блядь! Процедуры на стороне базы уже скомпилированы, они часто быстрее, чем гонять туда-сюда километровые SQL-скрипты.
  • Логика в одном месте. Вся бизнес-муть может сидеть прямо в базе. Для админов — сказка, для разработчиков — иногда ад, но что поделать.
  • Безопасность. Ну, в плане инъекций там всё прикрыто, да и права можно на процедуры навесить, а не на таблицы.
  • Возврат целых наборов данных (REF CURSOR). Может не просто имя вернуть, а целую таблицу результатов! Правда, выковыривать её — ещё то удовольствие.
  • Множественные результаты. Одна процедура может навернуть несколько сетов данных подряд. Чтобы всё это переварить, надо getMoreResults() юзать — волнение ебать, но работает.

Короче, инструмент мощный, но если накосячить с регистрацией OUT-параметров — получишь SQLException такой, что мало не покажется. Так что, блядь, думай головой, когда вызываешь!