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

Как устроена технология Spring Data?

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

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

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

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

Как устроена технология Spring Data

Spring Data — это проект Spring, который упрощает работу с данными (базами данных, кешами, поисковыми системами) через единый API. Основная идея: минимум кода для доступа к данным.

Архитектура Spring Data

┌─────────────────────────────────────┐
│   Spring Data Modules               │
├─────────────────────────────────────┤
│ Spring Data JPA                     │
│ Spring Data MongoDB                 │
│ Spring Data Redis                   │
│ Spring Data Elasticsearch           │
│ Spring Data REST                    │
└──────────────┬──────────────────────┘
               │
┌──────────────▼──────────────────────┐
│ Spring Data Commons                 │
│ (Repository, CrudRepository, etc)   │
└──────────────┬──────────────────────┘
               │
┌──────────────▼──────────────────────┐
│ ORM / Database Drivers              │
│ (Hibernate, MongoDB driver, Redis)  │
└──────────────┬──────────────────────┘
               │
┌──────────────▼──────────────────────┐
│ Underlying Data Store               │
│ (PostgreSQL, MySQL, MongoDB, etc)   │
└─────────────────────────────────────┘

1. Repository Pattern

Основное понятие — Repository интерфейс:

// Создаёшь интерфейс, Spring генерирует реализацию
public interface UserRepository extends JpaRepository<User, Long> {
    // CRUD операции уже встроены (save, findById, delete, etc)
    
    // Кастомные методы с query generation
    List<User> findByEmail(String email);
    List<User> findByAgeGreaterThan(int age);
    List<User> findByFirstNameAndLastName(String firstName, String lastName);
}

// Использование:
@Service
public class UserService {
    @Autowired
    private UserRepository repository;
    
    public void saveUser(User user) {
        repository.save(user); // INSERT или UPDATE
    }
    
    public User getUser(Long id) {
        return repository.findById(id).orElse(null); // SELECT
    }
    
    public List<User> searchByEmail(String email) {
        return repository.findByEmail(email); // Query generation!
    }
}

2. Query Generation

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

public interface ProductRepository extends JpaRepository<Product, Long> {
    // Метод:                        SQL:
    findByName(String name)        → SELECT * FROM product WHERE name = ?
    findByPriceGreaterThan(double) → SELECT * FROM product WHERE price > ?
    findByPriceBetween(d, d)       → SELECT * FROM product WHERE price BETWEEN ? AND ?
    findByNameAndCategory(s, s)    → SELECT * FROM product WHERE name = ? AND category = ?
    findByNameOrEmail(s, s)        → SELECT * FROM product WHERE name = ? OR email = ?
    findByNameStartingWith(s)      → SELECT * FROM product WHERE name LIKE ?
    findByNameContaining(s)        → SELECT * FROM product WHERE name LIKE %?%
    findByNameIgnoreCase(s)        → SELECT * FROM product WHERE LOWER(name) = LOWER(?)
    findByNameOrderByPrice(s)      → SELECT * FROM product WHERE name = ? ORDER BY price
    findTop10ByPrice()             → SELECT * FROM product ORDER BY price LIMIT 10
}

3. Иерархия Repository интерфейсов

Repository (marker interface)
    ├── CrudRepository
    │   ├── PagingAndSortingRepository
    │   │   └── JpaRepository (самый полный, рекомендуется)
    │   └── ReactiveCrudRepository
    │
    └── ListCrudRepository (возвращает List вместо Iterable)

Что даёт каждый:

// Repository — пустой интерфейс, только маркер
public interface Repository<T, ID> {}

// CrudRepository — CRUD операции
public interface CrudRepository<T, ID> extends Repository<T, ID> {
    <S extends T> S save(S entity);
    Optional<T> findById(ID id);
    Iterable<T> findAll();
    long count();
    void deleteById(ID id);
    // ...
}

// PagingAndSortingRepository — добавляет пагинацию и сортировку
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
    Page<T> findAll(Pageable pageable);
    Iterable<T> findAll(Sort sort);
}

// JpaRepository — добавляет batch операции и flush
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID> {
    <S extends T> List<S> saveAll(Iterable<S> entities);
    void flush();
    <S extends T> S saveAndFlush(S entity);
    void deleteInBatch(Iterable<T> entities);
    List<T> getById(Iterable<ID> ids);
}

4. CRUD Example

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    // CREATE (INSERT)
    public User createUser(String name, String email) {
        User user = new User(name, email);
        return userRepository.save(user); // INSERT
    }
    
    // READ (SELECT)
    public User getUserById(Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new RuntimeException("User not found"));
    }
    
    // UPDATE
    public User updateUser(Long id, String email) {
        User user = getUserById(id);
        user.setEmail(email);
        return userRepository.save(user); // UPDATE (same method as INSERT)
    }
    
    // DELETE
    public void deleteUser(Long id) {
        userRepository.deleteById(id); // DELETE
    }
    
    // LIST ALL
    public List<User> getAllUsers() {
        return userRepository.findAll(); // SELECT * FROM user
    }
}

