Home/Learn/Hibernate & JPA/@Query Annotation

@Query Annotation

Intermediate
Spring Data JPA

@Query overrides the derived query with explicit JPQL or native SQL; use @Modifying with @Transactional for bulk UPDATE or DELETE repository methods.

Overview

@Query lets you write explicit JPQL or native SQL on a Spring Data repository method, bypassing method name derivation. It is the preferred choice when the query is too complex for a readable method name. Named parameters (:param) are bound via @Param. For INSERT/UPDATE/DELETE bulk operations, combine @Query with @Modifying and @Transactional — without @Modifying, Spring Data raises an exception; without @Transactional, the modifying query runs outside a transaction. @Modifying's clearAutomatically=true clears the persistence context after the bulk update to prevent stale entities.

JPQL @Query

Use JPQL @Query for portable queries that benefit from entity-level aliases and relationship traversal without writing table-specific SQL.

Java — @Query with JPQL and projections
public interface OrderRepository extends JpaRepository<Order, Long> {

    // JPQL — entity alias, relationship traversal
    @Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.customerId = :cid AND o.status = :status")
    List<Order> findWithItemsByCustomer(@Param("cid") Long customerId,
                                        @Param("status") String status);

    // Projection with JPQL constructor expression
    @Query("SELECT new com.example.dto.OrderSummary(o.id, o.status, o.totalAmount) " +
           "FROM Order o WHERE o.createdAt >= :since")
    List<OrderSummary> findSummariesSince(@Param("since") LocalDateTime since);

    // Positional parameters (less readable but shorter)
    @Query("SELECT o FROM Order o WHERE o.status = ?1 ORDER BY o.createdAt DESC")
    List<Order> findByStatus(String status);
}

@Modifying — Bulk UPDATE and DELETE

@Modifying marks the @Query as a DML statement (INSERT/UPDATE/DELETE). @Transactional is required — add it to the repository method or ensure a transaction is provided by the caller.

Java — @Modifying bulk UPDATE and DELETE
public interface OrderRepository extends JpaRepository<Order, Long> {

    // Bulk UPDATE — requires @Modifying + @Transactional
    @Modifying(clearAutomatically = true)   // clear L1 cache after bulk update
    @Transactional
    @Query("UPDATE Order o SET o.status = :newStatus WHERE o.status = :oldStatus AND o.createdAt < :cutoff")
    int bulkUpdateStatus(@Param("newStatus") String newStatus,
                         @Param("oldStatus") String oldStatus,
                         @Param("cutoff") LocalDateTime cutoff);

    // Bulk DELETE
    @Modifying
    @Transactional
    @Query("DELETE FROM Order o WHERE o.status = 'CANCELLED' AND o.createdAt < :cutoff")
    int deleteOldCancelledOrders(@Param("cutoff") LocalDateTime cutoff);
}

flushAutomatically and clearAutomatically

clearAutomatically=true clears the first-level cache after the bulk DML so subsequent queries see the updated DB state. flushAutomatically=true flushes pending entity changes before the DML executes.

Java — flushAutomatically and clearAutomatically explained
@Modifying(
    flushAutomatically = true,    // flush dirty entities BEFORE bulk update
    clearAutomatically = true     // clear L1 cache AFTER bulk update
)
@Transactional
@Query("UPDATE Order o SET o.priority = :p WHERE o.customerId = :cid")
int setPriority(@Param("p") int priority, @Param("cid") Long customerId);

// Without clearAutomatically=true:
// 1. Bulk UPDATE runs → DB rows updated
// 2. em.find(Order, id) returns stale entity from L1 cache — BUG
// With clearAutomatically=true:
// 2. L1 cache cleared → next em.find() hits DB → fresh data

Key Points to Remember

  • 1@Query accepts JPQL (default) or SQL (nativeQuery=true) inline on the repository method
  • 2Use @Param to bind named parameters; positional ?1 also works but is less readable
  • 3@Modifying is required for UPDATE/DELETE @Query methods — omitting it throws an exception
  • 4@Transactional must be present on the method or the calling service for @Modifying queries
  • 5clearAutomatically=true clears the L1 cache after bulk DML — prevents reading stale entities
  • 6JPQL constructor expression: SELECT new com.example.Dto(a, b) FROM Entity — maps to DTO constructor

Interview Questions

Sign in to ask Aria
1

What happens if you add @Modifying without @Transactional?

MediumTCS
2

Why is clearAutomatically=true important after a bulk update?

HardAmazon
3

What is the JPQL constructor expression and when do you use it?

MediumInfosys
4

What is the difference between @Query JPQL and nativeQuery=true?

EasyWipro
5

How does flushAutomatically differ from clearAutomatically?

HardOracle

Ask Aria about @Query Annotation

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…