Ответ
Statement и PreparedStatement — это интерфейсы JDBC для выполнения SQL-запросов, но с ключевыми различиями в безопасности и производительности.
Statement
- Выполняет статические SQL-запросы, переданные в виде строки.
- Уязвим к SQL-инъекциям, так как параметры конкатенируются напрямую в строку запроса.
- Менее эффективен при многократном выполнении одного и того же запроса с разными параметрами, так как каждый раз запрос компилируется заново.
PreparedStatement
- Использует параметризованные запросы с плейсхолдерами (
?). - Защищает от SQL-инъекций, так как параметры передаются отдельно и экранируются драйвером БД.
- Повышает производительность при повторных вызовах: запрос компилируется один раз, а план выполнения может кэшироваться СУБД.
Пример использования:
// Statement (небезопасно и неэффективно)
String userInput = "123 OR 1=1";
Statement stmt = connection.createStatement();
String sql = "DELETE FROM users WHERE id = " + userInput; // Опасная конкатенация!
stmt.executeUpdate(sql); // Может удалить всех пользователей
// PreparedStatement (безопасно и эффективно)
int userId = 123;
String sql = "DELETE FROM users WHERE id = ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setInt(1, userId); // Параметр безопасно подставляется
pstmt.executeUpdate();
В тестировании важно проверять, что в коде приложения для всех операций с пользовательским вводом используется PreparedStatement, чтобы предотвратить уязвимости.
Ответ 18+ 🔞
Давай разжую эту тему, как будто объясняю на пальцах после третьей кружки кофе. Представь, что твой SQL-запрос — это дверь в твою базу данных.
Statement — это как если бы ты взял и прибил эту дверь наглухо, оставив только форточку. А в форточку ты пропихиваешь записку с командой. Любой проходящий мимо мудак может написать на этой записке что угодно. Например: «Удали всех пользователей». И система, такая доверчивая, выполнит. Это и есть SQL-инъекция — хуй с горы, а не защита. Каждый раз, когда ты шлёшь новую записку, база данных с нуля её читает и думает: «А что это тут такое?». Производительность — пиздец, терпения ебать ноль.
PreparedStatement — это уже нормальная дверь с кодовым замком. Ты заранее говоришь базе: «Слушай, будет запрос вида "удали пользователя с ID = ?"». База его компилирует, запоминает план и ждёт. А когда нужно выполнить, ты просто передаёшь цифру в этот плейсхолдер (?). База уже знает, что на этом месте должна быть только цифра, а не какая-нибудь манда с ушами вроде 123 OR 1=1. Она эту цифру безопасно подставляет. И если тебе нужно удалить десять пользователей подряд, запрос не компилируется десять раз — он один раз подготовлен и просто жрёт новые параметры. Овердохуища быстрее и безопаснее.
Смотри на примере, тут всё видно:
// Statement (это просто пиздец, как так можно)
String userInput = "123 OR 1=1"; // Вот этот пидарас шерстяной хочет всё сломать
Statement stmt = connection.createStatement();
String sql = "DELETE FROM users WHERE id = " + userInput; // Конкатенация строк — привет, инъекция!
stmt.executeUpdate(sql); // База выполнит это как "удали ВСЕХ, у кого id=123 ИЛИ 1=1". Всех пользователей накрыли медным тазом.
// PreparedStatement (вот так надо, по-человечески)
int userId = 123; // Нормальный, ожидаемый параметр
String sql = "DELETE FROM users WHERE id = ?"; // Вопрос — наш плейсхолдер
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setInt(1, userId); // Безопасно подставляем цифру на место первого знака вопроса
pstmt.executeUpdate(); // Выполнится ТОЛЬКО "удали пользователя с id=123"
Так что когда тестируешь или код пишешь, смотри в оба. Если видишь, что строки склеиваются плюсиками и туда суётся что попало с фронта — это красная тряпка. Доверия ебать ноль такому коду. Надо везде пихать PreparedStatement. Иначе будет вам не хиросима, а полноценный нигерсраки в продакшене.