← Назад к вопросам

Умеешь ли делать подготовленные SQL запросы

2.0 Middle🔥 141 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Моя экспертиза в подготовленных SQL-запросах (Prepared Statements)

Да, я отлично умею работать с подготовленными SQL-запросами (Prepared Statements) и считаю их одним из фундаментальных навыков для QA Automation Engineer, особенно при тестировании backend-систем, API и работе с базами данных. Подготовленные запросы — это не просто синтаксис, а критически важная практика для безопасности, производительности и стабильности автоматизированных тестов и проверяемого приложения.

Зачем это нужно в автоматизированном тестировании?

В контексте QA Automation подготовленные запросы используются для:

  • Предотвращения SQL-инъекций в тестах: Даже в тестовых сценариях мы должны моделировать безопасное поведение. Если тест "зашивает" данные прямо в строку запроса, это плохой пример и потенциальная уязвимость в самом тестовом фреймворке.
  • Чистоты и поддерживаемости тестового кода: Отделение логики запроса от данных улучшает читаемость.
  • Повторного использования: Один подготовленный запрос можно многократно выполнять с разными наборами данных, что идеально для data-driven тестирования.
  • Тестирования производительности (Performance Testing): При нагрузочном тестировании подготовленные запросы значительно эффективнее, так как план запроса компилируется СУБД один раз и переиспользуется, снижая нагрузку на базу.
  • Валидации бизнес-логики: Часто необходимо напрямую проверять состояние данных в БД после выполнения действий через UI или API. Подготовленные запросы делают такие проверки надежными.

Практические примеры на Java (JDBC)

Вот как это выглядит на практике. Сравним НЕПРАВИЛЬНЫЙ (уязвимый) подход и правильный с использованием PreparedStatement.

❌ Уязвимый запрос (конкатенация строк):

public User getUserByCredentials(String username, String password) throws SQLException {
    Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
    // ВАЖНО: ЭТО ОПАСНО! Параметры напрямую вшиваются в запрос.
    String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery(sql);
    // ... обработка результата
}

Если в username передать admin' --, запрос игнорирует проверку пароля. Это SQL-инъекция.

✅ Безопасный запрос с PreparedStatement:

public User getSecureUserByCredentials(String username, String password) throws SQLException {
    String sql = "SELECT id, email FROM users WHERE username = ? AND password = ?";
    // Плейсхолдеры '?' вместо прямых значений

    try (Connection conn = dataSource.getConnection();
         PreparedStatement pstmt = conn.prepareStatement(sql)) { // Запрос ПОДГОТАВЛИВАЕТСЯ здесь

        // Установка параметров с проверкой типов
        pstmt.setString(1, username); // Параметр 1 (первый '?')
        pstmt.setString(2, password); // Параметр 2 (второй '?')

        try (ResultSet rs = pstmt.executeQuery()) { // Выполнение уже с "подставленными" безопасными параметрами
            if (rs.next()) {
                return new User(rs.getInt("id"), rs.getString("email"));
            }
            return null;
        }
    } // try-with-resources автоматически закроет Connection, PreparedStatement и ResultSet
}

Ключевые преимущества этого подхода:

  1. Безопасность: СУБД сама корректно экранирует переданные в setString() или setInt() значения, делая инъекцию невозможной.
  2. Читаемость: Запрос четко отделен от данных.
  3. Производительность: В сложных запросах СУБД кэширует план выполнения подготовленного запроса.

Использование в современных фреймворках (Spring JdbcTemplate, JPA/Hibernate)

В реальных проектах мы редко пишем "голый" JDBC. Мы используем ORM или шаблоны доступа к данным.

  • В Spring JdbcTemplate:

    String sql = "UPDATE products SET price = ? WHERE id = ?";
    jdbcTemplate.update(sql, newPrice, productId); // Spring сам создает PreparedStatement
    
  • В JPA (например, Hibernate) через EntityManager или Spring Data JPA:

    // Использование именованных параметров
    String jpql = "SELECT c FROM Customer c WHERE c.region = :region AND c.active = :active";
    TypedQuery<Customer> query = entityManager.createQuery(jpql, Customer.class)
            .setParameter("region", regionName)
            .setParameter("active", true);
    List<Customer> result = query.getResultList();
    
    Hibernate всегда трансформирует HQL/JPQL-запросы в подготовленные SQL-запросы.

Сценарии применения в тестировании для QA Automation

  1. Настройка тестовых данных (Test Data Setup):
    @BeforeEach
    void setUpTestData() {
        String insertSql = "INSERT INTO orders (user_id, total, status) VALUES (?, ?, 'PENDING')";
        try (PreparedStatement pstmt = connection.prepareStatement(insertSql)) {
            pstmt.setInt(1, testUserId);
            pstmt.setBigDecimal(2, new BigDecimal("99.99"));
            pstmt.executeUpdate();
        }
    }
    
  2. Верификация данных после тестового действия (Assertions):
    public void verifyOrderStatusAfterPayment(int orderId, String expectedStatus) {
        String selectSql = "SELECT status FROM orders WHERE id = ?";
        try (PreparedStatement pstmt = connection.prepareStatement(selectSql)) {
            pstmt.setInt(1, orderId);
            ResultSet rs = pstmt.executeQuery();
            rs.next();
            String actualStatus = rs.getString("status");
            assertEquals(expectedStatus, actualStatus, "Order status mismatch after payment");
        }
    }
    
  3. Очистка данных после теста (Teardown):
    @AfterEach
    void cleanUpTestData() {
        String deleteSql = "DELETE FROM test_logs WHERE created_at < ?";
        // Удаляем старые тестовые логи
    }
    

Вывод: Умение работать с подготовленными запросами — обязательный навык. Это вопрос не только умения написать PreparedStatement, но и глубокого понимания безопасности приложений, эффективной работы с БД и написания чистого, надежного тестового кода. Я постоянно применяю этот подход как для прямых проверок БД в интеграционных тестах, так и для анализа, что именно "летит" в базу на уровне логов или трассировки SQL-запросов.