Skip to content

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?

Save to ▾
Show answer
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?

Save to ▾
Show answer
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.

Save to ▾
Show answer
Use a PreparedStatement and try-with-resources. public Optional<User> findByEmail(String email) throws SQLException { String sql = "SELECT id, name FROM users WHERE email = ?"; try (Connection c = dataSource.getConnection(); PreparedStatement ps = c.prepareStatement(sql)) { ps.setString(1, email); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) return Optional.of(new User(rs.getLong(1), rs.getString(2))); } } return Optional.empty(); }
04

How do you handle a situation where you made a wrong technical decision?

Save to ▾
Show answer
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?

Save to ▾
Show answer
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?

Save to ▾
Show answer
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?

Save to ▾
Show answer
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.
08

Explain the entity lifecycle states in JPA.

Save to ▾
Show answer
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?

Save to ▾
Show answer
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?

Save to ▾
Show answer
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?

Save to ▾
Show answer
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.

Save to ▾
Show answer
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.

Save to ▾
Show answer
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.
14

Write a JDBC batch insert for a list of products.

Save to ▾
Show answer
Batching sends many rows in one round trip. String sql = "INSERT INTO product(name, price) VALUES (?, ?)"; try (Connection c = ds.getConnection(); PreparedStatement ps = c.prepareStatement(sql)) { c.setAutoCommit(false); int n = 0; for (Product p : products) { ps.setString(1, p.name()); ps.setLong(2, p.price()); ps.addBatch(); if (++n % 500 == 0) ps.executeBatch(); } ps.executeBatch(); c.commit(); }
15

Tell me about a time you optimised a slow database query.

Save to ▾
Show answer
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?

Save to ▾
Show answer
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?

Save to ▾
Show answer
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?

Save to ▾
Show answer
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?

Save to ▾
Show answer
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?

Save to ▾
Show answer
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?

Save to ▾
Show answer
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?

Save to ▾
Show answer
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?

Save to ▾
Show answer
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.

Save to ▾
Show answer
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.

Save to ▾
Show answer
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.