В чём разница между JdbcTemplate Spring и Spring Data JPA?
JdbcTemplate — тонкая обёртка над JDBC с ручным SQL и маппингом; Spring Data JPA генерирует запросы из имён методов поверх Hibernate. JdbcTemplate выбирают для сложных/отчётных запросов и batch, Spring Data JPA — для стандартного CRUD со связями.
JdbcTemplate и Spring Data JPA: разные уровни абстракции
JdbcTemplate — тонкая обёртка над JDBC, которая убирает boilerplate (открытие соединения, обработка исключений, закрытие ресурсов), но оставляет разработчику полный контроль над SQL. Spring Data JPA работает поверх JPA-провайдера (обычно Hibernate), скрывает SQL полностью и генерирует запросы из имён методов или JPQL/HQL.
JdbcTemplate
Подключается через зависимость spring-jdbc. Требует ручного написания SQL и маппинга результатов:
@Repository
public class ProductJdbcRepository {
private final JdbcTemplate jdbc;
public ProductJdbcRepository(JdbcTemplate jdbc) {
this.jdbc = jdbc;
}
public List<Product> findByCategory(String category) {
String sql = """
SELECT id, name, price, category
FROM products
WHERE category = ?
ORDER BY price DESC
""";
return jdbc.query(sql,
(rs, rowNum) -> new Product(
rs.getLong("id"),
rs.getString("name"),
rs.getBigDecimal("price"),
rs.getString("category")
),
category
);
}
public int save(Product p) {
return jdbc.update(
"INSERT INTO products (name, price, category) VALUES (?, ?, ?)",
p.getName(), p.getPrice(), p.getCategory()
);
}
}
Spring Data JPA
Подключается через spring-boot-starter-data-jpa. Требует JPA-сущности и интерфейс репозитория:
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private BigDecimal price;
private String category;
// getters, setters
}
public interface ProductRepository extends JpaRepository<Product, Long> {
// Генерируется автоматически из имени метода:
List<Product> findByCategoryOrderByPriceDesc(String category);
// JPQL-запрос:
@Query("SELECT p FROM Product p WHERE p.price < :maxPrice")
List<Product> findCheaperThan(@Param("maxPrice") BigDecimal maxPrice);
// Нативный SQL:
@Query(value = "SELECT * FROM products WHERE category = :cat", nativeQuery = true)
List<Product> findByCategoryNative(@Param("cat") String category);
}
Когда что выбирать
- JdbcTemplate: сложные запросы с агрегацией, оконными функциями, CTE, хранимыми процедурами; высоконагруженные batch-операции; легаси-схемы, плохо ложащиеся на ORM; работа с несколькими схемами одновременно.
- Spring Data JPA: CRUD-операции над чётко определёнными сущностями; нужна пагинация и сортировка из коробки (
Pageable); аудит (@CreatedDate,@LastModifiedBy); проекции и DTO через интерфейсы. - Оба вместе: в одном проекте допустимо использовать
JpaRepositoryдля стандартных операций иJdbcTemplateдля отчётных запросов.
Производительность
Spring Data JPA через Hibernate использует кэш первого уровня (EntityManager) и может делать N+1 запросы при навигации по связям. JdbcTemplate не кэширует ничего — каждый вызов идёт в базу напрямую, что делает поведение предсказуемым.
Подводные камни
- N+1 в Spring Data JPA: загрузка коллекций LAZY внутри цикла приводит к N+1 запросам. Исправляется через
@EntityGraph, JOIN FETCH или batch-fetching. - OpenEntityManagerInView: по умолчанию включён в Spring Boot и держит сессию Hibernate открытой до конца HTTP-ответа. Это маскирует N+1 и вызывает проблемы в production — лучше отключить (
spring.jpa.open-in-view=false). - JdbcTemplate и SQL-инъекции: параметры передавайте только через
?или именованные параметры вNamedParameterJdbcTemplate, никогда через конкатенацию строк. - Транзакции при смешивании:
JdbcTemplateучаствует в Spring-транзакции, если использует тот жеDataSource. Но Hibernate и JDBC-соединение используют разные контексты — не смешивайте их в одной транзакции без явного flush. - Генерация схемы:
spring.jpa.hibernate.ddl-auto=updateопасен в production — Hibernate может добавить колонку, но никогда не удалит. Используйте Flyway или Liquibase. - Именованные методы в Spring Data: длинные имена вида
findByFirstNameAndLastNameAndStatusOrderByCreatedAtDescнечитаемы. Предпочтительнее@Queryс JPQL. - Batch в Spring Data JPA: saveAll не даёт реального batch без
spring.jpa.properties.hibernate.jdbc.batch_size=50и@GeneratedValue(SEQUENCE)— IDENTITY-стратегия отключает batching Hibernate.
Common mistakes
- Путать термин «jdbctemplate vs spring data jpa» с соседним механизмом Spring Framework.
- Не называть границу lifecycle, transaction, thread или request для «jdbctemplate vs spring data jpa».
- Игнорировать production-эффекты «jdbctemplate vs spring data jpa»: latency, SQL shape, memory, security или observability.
What the interviewer is testing
- Попросить объяснить механизм «jdbctemplate vs spring data jpa» на минимальном примере.
- Проверить, видит ли кандидат failure mode и диагностику для «jdbctemplate vs spring data jpa».
- Уточнить, какие настройки или API меняют «jdbctemplate vs spring data jpa» в реальном сервисе.