What is the difference between @Query and derived query methods?
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), orGROUP BY/HAVINGclauses 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.