Query Annotation and Named Queries

@Query in Spring Data Repositories

When a derived method name becomes too long (findByCategoryNameAndPriceLessThanEqualAndStockGreaterThanOrderByPriceAsc), write the JPQL yourself with @Query. Spring Data parses and validates it when the application starts, so a typo fails at boot, not in production.

public interface ProductRepository extends JpaRepository<Product, Long> {

    // named parameters
    @Query("select p from Product p where p.category.name = :category and p.price <= :max order by p.price")
    List<Product> findCheapInCategory(@Param("category") String category, @Param("max") BigDecimal max);

    // positional parameters
    @Query("select p from Product p where lower(p.name) like lower(concat('%', ?1, '%'))")
    List<Product> search(String text);

    // collection parameter -> IN clause
    @Query("select p from Product p where p.id in :ids")
    List<Product> findAllByIdIn(@Param("ids") Collection<Long> ids);
}

Update and Delete Queries

@Modifying marks a bulk UPDATE or DELETE. It runs directly in the database, skipping dirty checking and cascades, so it is ideal for "decrement stock only if enough is left" style operations.

@Modifying(clearAutomatically = true)   // clear stale entities from the persistence context afterwards
@Transactional
@Query("""
       update Product p set p.stock = p.stock - :qty
       where p.id = :id and p.stock >= :qty
       """)
int reserveStock(@Param("id") Long id, @Param("qty") int qty);

Atomic stock reservation

int updated = productRepository.reserveStock(7L, 2);
if (updated == 0) throw new OutOfStockException(7L);

Example output

DEBUG org.hibernate.SQL : update products set stock=(stock-?) where id=? and stock>=?
TRACE org.hibernate.orm.jdbc.bind : binding parameter (1:INTEGER) <- [2]
TRACE org.hibernate.orm.jdbc.bind : binding parameter (2:BIGINT) <- [7]
TRACE org.hibernate.orm.jdbc.bind : binding parameter (3:INTEGER) <- [2]

Tip: The condition stock >= :qty inside the UPDATE makes the check and the change one atomic step. Two buyers can never both take the last unit.

Named Queries

A named query is declared once on the entity with a name. Spring Data uses it automatically when a repository method is called EntityName.methodName.

@Entity
@Table(name = "products")
@NamedQuery(name = "Product.findLowStock",
            query = "select p from Product p where p.stock < :limit order by p.stock")
public class Product { ... }

public interface ProductRepository extends JpaRepository<Product, Long> {
    List<Product> findLowStock(@Param("limit") int limit);   // uses Product.findLowStock
}

// or directly:
em.createNamedQuery("Product.findLowStock", Product.class).setParameter("limit", 5).getResultList();

Named queries are checked at startup. Today most teams prefer @Query because the query sits next to the method that uses it.