JDBC, Hibernate and JPA Interview Questions for Java
25 JDBC, Hibernate and JPA questions with answers for java roles — 5 easy, 11 medium, 9 hard.
About JDBC, Hibernate and JPA
Every backend talks to a database. Learn JDBC basics, PreparedStatement and batching, connection pooling with HikariCP, then JPA and Hibernate mappings, entity lifecycle, lazy and eager loading, the N+1 problem, caching, transactions, isolation levels and locking, plus Spring Data JPA repositories and query tuning for real production workloads.
Sheet progressStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
✓ 3 questions free • Unlock the rest in seconds
01
What are the main steps to connect to a database with JDBC?
Get a Connection from a DataSource (or DriverManager in small programs), create a PreparedStatement with ? placeholders, bind parameters, execute with executeQuery or executeUpdate, read the ResultSet, and close everything with try-with-resources. In real apps a pooled DataSource like HikariCP supplies connections, and frameworks such as Spring JdbcTemplate or JPA remove most boilerplate.
02
Why is PreparedStatement preferred over Statement?
PreparedStatement sends the SQL with placeholders separately from the parameter values, so user input is never treated as SQL, which prevents SQL injection. The database can also cache and reuse the execution plan, and it handles types like dates correctly. Statement with string concatenation is unsafe and slower for repeated queries, so it should not be used with user input.
03
Write a JDBC method to fetch a user by email safely.
Situation: I chose eager loading for a reporting entity to save time, and later pages became slow. Task: own and fix it. Action: I told my lead, measured the impact, switched associations to lazy with targeted fetch joins and added query count tests. Result: performance recovered within a sprint and I now review fetch strategy in every design discussion.
05
What is the difference between JPA, Hibernate and Spring Data JPA?
JPA, now Jakarta Persistence, is a specification of interfaces and annotations for object relational mapping. Hibernate is the most popular implementation of that specification and adds its own features. Spring Data JPA sits on top and generates repository implementations from interfaces and method names like findByEmail, reducing boilerplate. Your code uses JPA annotations, Hibernate does the work and Spring Data simplifies access.
06
What is the N+1 select problem in Hibernate and how do you fix it?
N+1 happens when you load N parent entities with one query and then a lazy association triggers one extra query per parent, giving N+1 queries. Fix it with a JOIN FETCH in JPQL, an @EntityGraph on the repository method, or batch fetching with @BatchSize or hibernate.default_batch_fetch_size. Detect it by enabling SQL logging or using a query count assertion in tests.
07
What is the difference between lazy and eager loading?
Lazy loading fetches an association only when it is first accessed, using a proxy; eager loading fetches it immediately with the parent. In JPA, @ManyToOne and @OneToOne default to EAGER, while @OneToMany and @ManyToMany default to LAZY. Best practice is to make all associations lazy and fetch what each use case needs with JOIN FETCH or entity graphs, which avoids loading huge object graphs.
Transient: a new object not yet linked to a persistence context. Managed (persistent): attached to the context, so changes are tracked and flushed automatically through dirty checking. Detached: was managed but the context closed or it was evicted; changes are not tracked until merge(). Removed: scheduled for deletion on flush. persist, merge, detach and remove move entities between these states.
09
An API listing 100 orders fires 101 SQL queries. How do you fix it?
This is the N+1 problem from a lazy association loaded in a loop or by JSON serialisation. Fetch what the endpoint needs in one query with JOIN FETCH or @EntityGraph(attributePaths = "items"), or use a DTO projection query. For collections with pagination, fetch ids first, then fetch associations in a second query to avoid in-memory paging. Add a test that asserts the query count.
10
Users get LazyInitializationException when the API returns an entity. What is happening?
The entity was loaded inside a transaction, the session closed, and then Jackson touched a lazy association outside it. Do not fix it by turning on open-session-in-view or making everything eager. Instead, map the entity to a DTO inside the service transaction and fetch needed associations explicitly with JOIN FETCH or an entity graph. Spring Boot logs a warning because open-in-view is on by default.
11
A query is fast in development but takes 8 seconds in production. How do you investigate?
Production has more data, so check the execution plan with EXPLAIN ANALYZE on production-like data. Look for full table scans, missing indexes on filter or join columns, functions on indexed columns and poor statistics. Check if Hibernate generates the SQL you expect. Add a suitable composite index, rewrite the query or paginate, and verify the plan improves before release.
12
Map a one-to-many relation between Order and OrderItem in JPA.
Make the child own the foreign key and keep both sides in sync.
@Entity
class Order {
@Id @GeneratedValue Long id;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
List<OrderItem> items = new ArrayList<>();
void addItem(OrderItem i) { items.add(i); i.setOrder(this); }
}
@Entity
class OrderItem {
@Id @GeneratedValue Long id;
@ManyToOne(fetch = FetchType.LAZY) Order order;
}
13
Write a Spring Data JPA repository method that fetches orders with items to avoid N+1.
Use JOIN FETCH or an entity graph.
public interface OrderRepository extends JpaRepository<Order, Long> {
@Query("select distinct o from Order o join fetch o.items where o.customerId = :cid")
List<Order> findWithItems(@Param("cid") Long customerId);
@EntityGraph(attributePaths = "items")
List<Order> findByStatus(String status);
}
Both load orders and items in one query.
Situation: the customer search page took 6 seconds at peak. Task: bring it under 500 ms. Action: I captured the SQL, ran EXPLAIN ANALYZE and found a full scan due to LOWER(email) on an unindexed column and an N+1 on addresses. I added a functional index, fetched addresses with an entity graph and added pagination. Result: response time dropped to 180 ms and database CPU fell by a third.
16
How do you make sure database changes are released safely?
Situation: a manual schema change once broke our staging environment. Task: make migrations reliable. Action: I introduced Flyway with versioned scripts in the repo, required backward compatible changes such as adding nullable columns first, and ran migrations in CI against a test database. Result: we had zero failed migrations over the next year and rollbacks became predictable.
17
What is the first level and second level cache in Hibernate?
The first level cache is the persistence context itself; it is per session or EntityManager, always on, and returns the same object for repeated finds by id in one transaction. The second level cache is shared across sessions and optional, backed by providers like Ehcache or Infinispan through JCache. Use it for read-mostly reference data, and be careful about stale data in clustered setups.
18
What are transaction isolation levels and which problems do they prevent?
Read Uncommitted allows dirty reads. Read Committed prevents dirty reads and is the default in PostgreSQL and SQL Server. Repeatable Read also prevents non-repeatable reads and is the MySQL InnoDB default. Serializable prevents phantom reads too but reduces concurrency. Choose the lowest level that keeps data correct, and use explicit locking for specific hot rows.
19
What is optimistic versus pessimistic locking in JPA?
Optimistic locking adds a @Version column; on update Hibernate checks the version and throws OptimisticLockException if another transaction changed the row, so you retry. It suits low contention. Pessimistic locking uses SELECT FOR UPDATE through LockModeType.PESSIMISTIC_WRITE, blocking others until commit; it suits high contention on the same rows, like seat booking, but can reduce throughput.
20
Why is connection pooling important and how do you size a pool?
Opening a database connection is slow, so a pool like HikariCP keeps ready connections for reuse. A bigger pool is not always better, because the database has limited CPU and disk. A common starting point is around cores times two plus effective spindles per database, then tune with load tests. Watch pool wait time and active connections metrics, and set connection and query timeouts.
21
A batch job inserting 1 lakh rows through JPA takes 30 minutes. How do you speed it up?
Enable JDBC batching with hibernate.jdbc.batch_size around 50 and order_inserts, and avoid IDENTITY id generation, which disables insert batching; use a SEQUENCE with a pooled optimizer. Flush and clear the EntityManager every batch so the persistence context does not grow. For very large loads use JdbcTemplate batchUpdate or the database bulk loader. This often cuts time to a minute or two.
22
Two users booking the last seat both succeed. How do you prevent double booking?
This is a race between read and write. Use a database guarantee: a unique constraint on seat and show, an atomic conditional update like UPDATE seats SET status = 'BOOKED' WHERE id = ? AND status = 'FREE' and check the affected row count, or pessimistic locking with SELECT FOR UPDATE. Optimistic locking with a version column also works, retrying or failing the loser.
23
Production shows Connection is not available, request timed out after 30000ms from HikariCP. What do you check?
The pool is exhausted. Check for connection leaks, where code holds connections without closing, by setting leakDetectionThreshold. Look for long transactions doing remote calls inside @Transactional, slow queries holding connections and a pool size too small for the thread count. Fix leaks, move external calls outside transactions, optimise slow queries and then size the pool from load tests.
24
Add optimistic locking to an entity and handle the conflict.
Add a version field; Hibernate checks it on update.
@Entity
class Account {
@Id Long id;
long balance;
@Version Long version;
}
@Retryable(retryFor = ObjectOptimisticLockingFailureException.class, maxAttempts = 3)
@Transactional
public void debit(Long id, long amt) {
Account a = repo.findById(id).orElseThrow();
a.balance -= amt;
}
The update includes WHERE version = ?, so a stale write fails and is retried.
25
Write a JPQL query that returns a DTO projection instead of entities.
Projections load only needed columns and avoid dirty checking.
public record OrderSummary(Long id, String customer, long total) {}
@Query("""
select new com.shop.OrderSummary(o.id, c.name, o.total)
from Order o join o.customer c
where o.createdAt >= :from
""")
List<OrderSummary> summaries(@Param("from") LocalDate from);
Interface-based projections in Spring Data are another option.
Frequently asked questions
How important is JDBC, Hibernate and JPA in Java interviews?
Every backend talks to a database. Learn JDBC basics, PreparedStatement and batching, connection pooling with HikariCP, then JPA and Hibernate mappings, entity lifecycle, lazy and eager loading, the N+1 problem, caching, transactions, isolation levels and locking, plus Spring Data JPA repositories and query tuning for real production workloads.
How should I use the JDBC, Hibernate and JPA sheet?
Go top to bottom — easy to hard. Answer before opening each answer, tick what you solved and save misses to Last-Minute Revision.