Clean • Professional
Spring Data provides powerful ways to query the database without writing boilerplate code. Repository query methods help developers focus on business logic instead of SQL.
Query Methods are methods defined inside a Spring Data repository that allow you to retrieve, update, or delete data from the database.
Spring Data automatically:
👉 This means no SQL, no JDBC boilerplate.
Spring Data supports four main types of query methods:

Derived Query Methods use method name conventions in Spring Data. Spring automatically analyzes the method name and converts it into a SQL or JPQL query at runtime.
Why use Derived Queries?
Common Keywords
| Keyword | Purpose |
|---|---|
findBy | Fetch records from database |
existsBy | Check if a record exists |
countBy | Count number of records |
deleteBy | Delete records |
And, Or | Combine multiple conditions |
Between | Range-based queries |
Like, Containing | String pattern matching |
GreaterThan, LessThan | Comparison operators |
OrderBy | Sort results |
Examples of Derived Query Methods
List<User>findByName(String name);
List<User>findByStatusAndRole(String status, String role);
List<User>findByAgeGreaterThanOrderByNameAsc(int age);
booleanexistsByEmail(String email);
Example method:
findByAgeGreaterThanOrderByNameAsc
Spring interprets it as:
SELECT*FROM users
WHERE age> ?
ORDERBY nameASC;
Derived Query Methods work by parsing method names, not annotations or SQL.
When derived query method names become too long or complex, Spring Data provides the @Query annotation.
It allows you to write custom JPQL or native SQL queries directly in the repository.
Why Use @Query?
JPQL (Java Persistence Query Language) works with entity names and entity fields, not database table or column names.
Example
@Query("SELECT u FROM User u WHERE u.email = :email")
UserfindUserByEmail(@Param("email") String email);
User → Entity nameemail → Entity field:email → Named parameterSpring automatically converts this JPQL query into SQL.
Use native SQL queries when:
Example
@Query(
value = "SELECT * FROM users WHERE status = 'ACTIVE'",
nativeQuery = true
)
List<User>findActiveUsers();
users)status)nativeQuery = true tells Spring to run raw SQL
| Feature | JPQL | Native SQL |
|---|---|---|
| Query Target | Works with entity names and fields | Works with database tables and columns |
| Database Dependency | Database-independent | Database-dependent |
| Portability | High – works across different databases | Low – tied to a specific database |
| Recommended Use | Standard queries and business logic | Complex queries, database-specific features, performance optimization |
| Readability | Cleaner and easier to maintain | Can be complex and verbose |
| Example | @Query("SELECT u FROM User u WHERE u.email = :email") | @Query(value = "SELECT * FROM users WHERE status = 'ACTIVE'", nativeQuery = true) |
Spring Data provides @Param and @Modifying annotations to make query methods more powerful and flexible.
@Param Annotation
@Param is used to bind method parameters to named parameters in JPQL or SQL queries.
It helps make queries clear and readable, and avoids errors caused by parameter order.
Example
@Query("SELECT u FROM User u WHERE u.role = :role")
List<User>findByRole(@Param("role") String role);
:role in the query is linked to the role method parameter using @Param
@Modifying Annotation
@Modifying is required when a query changes data in the database.
It is used with UPDATE, DELETE, or INSERT operations.
Always use @Transactional with @Modifying to ensure the query executes within a transaction.
Example – UPDATE
@Modifying
@Transactional
@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
intupdateUserStatus(
@Param("id") Long id,
@Param("status") String status
);
Example – DELETE
@Modifying
@Transactional
@Query("DELETE FROM User u WHERE u.active = false")
intdeleteInactiveUsers();
Queries with
@Modifyingdo not return entities, they return the number of affected rows.
Sometimes, derived queries or @Query are not enough.
For complex queries, dynamic queries, or queries using Criteria API or native JDBC, you can create a custom repository.
Why Use Custom Repositories?
Step 1: Create a Custom Interface
Create a separate interface for your custom methods.
publicinterfaceUserRepositoryCustom {
List<User>findActiveUsersByRegion(String region);
}
Step 2: Implement the Interface
Create a class ending with Impl (Spring Data uses this naming convention).
publicclassUserRepositoryImplimplementsUserRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
@Override
public List<User>findActiveUsersByRegion(String region) {
return entityManager.createQuery(
"SELECT u FROM User u WHERE u.region = :region AND u.active = true",
User.class
)
.setParameter("region", region)
.getResultList();
}
}
Step 3: Extend Your Main Repository
Finally, extend your main repository with both JPA repository and custom interface.
publicinterfaceUserRepository
extendsJpaRepository<User, Long>, UserRepositoryCustom {
}
UserRepository has all CRUD methods, plus your custom methodAlways follow the Impl naming convention for the implementation class, otherwise Spring won’t detect it.
| Requirement | Best Approach |
|---|---|
| Simple queries | Derived queries |
| Medium complexity | @Query (JPQL) |
| Update / Delete operations | @Modifying |
| Using named parameters | @Param |
| Complex business logic / dynamic queries | Custom repository |
Spring Data provides multiple ways to handle queries in repositories.
@Query – Ideal for medium or complex queries using JPQL or native SQL.@Modifying – Required for update or delete operations.@Param – Binds method parameters to query parameters for clarity and maintainability.