Mastering the @Query Annotation in Java Spring: A Complete Guide for Beginners
Student
"I am starting to learn Java programming basics and building database queries with Spring Data JPA. When should I use the @Query annotation instead of standard method naming?"
Teacher
"The @Query annotation allows you to define custom JPQL or native SQL queries directly on your repository methods, giving you full control over complex data retrieval."
Student
"That sounds very powerful! How do we write and execute these custom queries?"
Teacher
"Let us explore the step-by-step usage of the query annotation in your Spring applications!"
1. Introduction to the Query Annotation in Spring Data JPA
Key Takeaway: Let's dive into the core concepts of "1. Introduction to the Query Annotation in Spring Data JPA" cleanly and effectively.
When working with Java beginners tutorials for Spring Boot, method name derivation is often the first approach to finding records. However, as applications grow, method names can become long and difficult to read. The @Query annotation solves this by letting developers write custom Jakarta Persistence Query Language (JPQL) or native SQL directly inside repository interfaces. It bridges the gap between Java programming basics and advanced database interaction.
For Java tutorial for beginners, think of @Query as writing a custom SQL command tailored specifically to fetch exact datasets without relying on automated naming rules. It provides maximum flexibility when optimizing performance or performing complex joins across multiple relational tables.
2. Writing Basic JPQL Queries with Query
JPQL operates on entity objects rather than raw database tables. Let us look at a simple repository example that uses the @Query annotation to select user records by email domain.
package com.example.demo.repository;
import com.example.demo.model.UserProfile;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface UserProfileRepository extends JpaRepository<UserProfile, Long> {
@Query("SELECT u FROM UserProfile u WHERE u.email LIKE %:domain")
List<UserProfile> findUsersByEmailDomain(@Param("domain") String domain);
}
In this code sample, the JPQL statement references the entity class UserProfile using the alias u. The named parameter matching via @Param safely injects runtime values into the query.
3. Using Native SQL Queries in Spring Repositories
Sometimes you need to execute database-specific SQL features that JPQL does not support. You can set the nativeQuery attribute to true inside the @Query annotation to run raw SQL statements.
package com.example.demo.repository;
import com.example.demo.model.ProductItem;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface ProductItemRepository extends JpaRepository<ProductItem, Long> {
@Query(value = "SELECT * FROM product_item WHERE price < :maxPrice", nativeQuery = true)
List<ProductItem> findAffordableProducts(@Param("maxPrice") double maxPrice);
}
Setting nativeQuery = true bypasses JPQL parsing, allowing you to target actual table and column names directly as defined in your relational database engine.
4. Performing Modifying Updates and Deletes
Key Takeaway: Let's dive into the core concepts of "4. Performing Modifying Updates and Deletes" cleanly and effectively.
By default, @Query is used for read operations. When you need to execute update or delete statements that modify database state, you must combine @Query with the @Modifying annotation.
package com.example.demo.repository;
import com.example.demo.model.StoreOrder;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
public interface StoreOrderRepository extends JpaRepository<StoreOrder, Long> {
@Transactional
@Modifying
@Query("UPDATE StoreOrder o SET o.customerName = :newName WHERE o.id = :orderId")
int updateCustomerNameForOrder(@Param("orderId") Long orderId, @Param("newName") String newName);
}
Combining @Modifying and @Transactional ensures that state-changing queries execute properly within an active transaction context and return the updated row count.
5. Handling Pagination and Sorting with Custom Queries
Spring Data JPA allows you to pass a Pageable parameter directly into methods annotated with @Query, making it simple to implement efficient pagination for large datasets.
package com.example.demo.repository;
import com.example.demo.model.SecureTokenRecord;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface SecureTokenRepository extends JpaRepository<SecureTokenRecord, String> {
@Query("SELECT t FROM SecureTokenRecord t WHERE t.description IS NOT NULL")
Page<SecureTokenRecord> findAllWithDescription(Pageable pageable);
}
Integrating Pageable with your custom query handles limits, offsets, and dynamic sorting automatically without extra coding effort.
6. Verifying Query Execution Logs in Spring Boot
Inspecting console logs during application startup and runtime helps confirm that Spring successfully parses and validates your custom query configurations.
2026-07-13 14:01:34.000 INFO --- [main] org.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2026-07-13 14:01:34.200 INFO --- [main] o.s.data.jpa.repository.query.JpaQueryMethod : Parsed query method [findUsersByEmailDomain] successfully
2026-07-13 14:01:34.500 INFO --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http)
Observing successful parsing logs verifies that your JPQL or native SQL syntax aligns with your entity structures and database driver setup.
Summary
Key Takeaway: Let's dive into the core concepts of "Summary" cleanly and effectively.
Throughout this comprehensive guide, we have explored the essential mechanics and advanced features of the @Query annotation in Java Spring and Spring Data JPA. For beginners diving into Java programming basics and building backend persistence layers for Spring Boot, understanding when and how to implement custom queries is an important milestone. We discussed how standard method name derivation works for simple retrievals, but when applications demand complex conditions, joins, or tailored performance optimizations, @Query provides the exact flexibility required. We learned how to write standard JPQL targeting entity attributes, execute database-specific native SQL queries by setting the nativeQuery flag, and manage data mutation operations safely using the @Modifying and @Transactional annotations together.
Furthermore, we examined how to integrate pagination effortlessly by passing Pageable parameters directly into our custom repository queries, ensuring that large datasets load efficiently without exhausting application memory. By adhering to these standard architectural patterns and validating execution logs upon startup, developers can maintain robust and high-performing database interactions in enterprise-grade Spring applications.
package com.example.demo.repository;
import com.example.demo.model.SummarySystemRecord;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.Optional;
public interface SummaryRecordRepository extends JpaRepository<SummarySystemRecord, Long> {
@Query("SELECT r FROM SummarySystemRecord r WHERE r.id = :recordId")
Optional<SummarySystemRecord> findRecordByIdCustom(@Param("recordId") Long recordId);
}
Student
"Reviewing all our lessons, I now realize that @Query is invaluable when method names get too long or when we need specific native SQL functionality."
Teacher
"Spot on! It gives you absolute freedom to write precisely optimized database queries while remaining fully integrated with Spring Data JPA."
Student
"And for update or delete statements, I must always remember to include both @Modifying and @Transactional."
Teacher
"Exactly right. You are now well-prepared to build efficient and advanced query layers in your future Spring Boot projects!"