Ответ
Использование нативного SQL в коде приложения считается антипаттерном по нескольким ключевым причинам:
1. Уязвимости безопасности (SQL-инъекции) Прямая конкатенация строк в запрос создаёт критическую уязвимость.
// Опасный подход
String query = "SELECT * FROM users WHERE login = '" + input + "'";
// Если input = "admin' OR '1'='1", запрос всегда вернёт данные
2. Нарушение абстракции и переносимости
- Запросы привязываются к диалекту конкретной СУБД (PostgreSQL, MySQL, Oracle).
- Миграция на другую СУБД потребует переписывания всех запросов.
- Усложняется модульное тестирование (требуется реальная БД).
3. Сложность поддержки и рефакторинга
- Запросы в виде строк не проверяются компилятором.
- Ошибки обнаруживаются только в runtime.
- Затруднён поиск использования запросов в IDE.
- Нет type-safety для параметров и результатов.
4. Потеря оптимизаций ORM (Hibernate/JPA) ORM-фреймворки предоставляют:
- Кэширование (первого и второго уровня).
- Ленивую загрузку связей (
FetchType.LAZY). - Пагинацию на уровне API (
Pageable). - Трансформацию результатов в типизированные объекты или DTO.
Рекомендуемые альтернативы:
- JPA Repository / HQL:
@Repository public interface UserRepository extends JpaRepository<User, Long> { @Query("SELECT u FROM User u WHERE u.email = :email") User findByEmail(@Param("email") String email); } - JdbcTemplate с NamedParameterJdbcTemplate:
String sql = "SELECT * FROM users WHERE region = :region"; MapSqlParameterSource params = new MapSqlParameterSource(); params.addValue("region", regionName); return jdbcTemplate.query(sql, params, new UserRowMapper()); - QueryDSL / JOOQ: Типобезопасные конструкторы запросов.
Исключение: Сложные аналитические запросы или массовые операции (UPDATE/DELETE) иногда пишутся нативным SQL, но должны быть изолированы и использовать только параметризованные запросы.