JDBC

JDBC — стандартный Java API для работы с реляционной БД через драйвер. Он задаёт интерфейсы, а драйвер конкретной СУБД реализует сетевой протокол и преобразование типов.

Основные объекты

  • DataSource — источник соединений; в приложении обычно связан с пулом;
  • Connection — сессия с БД и граница транзакции;
  • PreparedStatement — подготовленная параметризованная команда;
  • ResultSet — курсор результата;
  • SQLException — ошибка с SQL state и кодом поставщика.
String sql = "select id, name from customer where status = ?";
 
try (Connection connection = dataSource.getConnection();
     PreparedStatement statement = connection.prepareStatement(sql)) {
    statement.setString(1, status);
 
    try (ResultSet rows = statement.executeQuery()) {
        while (rows.next()) {
            long id = rows.getLong("id");
            String name = rows.getString("name");
        }
    }
}

Параметры нельзя применять к именам таблиц, столбцов или ключевым словам. Такие динамические части выбирают из белого списка, а значения передают через ?.

Транзакция

try (Connection connection = dataSource.getConnection()) {
    connection.setAutoCommit(false);
    try {
        // несколько команд на одном connection
        connection.commit();
    } catch (Exception exception) {
        connection.rollback();
        throw exception;
    }
}

Возврат соединения в пул не равен физическому закрытию, но close() обязателен: он освобождает ресурс и позволяет пулу сбросить состояние.

Batch

addBatch и executeBatch уменьшают число сетевых обменов. Эффект зависит от драйвера и его настроек. Большой batch следует делить на части, учитывать размер транзакции и анализировать частичный успех через BatchUpdateException.

Типичные сбои

  • утечка Connection, Statement или ResultSet;
  • SQL injection из-за конкатенации;
  • N+1 на уровне сетевых вызовов;
  • длинная транзакция удерживает соединение и блокировки;
  • неверное чтение NULL: для примитивного getter нужен wasNull() либо объектный тип;
  • пул исчерпан не потому, что «мало соединений», а из-за медленных запросов или утечек.

Вопросы для повторения

  1. Statement и PreparedStatement отличаются только скоростью? Нет, параметры PreparedStatement отделяют данные от SQL и защищают значения от injection.
  2. Почему нужен пул? Создание сетевого соединения дорого; пул повторно использует ограниченное число соединений.
  3. Потокобезопасен ли Connection? На совместное использование между потоками рассчитывать нельзя.
  4. Что делает auto-commit? Фиксирует каждую завершённую команду как отдельную транзакцию.
  5. Когда JDBC лучше ORM? Когда нужен точный SQL, streaming, batch или массовая операция без графа сущностей.

Связано: JPA Hibernate и JDBC сравнение интерфейсов, SQL инъекции, Транзакции принцип ACID.