In any application, proper data handling is extremely crucial. The persistence layer acts as a bridge between the database and the application, ensuring that data is stored and fetched smoothly.
Spring Data JPA provides multiple ways to define custom queries, making data retrieval flexible and efficient. Learn what are they with practical examples.
Eliminating Boilerplate Code with Spring Data Repositories
Traditionally, writing database access code involved a lot of repetitive boilerplate code for basic CRUD operations. Developers had to implement DAO (Data Access Object) patterns manually using EntityManager or JDBC templates.

Spring Data JPA eliminates this redundancy by allowing developers to define repository interfaces. The framework automatically provides implementations for standard CRUD operations, reducing development effort.
Defining Custom Queries
Sometimes, basic CRUD methods are not enough, and we need custom queries to fetch specific data. Spring Data JPA provides multiple ways to achieve this.
Let's suppose we have the below Table
CREATE TABLE "USER" (
"ID" NUMBER(10, 0),
"FIRST_NAME" VARCHAR2(400),
"LAST_NAME" VARCHAR2(400),
"EMAIL_ADDRESS" VARCHAR2(400)
);
It is translated to JPA as following
@Entity
@Table(name = "USER")
public class UserEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "FIRST_NAME")
private String firstName;
@Column(name = "LAST_NAME")
private String lastName;
@Column(name = "EMAIL_ADDRESS")
private String email;
// Getters and Setters go here...
}
1.From Methods Name
Spring Data JPA allows query methods to be derived from method names.
Example:
public interface UserRepository extends JpaRepository<UserEntity, Long> {
List<User> findByLastName(String lastName);
}
It is equivalent to:
SELECT * FROM users WHERE last_name = ?.
2.Using the @Query Annotation
If method name derivation is not enough, we can use the @Query
annotation for custom JPQL or native SQL queries.
public interface UserRepository extends JpaRepository<UserEntity, Long> {
@Query("SELECT u FROM User u WHERE u.emailAddress = ?1")
User findByEmail(String email);
}
For native SQL queries, use nativeQuery = true
public interface UserRepository extends JpaRepository<UserEntity, Long> {
@Query(value = "SELECT * FROM USER WHERE EMAIL_ADDRESS = ?1",
nativeQuery = true)
User findByEmailNative(String email);
}
3.Utilizing JPA Named Queries
Named queries can be defined at the entity level and referenced in the repository.
@Entity
@Table(name = "USER")
@NamedQueries({
@NamedQuery(name = "UserEntity.findByEmailDomain",
query = "SELECT u FROM UserEntity u WHERE u.email LIKE CONCAT('%@', ?1)"),
@NamedQuery(name = "UserEntity.countByLastName",
query = "SELECT COUNT(u) FROM UserEntity u WHERE u.lastName = ?1")
})
public class UserEntity {
// Entity fields and methods as shown earlier
}
Then, use it in the repository:
public interface UserRepository extends JpaRepository<UserEntity, Integer> {
List<UserEntity> findByEmailDomain(String domain);
Long countByLastName(String lastName);
}
Conclusion
By leveraging Spring Data JPA, developers can build robust, maintainable, and efficient persistence layers that scale with application growth.
Whether you're building a simple user management system or a complex enterprise application, Spring Data JPA provides the tools to streamline your data access code and enhance productivity.
Want to learn more?
- 📩 Subscribe
👉 Read Next: What Is Persistence Contexts In JPA?
👉 Read Next: What are Beans in Spring?
👉 Read Next: What Is Bean Lifecycle In Spring