← Home
☕Java Developer Interview Questions — Fresher to 5 YOEJava Dev InterviewNEW
🗄️

Spring Data JPA & Transactions

Repositories, N+1, lazy/eager, @Transactional · 11 questions

1

What is Spring Data JPA, and how do repository interfaces work with no implementation code?

Must Know🌱 Fresher

Spring Data JPA sits on top of JPA/Hibernate and eliminates almost all of the boilerplate data-access code (open a session, build a query, map results). You declare a repository interface extending JpaRepository<Entity, IdType> — no implementation class needed. At startup, Spring generates a dynamic proxy implementation of that interface on the fly (using reflection), backed by an actual EntityManager, giving you CRUD, paging, and sorting for free.

A full repository, zero implementation codejava
interface UserRepository extends JpaRepository<User, Long> {
    // save(), findById(), findAll(), delete(), count()... all inherited, already implemented
}
Spring Data JPA
2

What is the N+1 select problem, and how do you fix it?

Must Know📊 Mid

Fetching a list of N parent entities, then accessing a lazy association on each one individually, triggers 1 query for the parents + N additional queries (one per parent, to fetch its association) — instead of a single, efficient join.

❌ N+1 in action
List<Order> orders = orderRepo.findAll();      // 1 query -- fetches 100 orders
for (Order o : orders) {
    o.getCustomer().getName();                  // lazy load -- 1 query PER order
}
// Total: 1 + 100 = 101 queries, when 1 join query would do

Fixes: a JPQL JOIN FETCH to pull the association in the same query, an @EntityGraph to declare which associations to eagerly fetch for a specific query method, or batch fetching (@BatchSize) to collapse the N queries into a handful of IN (...) queries instead of one.

Spring Data JPAPerformance
3

What is the difference between lazy and eager fetching, and what are the default fetch types?

Must Know📊 Mid

Lazy fetching defers loading an association until you actually access it (a proxy stands in until then) — saves memory/queries when you don't always need the related data, but requires the session/transaction to still be open at access time, or you get LazyInitializationException. Eager fetching loads the association immediately, in the same query (or an immediate follow-up), whether you need it or not.

JPA's default fetch types differ by relationship type: @ManyToOne and @OneToOne default to EAGER; @OneToMany and @ManyToMany default to LAZY. In practice, most teams explicitly set everything to LAZY and fetch what's needed deliberately (via JOIN FETCH/entity graphs), since unexpected eager loading is a common source of the N+1 problem.

Spring Data JPAFetching
4

How does @Transactional work — propagation and rollback rules?

Must Know📊 Mid

@Transactional wraps a method in a database transaction: Spring opens one before the method runs, commits it if the method completes normally, and rolls it back if an unchecked exception (RuntimeException or Error) propagates out — by default, checked exceptions do not trigger a rollback unless you configure rollbackFor explicitly.

Propagation controls how a transactional method behaves when called from another method that's already inside a transaction — the default, REQUIRED, joins the existing transaction if one is active, or starts a new one if not. Other options include REQUIRES_NEW (always suspend the current transaction and start an independent one — useful for "always log this, even if the outer transaction rolls back") and NESTED (a savepoint within the outer transaction).

Spring Data JPATransactions
5

What is the difference between CrudRepository, PagingAndSortingRepository, and JpaRepository?

Important🌱 Fresher
InterfaceAdds
CrudRepositoryBasic CRUD: save, findById, findAll, delete, count.
PagingAndSortingRepositoryExtends CrudRepository, adds findAll(Pageable) and findAll(Sort).
JpaRepositoryExtends both, adds JPA-specific extras: batch operations (saveAll), flushing, getReferenceById (a lazy proxy without hitting the DB).
Spring Data JPA
6

What are derived query methods (e.g. findByNameAndAge)?

Important🌱 Fresher

Spring Data parses a repository method's name and generates the query implementation automatically — no SQL or JPQL written at all, as long as the method follows the naming convention.

Derived queries from method namesjava
interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByLastName(String lastName);
    List<User> findByAgeGreaterThanAndActiveTrue(int age);
    Optional<User> findByEmailIgnoreCase(String email);
    long countByCity(String city);
}

Great for simple lookups; once conditions get complex (multiple joins, dynamic filters), an explicit @Query or the Criteria API reads far more clearly than an increasingly long method name.

Spring Data JPAQuery Methods
7

What does @Query do, and what is the difference between JPQL and native SQL?

Important📊 Mid

