Spring FrameworkMiddleTechnical

В чём разница между 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» в реальном сервисе.

Sources

Related topics