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

Что такое Spring Data JDBC?

2.0 Middle🔥 121 комментариев
#Spring Boot и Spring Data#Spring Framework

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

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

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

Spring Data JDBC

Spring Data JDBC — это модуль Spring Data, предоставляющий лёгкий и минималистичный доступ к реляционным базам данных через JDBC. Это альтернатива JPA/Hibernate, когда нужна простота и полный контроль над SQL запросами.

Основная идея

Spring Data JDBC следует принципу DDD (Domain-Driven Design) и предоставляет:

  • Repository паттерн для абстракции доступа к БД
  • Минимум магии — нет прокси-объектов и ленивой загрузки
  • Прямое управление SQL — вы пишете запросы
  • Слабая связанность — простые POJO без JPA аннотаций

Зависимости

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

Основные компоненты

1. Сущность (Entity)

Простой POJO класс с аннотациями Spring Data JDBC:

import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
import org.springframework.data.relational.core.mapping.Column;

@Table("users")
public class User {
    
    @Id
    private Long id;              // Автоматически генерируется БД
    
    @Column("name")
    private String name;
    
    @Column("email")
    private String email;
    
    @Column("created_at")
    private LocalDateTime createdAt;
    
    // Конструкторы, getter, setter
    public User() {}
    
    public User(String name, String email) {
        this.name = name;
        this.email = email;
        this.createdAt = LocalDateTime.now();
    }
    
    // Getters and Setters
    public Long getId() { return id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    // ...
}

2. Repository интерфейс

Расширяет CrudRepository или PagingAndSortingRepository:

import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;

@Repository
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
    
    // Метод-запрос (spring-data-jdbc конвертирует в SQL)
    Optional<User> findByEmail(String email);
    
    // Кастомный SQL запрос
    @Query("SELECT * FROM users WHERE name LIKE :name")
    List<User> searchByName(String name);
    
    // Удаление по условию
    void deleteByEmail(String email);
    
    // Подсчёт записей
    long countByNameContaining(String namePattern);
}

Использование Repository

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    // Создание нового пользователя
    public User createUser(String name, String email) {
        User user = new User(name, email);
        return userRepository.save(user); // INSERT
    }
    
    // Получение по ID
    public User getUserById(Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new IllegalArgumentException("User not found"));
    }
    
    // Поиск по email
    public User findByEmail(String email) {
        return userRepository.findByEmail(email)
            .orElseThrow(() -> new IllegalArgumentException("User not found"));
    }
    
    // Поиск по имени
    public List<User> searchUsers(String name) {
        return userRepository.searchByName("%" + name + "%");
    }
    
    // Обновление
    public User updateUser(Long id, String newName) {
        User user = getUserById(id);
        user.setName(newName);
        return userRepository.save(user); // UPDATE
    }
    
    // Удаление
    public void deleteUser(Long id) {
        userRepository.deleteById(id); // DELETE
    }
    
    // Получение всех пользователей с пагинацией
    public Page<User> getAllUsers(int page, int size) {
        return userRepository.findAll(PageRequest.of(page, size));
    }
}

Методы Query по умолчанию

Spring Data JDBC генерирует SQL на основе имена метода:

// SELECT * FROM users WHERE email = ?
Optional<User> findByEmail(String email);

// SELECT * FROM users WHERE name = ? AND email = ?
Optional<User> findByNameAndEmail(String name, String email);

// SELECT * FROM users WHERE age > ?
List<User> findByAgeGreaterThan(int age);

// SELECT * FROM users WHERE name LIKE ?
List<User> findByNameContaining(String name);

// SELECT COUNT(*) FROM users WHERE active = true
long countByActive(boolean active);

// SELECT * FROM users WHERE active = true ORDER BY created_at DESC
List<User> findByActiveOrderByCreatedAtDesc(boolean active);

Кастомные SQL запросы

@Repository
public interface ProductRepository extends CrudRepository<Product, Long> {
    
    // Явный SELECT запрос
    @Query("SELECT * FROM products WHERE category = :category AND price > :minPrice")
    List<Product> findExpensiveByCategory(
        @Param("category") String category,
        @Param("minPrice") BigDecimal minPrice
    );
    
    // INSERT/UPDATE/DELETE с @Modifying
    @Modifying
    @Query("UPDATE products SET quantity = quantity - 1 WHERE id = :id")
    void decreaseQuantity(@Param("id") Long id);
    
    // Удаление
    @Modifying
    @Query("DELETE FROM products WHERE price < :minPrice")
    int deleteByMinPrice(@Param("minPrice") BigDecimal minPrice);
}

Связи между сущностями

Spring Data JDBC не поддерживает ленивую загрузку и требует явного управления связями:

@Table("orders")
public class Order {
    @Id
    private Long id;
    private String orderNumber;
    
    // Агрегированная ссылка на User (не связь)
    private Long userId;  // Вручную управляем ID
    
    // Для загрузки полного User нужен свой запрос
    public void loadUserData(UserRepository userRepository) {
        // Явная загрузка
    }
}

Транзакции

@Service
public class TransactionService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Transactional  // Все операции в одной транзакции
    public void processOrder(Long userId, Order order) {
        // Проверяем пользователя
        User user = userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException("User not found"));
        
        // Сохраняем заказ
        order.setUserId(user.getId());
        orderRepository.save(order);
        
        // Если возникнет исключение — откатит обе операции
    }
}

Spring Data JDBC vs JPA

ХарактеристикаSpring Data JDBCJPA/Hibernate
СложностьПростаяСложная
ПроизводительностьЛучше (нет магии)Может быть медленнее
Управление запросамиЯвноеНеявное (генерируется)
Ленивая загрузкаНетДа (проблемы N+1)
Кривая обученияНизкаяВысокая
ГибкостьОграниченнаяПолная
СвязиРучное управлениеАвтоматическое

Когда использовать

Используй Spring Data JDBC когда:

  • Нужна максимальная простота и понятность
  • Работаешь с относительно простой схемой БД
  • Критична производительность
  • Предпочитаешь явное управление SQL
  • Проект маленький или средний

Используй JPA когда:

  • Сложная архитектура с множеством связей
  • Нужна кроссплатформенность (разные БД)
  • Важна гибкость и функционал ORM
  • Большой enterprise проект

Spring Data JDBC — отличный выбор для разработчиков, которые хотят контролировать SQL без сложности полнофункционального ORM.