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.