5. Пагинация и сортировка

public interface UserRepository extends JpaRepository<User, Long> {
    // Встроенная поддержка пагинации
}

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserRepository repository;
    
    @GetMapping
    public Page<User> getUsers(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size,
        @RequestParam(defaultValue = "id") String sortBy
    ) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy));
        return repository.findAll(pageable);
        // SELECT * FROM user LIMIT 10 OFFSET 0 ORDER BY id
    }
}

6. Custom Queries с @Query

public interface UserRepository extends JpaRepository<User, Long> {
    // Кастомный JPQL запрос
    @Query("SELECT u FROM User u WHERE u.email = ?1")
    Optional<User> findUserByEmail(String email);
    
    // Native SQL запрос
    @Query(value = "SELECT * FROM user WHERE age > ?", nativeQuery = true)
    List<User> findAdults(int age);
    
    // С named параметрами (лучше для читаемости)
    @Query("SELECT u FROM User u WHERE u.firstName = :firstName AND u.lastName = :lastName")
    List<User> findByName(@Param("firstName") String first, @Param("lastName") String last);
    
    // С обновлением данных
    @Modifying
    @Transactional
    @Query("UPDATE User u SET u.active = false WHERE u.age > 65")
    void deactivateOldUsers();
}

7. Associations (Relationships)

// One-to-Many
@Entity
public class Author {
    @Id
    @GeneratedValue
    private Long id;
    
    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL)
    private List<Book> books;
}

@Entity
public class Book {
    @Id
    @GeneratedValue
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "author_id")
    private Author author;
}

// Repository для Author будет получать связанные Book-и
public interface AuthorRepository extends JpaRepository<Author, Long> {
    // Spring Data обработает joins
    List<Author> findByBooksTitle(String bookTitle);
    // SELECT a FROM Author a WHERE EXISTS (SELECT 1 FROM Book b WHERE b.author = a AND b.title = ?)
}

8. Specifications (Advanced Filtering)

// Для сложных динамических фильтров
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}

@Service
public class UserService {
    @Autowired
    private UserRepository repository;
    
    public List<User> searchUsers(String firstName, Integer ageFrom, Integer ageTo) {
        Specification<User> spec = Specification
            .where((root, query, cb) -> {
                List<Predicate> predicates = new ArrayList<>();
                
                if (firstName != null) {
                    predicates.add(cb.equal(root.get("firstName"), firstName));
                }
                if (ageFrom != null) {
                    predicates.add(cb.greaterThanOrEqualTo(root.get("age"), ageFrom));
                }
                if (ageTo != null) {
                    predicates.add(cb.lessThanOrEqualTo(root.get("age"), ageTo));
                }
                
                return cb.and(predicates.toArray(new Predicate[0]));
            });
        
        return repository.findAll(spec);
    }
}

9. Spring Data в других системах

Spring Data MongoDB:

public interface ProductRepository extends MongoRepository<Product, String> {
    List<Product> findByCategory(String category);
}
// Генерирует MongoDB запрос db.product.find({category: "electronics"})

Spring Data Redis:

public interface UserCacheRepository extends CrudRepository<User, String> {
    List<User> findByAge(int age);
}
// Работает с Redis, автоматически сериализует объекты

10. Производительность и Best Practices

Проблема 1: N+1 queries

// ❌ Плохо: 1 запрос + N запросов на связанные данные
List<Author> authors = repository.findAll();
for (Author author : authors) {
    System.out.println(author.getBooks()); // ← Отдельный запрос для каждого!
}

// ✅ Хорошо: JOIN в одном запросе
@Query("SELECT DISTINCT a FROM Author a LEFT JOIN FETCH a.books")
List<Author> findAllWithBooks();

Проблема 2: Слишком большие результаты

// ❌ Плохо: получаем все данные
List<User> users = repository.findAll();

// ✅ Хорошо: используем пагинацию
Page<User> users = repository.findAll(PageRequest.of(0, 20));

Проблема 3: Ненужные поля

// ✅ Хорошо: получаем только нужные поля
@Query("SELECT new UserDTO(u.id, u.name) FROM User u")
List<UserDTO> findAllUsers();

Вывод

Spring Data абстрагирует работу с базами данных и позволяет фокусироваться на бизнес-логике. Основные преимущества:

  • Минимум boilerplate кода
  • Query generation из имён методов
  • Встроенная пагинация, сортировка, фильтрация
  • Поддержка множества БД с одним API
  • Хороший баланс между удобством и контролем
Как устроена технология Spring Data? | PrepBro