@Query lets you write the query explicitly instead of relying on method-name derivation — needed once a query gets complex, or needs a join/aggregation the naming convention can't express.

  • JPQL (the default) is written against your entity model (class and field names, not table/column names) — portable across databases, and Hibernate translates it to the right SQL dialect.
  • Native SQL (nativeQuery = true) is real, database-specific SQL — an escape hatch for vendor-specific features (window functions, full-text search) JPQL can't express, at the cost of database portability.
JPQL vs nativejava
@Query("SELECT u FROM User u WHERE u.active = true AND u.age > :minAge")
List<User> findActiveAdults(@Param("minAge") int minAge); // JPQL -- entity/field names

@Query(value = "SELECT * FROM users WHERE active = true", nativeQuery = true)
List<User> findActiveNative(); // real SQL -- table/column names
Spring Data JPAJPQL
8

How do you map @OneToMany, @ManyToOne, and @ManyToMany relationships?

Important📊 Mid
Bidirectional @OneToMany / @ManyToOnejava
@Entity
class Department {
    @OneToMany(mappedBy = "department") // "department" = the field name on Employee
    List<Employee> employees;
}

@Entity
class Employee {
    @ManyToOne
    @JoinColumn(name = "department_id") // this side owns the foreign key column
    Department department;
}

mappedBy marks the inverse (non-owning) side of a bidirectional relationship — it just tells JPA "the foreign key is managed by the other side's field," so JPA doesn't try to create a redundant join table. The side without mappedBy (here, Employee) owns the actual foreign-key column. @ManyToMany works similarly but needs a join table (@JoinTable) on the owning side, since neither entity can hold the other's foreign key directly for a many-to-many relationship.

Spring Data JPAEntity Mapping
9

How do you implement pagination and sorting with Spring Data JPA?

Important🌱 Fresher

Add a Pageable (and/or Sort) parameter to a repository method — Spring Data JPA translates it into the right LIMIT/OFFSET/ORDER BY automatically, and returns a Page<T> that also carries total element/page counts.

Pagination and sortingjava
interface UserRepository extends JpaRepository<User, Long> {
    Page<User> findByActive(boolean active, Pageable pageable);
}

// Usage:
Pageable pageable = PageRequest.of(0, 20, Sort.by("lastName").ascending());
Page<User> page = userRepo.findByActive(true, pageable);
page.getContent();       // the 20 users on this page
page.getTotalElements(); // total matching rows across all pages
Spring Data JPAPagination
10

What is optimistic locking (@Version) vs pessimistic locking in JPA?

Important📊 Mid

Both prevent two transactions from silently clobbering each other's changes to the same row, but with opposite trade-offs:

  • Optimistic locking — assumes conflicts are rare. Add a @Version field to the entity; every update checks the version hasn't changed since it was read, and increments it. If another transaction updated the row in between, the version check fails and JPA throws OptimisticLockException — the app then retries or surfaces a "someone else already changed this" error. No database locks are held while the user is "thinking."
  • Pessimistic locking — assumes conflicts are likely, so it locks the row upfront (SELECT ... FOR UPDATE, via @Lock(LockModeType.PESSIMISTIC_WRITE)) for the duration of the transaction, blocking any other transaction that tries to read/write that row until the first one commits or rolls back.
@Version for optimistic lockingjava
@Entity
class Product {
    @Id Long id;
    int stock;
    @Version
    long version; // JPA manages this automatically -- checked and incremented on every update
}

Optimistic locking scales better under low contention (no held locks); pessimistic locking is safer under high contention where retries would be constant, at the cost of reduced concurrency while the lock is held.

Spring Data JPALocking
11

Why doesn’t @Transactional work when a method is called from within the same class?

Advanced📊 Mid

Spring's @Transactional (like most Spring AOP features) works via proxies — when another bean calls a @Transactional method, it's actually calling the proxy, which opens the transaction, then delegates to the real method. If a method calls another @Transactional method on itself (this.otherMethod()), that call goes directly to the real object, completely bypassing the proxy — so the transactional behavior on the called method is silently skipped.

Self-invocation bypasses the proxyjava
@Service
class OrderService {
    void placeOrder() {
        saveOrder(); // calls "this.saveOrder()" directly -- proxy never intercepts this
    }
    @Transactional
    void saveOrder() { /* runs WITHOUT a transaction when called this way */ }
}

Fix: move the transactional method to a separate bean and call it through that bean, or inject a self-reference of the proxied bean.

Spring Data JPATransactionsAOP