What is the difference between @Query and derived query methods?

7 minintermediatequery-annotationjpqlnative-query

Quick Answer

Derived query methods generate a query automatically from the method's name, which is concise for simple filters but becomes unreadable for complex conditions and can't express arbitrary joins/aggregations easily. @Query lets you write an explicit JPQL (or native SQL, via nativeQuery = true) query string directly on the repository method, giving full control over the exact query — necessary for anything beyond straightforward property-based filtering, like custom joins, aggregate functions, or database-specific SQL features.

Detailed Answer

Both let a Spring Data repository method run a query, but differ in how explicit the query itself is:

Derived query methods infer the query entirely from the method name:

List<Order> findByStatusAndCreatedAtAfter(String status, Instant after);

Great for simple, property-based filters — but the method name grows unwieldy fast for anything more complex, and can't express things like joins across unrelated entities, aggregate functions, or arbitrary custom logic.

@Query lets you write the query explicitly, as JPQL (Spring Data JPA's default) or, with nativeQuery = true, as raw SQL specific to your database:

public interface OrderRepository extends JpaRepository<Order, Long> {

    @Query("SELECT o FROM Order o JOIN o.customer c WHERE c.region = :region AND o.total > :minTotal")
    List<Order> findHighValueOrdersByRegion(@Param("region") String region, @Param("minTotal") BigDecimal minTotal);

    @Query(value = "SELECT * FROM orders WHERE created_at > NOW() - INTERVAL '7 days'", nativeQuery = true)
    List<Order> findRecentOrders(); // raw SQL, using a Postgres-specific INTERVAL expression
}

When to reach for @Query instead of a derived method name:

  • The query involves joins across multiple entities, aggregate functions (COUNT, SUM, AVG), or GROUP BY/HAVING clauses that a method-name-derived query can't express cleanly.
  • You need database-specific SQL features not portable through JPQL — this requires nativeQuery = true, at the cost of losing JPQL's database-portability and some of Spring Data's automatic result-mapping conveniences.
  • The equivalent method name would be long and hard to read, even if technically expressible via derivation.

Rule of thumb: derived query methods for straightforward, readable property-based lookups; @Query (JPQL first, native SQL only when genuinely necessary) once the query's complexity would make a derived method name unreadable or when SQL-specific features are required.