javaintermediate

Spring Data JPA — Custom Queries

Write custom JPA queries with @Query, derived methods, Specifications, and native SQL for complex lookups.

java
import org.springframework.data.jpa.repository.*;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.Specification;
import java.time.LocalDate;
import java.util.*;

public interface UserRepository extends JpaRepository<User, Long>,
                                        JpaSpecificationExecutor<User> {

    // Derived query methods
    Optional<User> findByEmail(String email);
    List<User> findByNameContainingIgnoreCase(String name);
    List<User> findByCreatedAtAfter(LocalDate date);
    boolean existsByEmail(String email);
    long countByStatus(String status);

    // JPQL query
    @Query("SELECT u FROM User u WHERE u.age BETWEEN :min AND :max ORDER BY u.name")
    List<User> findByAgeRange(int min, int max);

    // JPQL with join fetch (avoid N+1)
    @Query("SELECT u FROM User u LEFT JOIN FETCH u.orders WHERE u.id = :id")
    Optional<User> findByIdWithOrders(Long id);

    // Native SQL
    @Query(value = "SELECT * FROM users WHERE email LIKE %:domain", nativeQuery = true)
    List<User> findByEmailDomain(String domain);

    // Pagination
    Page<User> findByStatus(String status, Pageable pageable);

    // Modifying query
    @Modifying
    @Query("UPDATE User u SET u.status = :status WHERE u.lastLogin < :date")
    int deactivateInactiveUsers(String status, LocalDate date);
}

// Specification for dynamic queries
class UserSpecs {
    static Specification<User> hasName(String name) {
        return (root, query, cb) ->
            name == null ? null : cb.like(cb.lower(root.get("name")), "%" + name.toLowerCase() + "%");
    }

    static Specification<User> hasStatus(String status) {
        return (root, query, cb) ->
            status == null ? null : cb.equal(root.get("status"), status);
    }

    static Specification<User> ageGreaterThan(int age) {
        return (root, query, cb) -> cb.greaterThan(root.get("age"), age);
    }
}

// Usage: repo.findAll(hasName("alice").and(hasStatus("active")), pageable);

Sponsored

Supabase

Use Cases

  • Complex database queries in Spring applications
  • Dynamic filtering with JPA Specifications
  • Avoiding N+1 query problems with fetch joins

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.