Blog
50 Spring data JPA Real Interview Questions Asked in MNC
- January 22, 2026
- Posted by: InterviewExpert.org
- Category: Backend Interview Preparation Spring MVC
Spring Data JPA – Core & Conceptual Questions
1. What is Spring Data JPA and how is it different from plain JPA?
Spring Data JPA is a Spring framework module that simplifies data access by reducing boilerplate code required when working with JPA.
Difference from plain JPA:
- Spring Data JPA provides ready-made repository interfaces, automatic query generation, and integration with Spring’s transaction management.
- Plain JPA requires writing
EntityManager, queries, and transaction handling manually.
2. What is JPA and why is it used in Java applications?
JPA (Java Persistence API) is a Java specification that defines how Java objects are mapped to relational database tables.
Why JPA is used:
- Provides ORM (Object Relational Mapping)
- Eliminates boilerplate JDBC code
- Makes applications database-independent
- Supports caching, lazy loading, and transactions
JPA allows developers to work with objects instead of SQL queries.
3. What are the advantages of using Spring Data JPA?
Key advantages of Spring Data JPA:
- Reduces boilerplate code (no DAO implementations)
- Supports derived queries from method names
- Built-in pagination and sorting
- Seamless integration with Spring transactions
- Easy auditing support
- Cleaner and more maintainable code
It helps developers focus on business logic rather than database plumbing.
4. What are the key interfaces in Spring Data JPA (CrudRepository, JpaRepository, PagingAndSortingRepository)?
Spring Data JPA provides repository interfaces to simplify data access by offering ready-made CRUD, pagination, and JPA-specific features. CrudRepository provides basic CRUD operations, PagingAndSortingRepository adds pagination and sorting, and JpaRepository extends both with additional JPA-specific features and is the most commonly used.
1. CrudRepository<T, ID>
- Basic CRUD operations
- Methods:
save(),findById(),findAll(),delete() - Best for simple applications
2. PagingAndSortingRepository<T, ID>
- Extends
CrudRepository - Adds pagination and sorting
- Methods:
findAll(Pageable pageable),findAll(Sort sort) - Used when handling large datasets
3. JpaRepository<T, ID>
- Extends
PagingAndSortingRepository - Adds JPA-specific features
- Methods:
flush(),saveAndFlush(),findAll() - Most commonly used in real-world applications
JpaRepository is the most commonly used interface.
Quick Comparison (Interview Friendly)
| Interface | CRUD | Pagination | Sorting | JPA Features |
|---|---|---|---|---|
| CrudRepository | ✅ | ❌ | ❌ | ❌ |
| PagingAndSortingRepository | ✅ | ✅ | ✅ | ❌ |
| JpaRepository | ✅ | ✅ | ✅ | ✅ |
5. What is the Repository pattern?
The Repository pattern is a design pattern that abstracts data access logic and provides a clean interface for performing CRUD operations on the data source. It decouples the business logic from persistence logic, making the application easier to test, maintain, and modify without affecting the domain layer.
In Spring Boot, repositories are typically implemented using Spring Data JPA, where interfaces extend JpaRepository or CrudRepository, and Spring automatically provides the implementation.
Repository pattern makes the code:
- Loosely coupled
- Testable
- Maintainable
6. What is the role of @Repository annotation?
@Repository is a Spring stereotype annotation used to mark a class as a Data Access Object (DAO). It indicates that the class interacts with the database and enables exception translation, converting low-level persistence exceptions into Spring’s unified DataAccessException hierarchy.
Key Roles of @Repository
- Identifies the persistence layer (DAO)
- Translates database exceptions automatically
- Enables component scanning so Spring can manage the bean
- Makes DAO classes easier to mock and test
7. How does the method name query derivation mechanism work in Spring Data JPA?
Spring Data JPA parses method names and converts them into SQL queries. The framework analyzes keywords in the method name (like findBy, And, Or, Between, OrderBy) and maps them to entity fields to create the corresponding JPQL/SQL query at runtime.
Example:
findByEmailAndStatus(String email, String status)Generated SQL:
SELECT * FROM user WHERE email=? AND status=?Keywords like findBy, And, Or, Between, OrderBy drive query creation.
8. What is the difference between CrudRepository and JpaRepository?
CrudRepository provides basic CRUD operations, while JpaRepository extends it with pagination, sorting, and JPA-specific features.JpaRepository is more powerful and commonly used in real-world Spring Boot applications.
| Feature | CrudRepository | JpaRepository |
|---|---|---|
| CRUD operations | ✅ Yes | ✅ Yes |
| Pagination | ❌ No | ✅ Yes |
| Sorting | ❌ No | ✅ Yes |
| Batch operations | ❌ No | ✅ Yes |
| JPA-specific methods | ❌ No | ✅ Yes (flush, saveAndFlush) |
| Extends | Repository | PagingAndSortingRepository |
Example of CrudRepository
public interface UserRepo extends CrudRepository<User, Long> { }Example of JpaRepository
public interface UserRepo extends JpaRepository<User, Long> { }9. What is an Entity in JPA?
An Entity in JPA is a lightweight, persistent Java class that represents a table in a relational database. Each entity instance corresponds to a row in the table, and its fields map to the table’s columns.
Simply you can also say, A JPA Entity is a persistent Java class annotated with @Entity that maps to a database table, where each object represents a row in that table.
Characteristics of Entity:
- Annotated with
@Entity - Must have a primary key (
@Id) - Managed by the JPA EntityManager
- Mapped to a database table (optionally using
@Table) - Must have a no-argument constructor
Entity lifecycle is managed by JPA.
Example of @Entity
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
}10. Explain EntityManager and its responsibilities.
EntityManager is the core JPA interface responsible for managing the lifecycle of entities and interacting with the persistence context. It handles CRUD operations, query execution, transaction synchronization, and entity state management.
EntityManager is the core JPA interface responsible for:
- Managing entity lifecycle
- CRUD operations
- Query execution
- Persistence context management
Spring Data JPA internally handles EntityManager for you.
11. What is the Persistence Context?
The Persistence Context is a first-level cache managed by JPA (via the EntityManager) that tracks and manages the lifecycle of entity objects during a transaction.
It represents a set of managed entity instances where each entity is unique per database row.
The persistence context ensures that:
- Each database row maps to only one entity object in memory
- Any change made to a managed entity is automatically detected and synchronized with the database
- Repeated fetches of the same entity return the same object reference
It exists per transaction or per EntityManager.
12. What is dirty checking in Hibernate/JPA?
Dirty checking is a Hibernate/JPA mechanism that automatically detects changes made to managed entities and synchronizes those changes with the database at transaction commit or flush time without explicitly calling save() or update().
How Dirty checking Works?
- Entity is in managed (persistent) state
- Hibernate takes a snapshot of the entity when it is loaded
- At flush/commit, it compares the current state with the snapshot
- If changes are found, an UPDATE query is generated automatically
Dirty checking Example
@Transactional
public void updateUser(Long id) {
User user = entityManager.find(User.class, id);
user.setName("New Name"); // No save() call
// UPDATE happens automatically on commit
}13. What is flush() vs commit() in JPA?
In JPA, flush() synchronizes the persistence context with the database, while commit() finalizes the transaction and makes changes permanent.
flush() sends SQL (INSERT, UPDATE, DELETE) to the database but does not commit the transaction.
commit() commits the transaction and makes all flushed changes permanent.
If rollback happens after flush() but before commit(), changes are not persisted.
Key Differences between flush() and commit()
| Aspect | flush() | commit() |
|---|---|---|
| Purpose | Sync entity state with DB | End transaction |
| Writes to DB | ✅ Yes | ✅ Yes |
| Transaction ends | ❌ No | ✅ Yes |
| Can be rolled back | ✅ Yes | ❌ No |
| Automatically called | Before query / commit | At transaction end |
commit() always performs a flush.
14. What is fetch type and its defaults for relationships? (EAGER vs LAZY)
Fetch type defines when related entities are loaded from the database in JPA relationships. It controls whether associated data is fetched immediately or only when accessed, which has a direct impact on performance. Use LAZY by default for better performance and explicitly use EAGER only when the related data is always required.
There are two fetch types: EAGER and LAZY.
- EAGER fetch means related entities are loaded immediately along with the parent entity. It can lead to unnecessary data loading and performance issues if overused.
- LAZY fetch means related entities are loaded only when they are accessed. It is more efficient and recommended for most relationships.
Default fetch types for JPA relationships:
@ManyToMany→ LAZY@OneToOne→ EAGER@ManyToOne→ EAGER@OneToMany→ LAZY
15. What is cascading and cascade types in JPA?
Cascading in JPA means that operations performed on a parent entity are automatically propagated to its related child entities.
In simple words, you don’t need to explicitly perform the same operation on the child entity—JPA does it for you.
Cascading is defined using the cascade attribute in relationship annotations like:@OneToMany, @ManyToOne, @OneToOne, @ManyToMany.
Why is cascading needed?
- To maintain data consistency
- To reduce boilerplate code
- To ensure parent–child entities are handled together
Example (Real-world explanation)
Consider:
- Order → Parent
- OrderItem → Child
If you save an Order, you usually want all its OrderItems to be saved automatically.
This is where cascading helps.
Cascade Types in JPA
1. CascadeType.PERSIST
- When the parent is saved, the child is also saved.
- Used during insert operations.
Example of CascadeType.PERSIST:
@OneToMany(cascade = CascadeType.PERSIST)
private List<OrderItem> items;Saving Order will also save OrderItem.
2. CascadeType.MERGE
- When the parent is updated, the child is updated as well.
- Used during update operations.
Helpful when updating detached entities.
3. CascadeType.REMOVE
- When the parent is deleted, the child is also deleted.
- Maps to DELETE operation.
Use carefully, as it can delete large amounts of data.
4. CascadeType.REFRESH
- When the parent entity is refreshed from the database, the child is also refreshed.
- Discards in-memory changes and reloads from DB.
5. CascadeType.DETACH
- Detaches both parent and child from the persistence context.
- No further tracking by JPA.
6. CascadeType.ALL
- Applies all cascade operations:
- PERSIST
- MERGE
- REMOVE
- REFRESH
- DETACH
Example of CascadeType.ALL:
@OneToMany(cascade = CascadeType.ALL)16. What is the N+1 select problem and how can it be avoided?
The N+1 select problem is a performance issue in ORM frameworks like JPA/Hibernate where one query is executed to fetch parent entities and then N additional queries are executed to fetch their related entities.
It typically occurs when a collection or relationship is fetched lazily and accessed inside a loop.
N+1 Example explanation:
If you fetch 1 list of Orders and then access their associated Customers, JPA first runs 1 query to fetch all Orders, and then runs N separate queries to fetch each Customer, resulting in N+1 total queries.
Why it is a problem:
- Causes excessive database calls
- Degrades application performance
- Increases response time significantly
How to solve it:
- Switch to EAGER fetching carefully when appropriate
- Use fetch joins (
JOIN FETCH) in JPQL - Use
@EntityGraphto control fetching - Use batch fetching (
@BatchSize)
17. What is the difference between JPQL and SQL?
JPQL is an object-oriented, database-independent query language that operates on entities, while SQL is a database-specific query language that operates on tables and columns.
Use JPQL when working with JPA entities and ORM features, and use SQL when you need fine-grained control over database-specific queries or performance optimizations.
Example difference:
JPQL uses entity names and fields
SELECT u FROM User u WHERE u.age > 25SQL uses table and column names
SELECT * FROM users WHERE age > 2518. What is a native query and how to write one in Spring Data JPA?
A native query is a query written in database-specific SQL, not in JPQL. Spring Data JPA allows executing native SQL when JPQL is not sufficient or optimal. Spring Data JPA allows native queries using the @Query annotation with nativeQuery = true.
How to write a native query?
@Query(value = "SELECT * FROM users WHERE status = ?1", nativeQuery = true)
List<User> findUsersByStatus(String status);Named Parameters Example
@Query(value = "SELECT * FROM users WHERE email = :email", nativeQuery = true)
User findByEmail(@Param("email") String email);Key interview points
- Native queries work on tables and columns, not entities
- Result can be mapped to:
- Entity
- DTO
Object[]
- Less portable across databases
19. When would you use @Query annotation over derived queries?
Derived queries are great for simple conditions, but @Query is preferred when queries become complex.
Use @Query when:
- Query involves joins across multiple entities
- Custom select (partial fields / DTO projection)
- Complex conditions (OR, subqueries)
- Performance optimization is required
- Native SQL is needed
Example
@Query("SELECT u FROM User u JOIN u.roles r WHERE r.name = :role")
List<User> findUsersByRole(@Param("role") String role);Interview insight
@Queryimproves control and performance- Derived queries improve readability
20. What is the difference between save() and saveAndFlush()?
save() defers database write, while saveAndFlush() immediately syncs changes with the database. Both methods persist entities, but they differ in when data is written to the database.
save() delays database synchronization until flush or commit, while saveAndFlush() immediately flushes changes to the database.
Short Explanation
save()stores the entity in the persistence context; SQL is executed later at flush or commit.saveAndFlush()forces Hibernate to execute SQL immediately by callingflush()internally.- Both run within the same transaction.
Example
userRepository.save(user); // SQL not executed yet
userRepository.saveAndFlush(user); // SQL executed immediately
Difference between save() and saveAndFlush()
| save() | saveAndFlush() |
|---|---|
| Saves entity to persistence context | Saves and immediately flushes |
| DB write may be delayed | Forces DB synchronization |
| Faster in batch operations | Slightly slower |
| Flush happens at commit | Flush happens immediately |
Example use case
saveAndFlush()→ When subsequent logic depends on DB state (e.g., constraint validation)save()→ Normal CRUD operations
21. What is the purpose of @Transactional in Spring Data JPA?
@Transactional is used to define a transactional boundary in Spring Data JPA, ensuring that a group of database operations are executed as a single atomic unit.
Its main purpose is to maintain data consistency by following the ACID properties. If all operations inside the transaction succeed, the transaction is committed; if any operation fails with an exception, the transaction is rolled back automatically.
Spring Data JPA repository methods are already transactional, but @Transactional is crucial at the service layer to control transaction scope and behavior.
When @Transactional is applied:
- On runtime exception, changes are rolled back
- Spring starts a transaction before method execution
- All JPA operations run within the same persistence context
- On successful completion, changes are committed to the database
22. Explain Optimistic vs Pessimistic Locking.
Locking is used to handle concurrent data access. Optimistic locking assumes minimal conflicts, while pessimistic locking prevents conflicts by locking data early.
Optimistic Locking
- Uses a version column (
@Version) - No database lock
- Checks for conflicts at commit time
- Best for low-conflict systems
@Version
private int version;Pessimistic Locking
- Locks database rows immediately
- Other transactions must wait
- Suitable for high-conflict systems
- Uses database-level locks
Comparison between Optimistic and Pessimistic
| Optimistic | Pessimistic |
|---|---|
| No DB lock | DB lock |
| Better performance | Safer concurrency |
| May throw exception | Blocks other users |
23. What are JPA lock modes?
JPA lock modes are mechanisms used to control concurrent access to database records to maintain data consistency during transactions. They define how and when an entity is locked while being read or updated.
Optimistic Lock Modes
Does not lock rows; uses a version check.
OPTIMISTICOPTIMISTIC_FORCE_INCREMENT
Optimistic Lock Example
@Version
private Long version;Pessimistic Lock Modes
PESSIMISTIC_READ– Prevents other transactions from writingPESSIMISTIC_WRITE– Prevents other transactions from reading or writingPESSIMISTIC_FORCE_INCREMENT– Forces version increment
Pessimistic Lock Example
@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<User> findById(Long id);When to Use what?
- Optimistic locking → High read, low conflict systems
- Pessimistic locking → High write contention, critical updates
24. What is Auditing in Spring Data JPA and how do you enable it?
Auditing automatically tracks who created/modified an entity and when. Auditing automatically records metadata like creation and modification details of entities.
Fields commonly audited
- Created date
- Last modified date
- Created by
- Modified by
Steps to enable auditing
- Enable auditing
@EnableJpaAuditing- Use auditing annotations
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;Why auditors love this
- No manual timestamp handling
- Essential for enterprise applications
- Improves traceability
25. What is the Criteria API and when do you use it?
The Criteria API is a JPA feature used to build type-safe, dynamic queries programmatically instead of writing static JPQL or SQL. It allows queries to be constructed at runtime using Java objects.
Why use Criteria API
- Dynamic filtering
- Conditional query building
- Avoids string-based JPQL errors
- Compile-time safety
When to use it
- Search screens with many optional filters
- Reports with dynamic conditions
- Complex query construction at runtime
Drawback
- More verbose
- Harder to read than JPQL
Criteria API Example
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> root = cq.from(User.class);
cq.select(root).where(cb.equal(root.get("status"), "ACTIVE"));
List<User> users = entityManager.createQuery(cq).getResultList();Entity & Relationship Mapping Questions
1. How do you define a composite primary key in JPA?
A composite primary key is a primary key that consists of more than one column.
In JPA, it can be defined in two ways:
- Using
@EmbeddedId - Using
@IdClass
Option 1: Using @EmbeddedId (Most Preferred)
- Create a separate embeddable class
- Annotate it with
@Embeddable - Use it in the entity with
@EmbeddedId
@Embeddable
public class OrderId {
private Long orderId;
private Long productId;
}@Entity
public class Order {
@EmbeddedId
private OrderId id;
}Key points to mention in interview
- Cleaner design
- Better encapsulation
- Easier to maintain
Option 2: Using @IdClass
- Primary key fields are defined directly in the entity
- A separate key class is used only for mapping
@IdClass(OrderId.class)
@Entity
public class Order {
@Id
private Long orderId;
@Id
private Long productId;
}Interview insight
@IdClassis often seen in legacy systems@EmbeddedIdis preferred in modern applications
2. What is the difference between @Entity and @Table?
@Entity | @Table |
|---|---|
| Marks a class as a JPA entity | Maps entity to a specific table |
| Mandatory | Optional |
| Represents an object | Defines DB table details |
Example of using @Entity and @Table
@Entity
@Table(name = "users")
public class User {
}Important interview point
@Entityis required;@Tableis optional- If
@Tableis not used, JPA uses class name as table name
3. What are the various JPA relationship mappings (@OneToOne, @OneToMany, @ManyToOne, @ManyToMany)?
@OneToOne
- One entity maps to exactly one other entity
Example: User and Profile
@OneToOne
@JoinColumn(name = "profile_id")
private Profile profile;@OneToMany
- One parent has many children
Example: Order and OrderItems
@OneToMany(mappedBy = "order")
private List<OrderItem> items;@ManyToOne
- Many entities map to one parent
Example: Orders and Customer
@ManyToOne
@JoinColumn(name = "customer_id")
private Customer customer;@ManyToMany
- Many entities map to many entities
Example: Student and Course
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)4. What is mappedBy attribute used for?
mappedBy is used in bidirectional relationships to indicate the owning side of the relationship. It tells JPA which side is NOT the owner of the relationship.
In simple words:
mappedBypoints to the field name in the owning entity- It tells JPA:
- “This side is just a mirror. The other side controls the relationship.”
Why it is important
- Prevents duplicate join tables
- Improves performance
- Avoids unnecessary updates
Example: Order and OrderItem
Order Entity (Inverse Side)
@OneToMany(mappedBy = "order")
private List<OrderItem> items;Orderis NOT the ownermappedBy = "order"means:
Theorderfield inOrderItemowns this relationship
OrderItem Entity (Owning Side)
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;OrderItemowns the relationshiporder_idforeign key exists here- Database updates happen from this side
How JPA Updates Data
Correct way:
orderItem.setOrder(order);Not enough alone:
order.getItems().add(orderItem);Because:
- Only the owning side updates the database
Best practice:
order.getItems().add(orderItem);
orderItem.setOrder(order);5. How do you map bidirectional relationships?
A bidirectional relationship means both entities reference each other. Bidirectional relationships in JPA are mapped by defining the relationship on both entities, where one side is the owning side and the other is the inverse side using mappedBy.
The owning side manages the foreign key in the database.
Example: Order ↔ OrderItem OneToMany ↔ ManyToOne
@Entity
public class Order {
@OneToMany(mappedBy = "order")
private List<OrderItem> items;
}@Entity
public class OrderItem {
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
}Item → Owning sideOrder → Inverse side
Key Points (Very Important for Interviews)
- Owning side → contains the foreign key (
@JoinColumn) - Inverse side → uses
mappedBy - Only the owning side updates the database
- Both sides should be kept in sync in code
6. Explain JoinTable vs JoinColumn.
@JoinColumn maps a relationship using a foreign key column, while @JoinTable maps a relationship using a separate join table.
@JoinColumn
- Used for foreign key mapping
- Common in
@ManyToOneand@OneToOne
@ManyToOne
@JoinColumn(name = "user_id")
private User user;@JoinTable
- Used when a separate join table is needed
- Mostly used in
@ManyToMany
@ManyToMany
@JoinTable(
name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles;Summary
@JoinTable→ Intermediate table@JoinColumn→ Direct FK
7. What is @EmbeddedId and @IdClass?
@EmbeddedId and @IdClass are JPA mechanisms used to define composite primary keys (multiple columns as a primary key). In real projects, @EmbeddedId is preferred due to cleaner design.
@EmbeddedId
- Uses an embeddable key class
- Primary key is represented as a single object
- Cleaner and more object-oriented
- Preferred in most cases
@Embeddable
public class OrderItemId implements Serializable {
private Long orderId;
private Long productId;
}@Entity
public class OrderItem {
@EmbeddedId
private OrderItemId id;
}@IdClass
- Primary key fields are defined directly in the entity
- Separate key class only defines the key structure
- Less clean but more explicit
public class OrderItemId implements Serializable {
private Long orderId;
private Long productId;
}@Entity
@IdClass(OrderItemId.class)
public class OrderItem {
@Id
private Long orderId;
@Id
private Long productId;
}Key Differences between @EmbeddedId and @IdClass
| Aspect | @EmbeddedId | @IdClass |
|---|---|---|
| Key representation | Single embedded object | Multiple @Id fields |
| Object-oriented | ✅ Yes | ❌ Less |
| Code readability | Cleaner | More verbose |
| Recommended | ✅ Yes | ⚠️ Less preferred |
8. What is orphanRemoval?
orphanRemoval = true ensures that child entities are deleted when removed from the parent collection. orphanRemoval is a JPA attribute that automatically deletes child entities when they are removed from a parent’s relationship. It ensures that “orphaned” child records do not remain in the database.
Example of orphanRemoval
@Entity
public class Order {
@OneToMany(mappedBy = "order", orphanRemoval = true)
private List<Item> items;
}What happens
- Remove child from collection → deleted from DB
- Parent still exists
Difference from cascade REMOVE
- orphanRemoval deletes children when disassociated
- Cascade REMOVE deletes children when parent is deleted
9. How to handle inheritance mappings in JPA? (SINGLE_TABLE, JOINED, TABLE_PER_CLASS).
JPA supports inheritance mapping to represent an object-oriented class hierarchy in relational databases using three strategies: SINGLE_TABLE, JOINED, and TABLE_PER_CLASS.
JPA handles inheritance using SINGLE_TABLE for speed, JOINED for normalized schemas, and TABLE_PER_CLASS for separate tables per concrete class, each with different performance trade-offs.
1. SINGLE_TABLE
- All classes stored in one table
- Uses a discriminator column
- Fastest performance
- May contain nullable columns
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type")
public class Vehicle { }2. JOINED
- Each class has its own table
- Tables joined using primary keys
- Normalized design
- Slightly slower due to joins
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Vehicle { }3. TABLE_PER_CLASS
- Each concrete class has a separate table
- No joins
- Poor performance for polymorphic queries
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Vehicle { }10. What is @ElementCollection?
@ElementCollection is a JPA annotation used to store collections of non-entity (value) types as part of an entity. These elements do not have their own identity and are stored in a separate collection table.
Example of @ElementCollection
@Entity
public class User {
@Id
private Long id;
@ElementCollection
@CollectionTable(name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"))
@Column(name = "role")
private Set<String> roles;
}Key characteristics of @ElementCollection
- No primary key
- Cannot exist independently
- Stored in separate table
Advanced & Practical Usage Questions
1. How to implement Pagination and Sorting in Spring Data JPA?
Pagination and Sorting in Spring Data JPA are implemented using the Pageable and Sort abstractions.
To use them, the repository must extend JpaRepository or PagingAndSortingRepository. Spring Data JPA automatically applies pagination and sorting at the database level, not in memory.
Pagination is implemented by passing a Pageable object (usually PageRequest) to a repository method. Sorting can be defined inside PageRequest or separately using Sort.
Example of Pagination and Sorting:
Pageable pageable = PageRequest.of(0, 10, Sort.by("name").ascending());
Page<User> users = userRepository.findAll(pageable);The Page<T> result provides both data and metadata such as total pages, total records, and current page.
2. What is Projections and DTO projections in Spring Data JPA?
Projections are used when you want to fetch only selected columns instead of the full entity. This improves performance and avoids exposing unnecessary data. Projections help optimize read operations by fetching only required fields instead of complete entities.
Spring Data JPA supports three types of projections.
1. Interface-based projection example:
public interface UserNameEmailView {
String getName();
String getEmail();
}Repository usage:
List<UserNameEmailView> findByStatus(String status);2. Class-based DTO projection example:
public class UserDTO {
private String name;
private String email;
public UserDTO(String name, String email) {
this.name = name;
this.email = email;
}
}3. Repository with JPQL:
@Query("SELECT new com.example.dto.UserDTO(u.name, u.email) FROM User u WHERE u.status = :status")
List<UserDTO> findUserDTOs(@Param("status") String status);Interview points to highlight:
- DTO projections are preferred for APIs
- Entities should not be exposed directly
- Reduces memory usage and improves performance
3. Explain Query by Example (QBE) in Spring Data JPA.
Query by Example allows building dynamic queries using an example entity (probe) instead of writing JPQL or SQL. Query by Example is useful for simple dynamic filtering where query conditions are not fixed at compile time
You create:
- A probe object with fields set
- An
ExampleMatcherto define matching behavior
Example code:
User probe = new User();
probe.setStatus("ACTIVE");
probe.setCity("Delhi");
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnoreNullValues()
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);
Example<User> example = Example.of(probe, matcher);
List<User> users = userRepository.findAll(example);Important interview points:
- No joins or complex conditions supported
- Only non-null fields are considered
- Best for simple search filters
4. How do you write Custom Repository Implementation?
Custom repository implementation is used when Spring Data’s derived queries or @Query are not enough. Custom repository implementation allows extending Spring Data JPA without losing its abstraction.
Steps involved in writing Custom Repository Implementation:
First, define a custom repository interface:
public interface UserRepositoryCustom {
List<User> findActiveUsersWithCustomLogic();
}Then extend it in your main repository:
public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {
}Now create the implementation class. The name must end with Impl:
@Repository
public class UserRepositoryImpl implements UserRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
@Override
public List<User> findActiveUsersWithCustomLogic() {
return entityManager
.createQuery("SELECT u FROM User u WHERE u.status = 'ACTIVE'", User.class)
.getResultList();
}
}Interview Point:
- Allows mixing Spring Data with custom logic
- Naming convention is mandatory
- Spring automatically wires the implementation
5. What is Specification API?
The Specification API in Spring Data JPA is used to build reusable, type-safe, and dynamic database queries using the JPA Criteria API. It allows combining query conditions programmatically instead of writing static JPQL.
To use it, your repository must extend JpaSpecificationExecutor.
6. How does Spring Data JPA handle Transactions internally with save/read methods?
Spring Data JPA handles transactions internally using Spring’s transaction management abstraction. Most repository methods are already transactional by default, even if you do not explicitly annotate them.
For read operations like findById(), findAll(), or derived finder methods:
- They are executed inside a read-only transaction
- Spring sets
@Transactional(readOnly = true)internally - This improves performance by disabling dirty checking
For write operations like save(), delete(), saveAll():
- They run inside a read-write transaction
- If no transaction exists, Spring creates one
- On method completion, Spring commits the transaction
- On runtime exception, Spring rolls back the transaction
Example
userRepository.save(user);Internally:
- Transaction starts
- Entity is managed by persistence context
- Changes are flushed at commit time
- Transaction commits
If you annotate a service method with @Transactional, all repository calls inside it participate in the same transaction.
@Transactional
public void updateUser(User user) {
userRepository.save(user);
auditRepository.save(audit);
}7. What are some performance optimization tips in Spring Data JPA?
Performance optimization in Spring Data JPA mainly focuses on reducing database calls, controlling fetching, and minimizing memory usage.
Key optimization techniques
1. Prefer LAZY Fetching over EAGER
Avoid loading related entities unless they are actually needed.
@OneToMany(fetch = FetchType.LAZY)
private List<OrderItem> items;Prevents unnecessary joins and improves performance.
2. Avoid the N+1 Select Problem
Use fetch joins or entity graphs to load related data in a single query.
✔ Reduces multiple SQL queries
✔ Improves response time
3. Use DTO Projections for Read-Only Queries
Avoid loading full entities when only a few fields are required.
@Query("SELECT new com.dto.UserDTO(u.name, u.email) FROM User u")
List<UserDTO> findUsers();Saves memory and improves query performance.
4. Enable Pagination for Large Datasets
Never load large tables completely into memory.
Page<User> users = userRepository.findAll(PageRequest.of(0, 20));✔ Limits data fetched
✔ Improves scalability
5. Use Batch Operations for Inserts and Updates
Batch processing reduces the number of database round trips.
✔ Faster bulk operations
✔ Better throughput
6. Mark Read-Only Transactions Explicitly
Use read-only transactions when data is not being modified.
@Transactional(readOnly = true)
public List<User> getUsers() {
return userRepository.findAll();
}Enables optimizations at the persistence and database level.
7. Add Database Indexes on Frequently Queried Columns
Indexes significantly improve query performance for:
- WHERE clauses
- JOIN conditions
- ORDER BY operations
Especially important for large tables
8. How to handle batch inserts/updates with Spring Data JPA?
Handling batch inserts and updates in Spring Data JPA is very important for performance, especially when dealing with large datasets.
Why Batch Processing is Needed?
By default, JPA
- Executes one SQL statement per entity
- Causes N+1 inserts/updates
- Is slow for bulk operations
Batching groups multiple SQL statements into a single round trip to the database.
1. Enable Batch Processing in JPA (Mandatory)
Add these properties in application.properties or application.yml.
application.properties
spring.jpa.properties.hibernate.jdbc.batch_size=50
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
spring.jpa.properties.hibernate.jdbc.batch_versioned_data=trueWhat these do:
batch_size→ number of records per batchorder_inserts/updates→ groups similar SQL statementsbatch_versioned_data→ allows batching with@Version
2. Use saveAll() (Basic Batch Insert)
List<User> users = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
users.add(new User("User " + i));
}
userRepository.saveAll(users);Limitation
- All entities stay in memory
- Not suitable for very large data (10k+)
3. Use EntityManager with Flush & Clear (Recommended)
This is the best and safest approach.
@Autowired
private EntityManager entityManager;
@Transactional
public void batchInsert(List<User> users) {
int batchSize = 50;
for (int i = 0; i < users.size(); i++) {
entityManager.persist(users.get(i));
if (i > 0 && i % batchSize == 0) {
entityManager.flush();
entityManager.clear();
}
}
entityManager.flush();
entityManager.clear();
}Why this works well:
flush()→ sends batch to DBclear()→ frees memory- Prevents OutOfMemoryError
4. Batch Updates with JPA
Option 1: Update via EntityManager (Batching enabled)
@Transactional
public void batchUpdate(List<User> users) {
int batchSize = 50;
for (int i = 0; i < users.size(); i++) {
entityManager.merge(users.get(i));
if (i % batchSize == 0) {
entityManager.flush();
entityManager.clear();
}
}
}Option 2: JPQL Bulk Update (Fastest, No Entity Lifecycle)
@Modifying
@Transactional
@Query("UPDATE User u SET u.status = :status WHERE u.active = true")
int updateStatus(@Param("status") String status);9. How do you handle large datasets efficiently?
Handling large datasets efficiently requires controlled fetching and streaming data instead of loading everything into memory.
Best practices include
1. Using pagination:
Page<User> page = userRepository.findAll(PageRequest.of(0, 100));2. Using Slice instead of Page when total count is not required:
Slice<User> slice = userRepository.findByStatus("ACTIVE", pageable);3. Using streaming results:
@Query("SELECT u FROM User u")
Stream<User> streamAllUsers();10. What is Entity graph in Spring Data JPA?
An Entity Graph in Spring Data JPA is used to control which related entities are fetched from the database, helping optimize performance by avoiding unnecessary lazy loading or N+1 query problems.
Entity Graph helps solve:
- Over-fetching caused by EAGER relationships
- N+1 select problem
11. What is @Modifying annotation and when to use it?
@Modifying is used in Spring Data JPA to indicate that a query modifies data instead of just reading it. By default, Spring Data assumes that queries annotated with @Query are SELECT queries. When you execute UPDATE, DELETE, or INSERT queries, you must use @Modifying.
12. What are custom converters and how to use them?
Custom converters in JPA are used to convert entity attribute values to database column values and vice versa. They are implemented using the AttributeConverter interface and the @Converter annotation.
Custom converters are useful when:
- Mapping enums to custom values
- Encrypting/decrypting data
- Converting complex objects to database-friendly formats
Example of Custom converters
@Converter(autoApply = true)
public class BooleanToYNConverter
implements AttributeConverter<Boolean, String> {
@Override
public String convertToDatabaseColumn(Boolean value) {
return value ? "Y" : "N";
}
@Override
public Boolean convertToEntityAttribute(String value) {
return "Y".equals(value);
}
}@Entity
public class User {
private Boolean active; // automatically stored as Y/N
}13. How do you handle exception translation in Spring Data JPA?
Spring Data JPA automatically translates low-level persistence exceptions into Spring’s unified exception hierarchy. Spring Data JPA handles exception translation by converting low-level persistence exceptions (JPA/Hibernate) into Spring’s unified DataAccessException hierarchy.
This allows consistent, database-agnostic exception handling.
How Exception Translation Works?
- Enabled automatically by
@Repository - Implemented using
PersistenceExceptionTranslationPostProcessor - Converts vendor-specific exceptions (Hibernate, JPA) into Spring exceptions
Example of exception translation
@Repository
public class UserRepositoryImpl {
// DB exceptions are translated automatically
}try {
userRepository.save(user);
} catch (DataIntegrityViolationException ex) {
// Handle constraint violation
}14. How do you test Spring Data JPA repositories using @DataJpaTest?
@DataJpaTest is used to test Spring Data JPA repositories by loading only JPA-related components and configuring an in-memory database. It enables fast, isolated repository tests without starting the full application context.
Key Points of Spring Data JPA
- Loads only repository layer
- Auto-configures H2 / in-memory DB
- Uses
@Transactional(rolls back after each test) - Faster than
@SpringBootTest
Example of Spring Data JPA
@DataJpaTest
class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Test
void testFindByEmail() {
User user = new User("test@mail.com");
userRepository.save(user);
User found = userRepository.findByEmail("test@mail.com");
assertNotNull(found);
}
}15. Explain second level cache and how to configure it with Spring Data JPA.
The second-level cache is a cache that stores entity data across sessions and transactions. Unlike the first-level cache (persistence context), it is shared across the application. Second-level cache improves performance by caching entity data across sessions, reducing repetitive database access in read-heavy systems.
Why cache is useful:
- Reduces database calls
- Improves performance for frequently accessed data
- Useful for read-heavy applications
Hibernate is the most common JPA provider that supports second-level caching.
Typical cache providers:
- Ehcache
- Hazelcast
- Infinispan
- Redis (via integrations)
Enabling second-level cache (application.properties):
spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactoryMark entity as cacheable:
@Entity
@Cacheable
@org.hibernate.annotations.Cache(
usage = CacheConcurrencyStrategy.READ_WRITE
)
public class User {
}Key interview points:
- First-level cache is mandatory and session-scoped
- Second-level cache is optional and application-scoped
- Not suitable for frequently changing data