Spring Data JPA: In-Depth Notes + Interview Questions



Table of Contents
Spring Data JPA: In-Depth Notes + Interview Questions
1. What is Spring Data JPA?
Spring Data JPA is part of the Spring Data family. It sits on top of JPA (Java Persistence API) and Hibernate (the default JPA provider) to eliminate boilerplate data access code.
Layered Architecture
Your Code
↓
Spring Data JPA ← Repositories, Query Methods
↓
JPA (javax/jakarta.persistence) ← Standard API
↓
Hibernate ← JPA Implementation (ORM)
↓
JDBC
↓
Database (MySQL, PostgreSQL, H2, etc.)
Key Dependencies (pom.xml)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Choose your DB driver -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
2. application.properties Configuration
# DataSource spring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=root spring.datasource.password=secret spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # JPA / Hibernate spring.jpa.hibernate.ddl-auto=update # Options: none | validate | update | create | create-drop spring.jpa.show-sql=true spring.jpa.format-sql=true spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect # Naming strategy spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
ddl-auto Values Explained
| Value | Behaviour |
|---|---|
none | Do nothing to schema |
validate | Validate schema, throw error if mismatch |
update | Update schema (add columns, don't drop) |
create | Drop and recreate schema on startup |
create-drop | Create on startup, drop on shutdown |
Production rule: Use
validateornone. Use Flyway/Liquibase for migrations.
3. JPA Entity — Complete Mapping
import jakarta.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "users",
uniqueConstraints = @UniqueConstraint(columnNames = "email"))
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "full_name", nullable = false, length = 100)
private String name;
@Column(unique = true, nullable = false)
private String email;
@Column(name = "is_active", columnDefinition = "BOOLEAN DEFAULT TRUE")
private boolean active = true;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Role role;
@CreationTimestamp
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
// Getters, Setters, Constructors
}
@GeneratedValue Strategies
| Strategy | Description |
|---|---|
IDENTITY | Auto-increment by DB (MySQL, PostgreSQL serial) |
SEQUENCE | Uses DB sequence object (PostgreSQL default) |
TABLE | Uses a separate table to generate IDs (portable, slow) |
AUTO | JPA picks strategy based on DB (default) |
UUID | Generates UUID (Spring Data 3+) |
// UUID strategy example
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;
4. Entity Relationships
@OneToOne
@Entity
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "profile_id", referencedColumnName = "id")
private UserProfile profile;
}
@Entity
public class UserProfile {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String bio;
private String avatarUrl;
@OneToOne(mappedBy = "profile")
private User user;
}
@OneToMany / @ManyToOne (Most Common)
@Entity
public class Department {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Employee> employees = new ArrayList<>();
}
@Entity
public class Employee {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "department_id")
private Department department;
}
@ManyToMany
@Entity
public class Student {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
}
@Entity
public class Course {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();
}
5. Cascade Types
| CascadeType | Effect |
|---|---|
PERSIST | Save parent → also saves children |
MERGE | Update parent → also updates children |
REMOVE | Delete parent → also deletes children |
REFRESH | Refresh parent → also refreshes children |
DETACH | Detach parent → also detaches children |
ALL | All of the above |
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
orphanRemoval = true— deletes child records when removed from parent's collection.
6. Fetch Types
| FetchType | Behaviour |
|---|---|
LAZY (default for collections) | Data loaded only when accessed |
EAGER (default for single entities) | Data loaded immediately with parent |
// Always prefer LAZY for collections to avoid N+1 problems
@OneToMany(fetch = FetchType.LAZY)
private List<Order> orders;
N+1 Problem & Fix
Problem: Loading 100 employees fires 1 query for employees + 100 queries for their departments.
Fix — Use @EntityGraph or JOIN FETCH:
// In Repository
@EntityGraph(attributePaths = {"department"})
List<Employee> findAll();
// OR with JPQL
@Query("SELECT e FROM Employee e JOIN FETCH e.department")
List<Employee> findAllWithDepartment();
7. Spring Data Repositories
Repository Hierarchy
Repository (marker interface)
└── CrudRepository<T, ID> → Basic CRUD
└── PagingAndSortingRepository<T, ID> → + Pagination & Sorting
└── JpaRepository<T, ID> → + JPA-specific (flush, batch, etc.)
JpaRepository — Built-in Methods
public interface UserRepository extends JpaRepository<User, Long> { }
| Method | Description |
|---|---|
save(entity) | Insert or Update |
saveAll(entities) | Batch insert/update |
findById(id) | Returns Optional<T> |
findAll() | All records |
findAllById(ids) | Find by list of IDs |
existsById(id) | Check existence |
count() | Total count |
deleteById(id) | Delete by ID |
delete(entity) | Delete entity |
deleteAll() | Delete everything |
flush() | Force sync to DB |
saveAndFlush(entity) | Save + immediate flush |
findAll(Pageable) | Paginated results |
findAll(Sort) | Sorted results |
8. Derived Query Methods (Method Name Queries)
Spring generates SQL automatically from method names.
public interface UserRepository extends JpaRepository<User, Long> {
// SELECT * FROM users WHERE name = ?
List<User> findByName(String name);
// SELECT * FROM users WHERE email = ? AND active = ?
Optional<User> findByEmailAndActive(String email, boolean active);
// SELECT * FROM users WHERE name LIKE '%keyword%'
List<User> findByNameContaining(String keyword);
// SELECT * FROM users WHERE created_at BETWEEN ? AND ?
List<User> findByCreatedAtBetween(LocalDateTime start, LocalDateTime end);
// SELECT * FROM users WHERE age > ?
List<User> findByAgeGreaterThan(int age);
// SELECT * FROM users ORDER BY name ASC
List<User> findByActiveOrderByNameAsc(boolean active);
// SELECT COUNT(*) FROM users WHERE role = ?
long countByRole(Role role);
// DELETE FROM users WHERE active = false
void deleteByActive(boolean active);
// Check existence
boolean existsByEmail(String email);
}
Keyword Reference
| Keyword | Example | SQL |
|---|---|---|
And | findByNameAndEmail | WHERE name=? AND email=? |
Or | findByNameOrEmail | WHERE name=? OR email=? |
Is, Equals | findByNameIs | WHERE name=? |
Between | findByAgeBetween | WHERE age BETWEEN ? AND ? |
LessThan | findByAgeLessThan | WHERE age < ? |
GreaterThan | findByAgeGreaterThan | WHERE age > ? |
Like | findByNameLike | WHERE name LIKE ? |
Containing | findByNameContaining | WHERE name LIKE %?% |
StartingWith | findByNameStartingWith | WHERE name LIKE ?% |
EndingWith | findByNameEndingWith | WHERE name LIKE %? |
IsNull | findByEmailIsNull | WHERE email IS NULL |
IsNotNull | findByEmailIsNotNull | WHERE email IS NOT NULL |
In | findByRoleIn | WHERE role IN (?,?) |
NotIn | findByRoleNotIn | WHERE role NOT IN (?) |
True / False | findByActiveTrue | WHERE active = true |
OrderBy | findByNameOrderByAgeAsc | ORDER BY age ASC |
Top / First | findTop3ByActive | LIMIT 3 |
Distinct | findDistinctByName | SELECT DISTINCT ... |
9. Custom Queries
JPQL (Java Persistence Query Language)
// JPQL uses entity class names and field names (not table/column names)
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);
@Query("SELECT u FROM User u WHERE u.active = true ORDER BY u.name ASC")
List<User> findAllActiveUsers();
// JPQL with JOIN
@Query("SELECT u FROM User u JOIN FETCH u.orders WHERE u.id = :id")
Optional<User> findByIdWithOrders(@Param("id") Long id);
Native SQL Query
@Query(value = "SELECT * FROM users WHERE email = :email", nativeQuery = true)
Optional<User> findByEmailNative(@Param("email") String email);
// Native with pagination
@Query(
value = "SELECT * FROM users WHERE active = true",
countQuery = "SELECT COUNT(*) FROM users WHERE active = true",
nativeQuery = true
)
Page<User> findActiveUsers(Pageable pageable);
Modifying Queries (UPDATE / DELETE)
@Modifying
@Transactional
@Query("UPDATE User u SET u.active = false WHERE u.id = :id")
int deactivateUser(@Param("id") Long id);
@Modifying
@Transactional
@Query("DELETE FROM User u WHERE u.active = false")
void deleteInactiveUsers();
10. Pagination & Sorting
// Repository method
Page<User> findByActive(boolean active, Pageable pageable);
List<User> findByRole(Role role, Sort sort);
// Service usage
public Page<User> getUsers(int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("name").ascending());
return userRepository.findByActive(true, pageable);
}
Page Object Contents
Page<User> result = userRepository.findAll(pageable);
result.getContent(); // List of users on this page
result.getTotalElements(); // Total records in DB
result.getTotalPages(); // Total pages
result.getNumber(); // Current page number (0-based)
result.getSize(); // Page size
result.isFirst(); // Is this the first page?
result.isLast(); // Is this the last page?
result.hasNext(); // Has next page?
Sort Examples
Sort.by("name").ascending()
Sort.by("name").descending()
Sort.by("name").ascending().and(Sort.by("createdAt").descending())
// In Pageable
PageRequest.of(0, 10, Sort.by(Sort.Direction.DESC, "createdAt"))
11. Specifications (Dynamic Queries)
Use when query conditions are determined at runtime (filter APIs).
// Add to repository
public interface UserRepository extends JpaRepository<User, Long>,
JpaSpecificationExecutor<User> { }
// Define Specifications
public class UserSpecifications {
public static Specification<User> hasName(String name) {
return (root, query, cb) ->
name == null ? null : cb.equal(root.get("name"), name);
}
public static Specification<User> isActive(Boolean active) {
return (root, query, cb) ->
active == null ? null : cb.equal(root.get("active"), active);
}
public static Specification<User> hasRole(Role role) {
return (root, query, cb) ->
role == null ? null : cb.equal(root.get("role"), role);
}
}
// Service usage — combine dynamically
public List<User> searchUsers(String name, Boolean active, Role role) {
Specification<User> spec = Specification
.where(UserSpecifications.hasName(name))
.and(UserSpecifications.isActive(active))
.and(UserSpecifications.hasRole(role));
return userRepository.findAll(spec);
}
12. Transaction Management
@Transactional Attributes
@Transactional(
propagation = Propagation.REQUIRED, // default
isolation = Isolation.DEFAULT, // default
readOnly = false,
rollbackFor = Exception.class,
timeout = 30
)
public void processOrder(Order order) {
// All DB operations here run in one transaction
}
Propagation Types
| Propagation | Behaviour |
|---|---|
REQUIRED | Join existing or create new (default) |
REQUIRES_NEW | Always create new, suspend existing |
NESTED | Nested inside existing transaction |
SUPPORTS | Join if exists, else run without |
NOT_SUPPORTED | Suspend existing, run without transaction |
MANDATORY | Must run in existing, else throw exception |
NEVER | Must NOT run in transaction, else throw |
Isolation Levels
| Isolation | Prevents |
|---|---|
READ_UNCOMMITTED | Nothing (dirty reads allowed) |
READ_COMMITTED | Dirty reads |
REPEATABLE_READ | Dirty reads + non-repeatable reads |
SERIALIZABLE | All concurrency issues (slowest) |
// Read-only for SELECT queries (performance optimization)
@Transactional(readOnly = true)
public List<User> getAllUsers() {
return userRepository.findAll();
}
13. Auditing with Spring Data JPA
// Enable auditing in main class or config
@SpringBootApplication
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
public class MyApp { }
// Auditable base entity
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
@CreatedBy
@Column(updatable = false)
private String createdBy;
@LastModifiedBy
private String updatedBy;
}
// Extend in your entities
@Entity
public class User extends BaseEntity {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
// Provide current user
@Component
public class AuditorProvider implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
return Optional.of(
SecurityContextHolder.getContext()
.getAuthentication().getName()
);
}
}
14. Projections (Partial Data Fetching)
Interface Projection
public interface UserSummary {
String getName();
String getEmail();
}
// Repository
List<UserSummary> findByActive(boolean active);
Class-based (DTO) Projection
public record UserDTO(String name, String email) { }
// Repository
@Query("SELECT new com.example.dto.UserDTO(u.name, u.email) FROM User u WHERE u.active = true")
List<UserDTO> findActiveUserDTOs();
Dynamic Projection
<T> List<T> findByActive(boolean active, Class<T> type);
// Usage
List<UserSummary> summaries = repo.findByActive(true, UserSummary.class);
List<User> full = repo.findByActive(true, User.class);
15. Entity Lifecycle Callbacks
@Entity
public class User {
@PrePersist
public void prePersist() {
System.out.println("Before insert");
}
@PostPersist
public void postPersist() {
System.out.println("After insert");
}
@PreUpdate
public void preUpdate() {
System.out.println("Before update");
}
@PostUpdate
public void postUpdate() {
System.out.println("After update");
}
@PreRemove
public void preRemove() {
System.out.println("Before delete");
}
@PostRemove
public void postRemove() {
System.out.println("After delete");
}
@PostLoad
public void postLoad() {
System.out.println("After load from DB");
}
}
16. Soft Delete Pattern
@Entity
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Column(name = "deleted")
private boolean deleted = false;
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Automatically filter deleted records
@Query("SELECT u FROM User u WHERE u.deleted = false")
List<User> findAllActive();
// Soft delete
@Modifying
@Transactional
@Query("UPDATE User u SET u.deleted = true WHERE u.id = :id")
void softDelete(@Param("id") Long id);
}
For automatic filtering, use Hibernate's
@SQLRestriction(Hibernate 6+):
@Entity
@SQLRestriction("deleted = false")
public class User { ... }
17. Common Pitfalls & Best Practices
| Pitfall | Fix |
|---|---|
| N+1 queries | Use JOIN FETCH or @EntityGraph |
EAGER fetch on collections | Always use LAZY for collections |
CascadeType.ALL on @ManyToMany | Use only PERSIST + MERGE |
Missing @Transactional on @Modifying | Always add it |
| Loading full entities for read-only views | Use projections/DTOs |
Using create-drop in production | Use validate or none |
| Exposing entities directly in REST | Use DTOs |
Not using Optional with findById | Always unwrap with orElseThrow |
Interview Questions — Spring Data JPA
Beginner Level
Q1. What is JPA? What is Hibernate? How do they relate?
JPA (Java Persistence API) is a specification (set of interfaces/rules) for ORM in Java. Hibernate is the most popular implementation of JPA. Spring Boot uses Hibernate as the default JPA provider. Analogy: JPA = JDBC interface, Hibernate = MySQL JDBC Driver.
Q2. What is the difference between CrudRepository, JpaRepository, and PagingAndSortingRepository?
CrudRepository— basic CRUD operationsPagingAndSortingRepository— addsfindAll(Pageable)andfindAll(Sort)JpaRepository— extends both + adds JPA-specific methods likeflush(),saveAndFlush(),deleteInBatch()In practice, always extend
JpaRepositoryunless you want to limit the API surface.
Q3. What is the difference between save() and saveAndFlush()?
save()— persists to the persistence context (first-level cache), may not immediately hit DBsaveAndFlush()— saves AND immediately synchronizes with DB UsesaveAndFlush()when you need the DB to reflect changes right away (e.g., within the same transaction).
Q4. What does @Transactional do?
It wraps the method in a database transaction. If the method completes successfully, the transaction commits. If a
RuntimeExceptionis thrown, it rolls back automatically. Spring applies it via AOP (proxy-based), so it only works on public methods called from outside the bean.
Q5. What is Optional and why does findById() return it?
Optional<T>is a container that may or may not contain a value. It forces the caller to explicitly handle the "not found" case, avoidingNullPointerException.javaUser user = userRepository.findById(id) .orElseThrow(() -> new EntityNotFoundException("User not found: " + id));
Q6. What is the difference between @Entity and @Table?
@Entity— marks the class as a JPA entity (mandatory)@Table— optional, specifies the actual table name, schema, unique constraints If@Tableis omitted, the table name defaults to the class name.
Q7. What is ddl-auto and which value should be used in production?
Controls how Hibernate manages schema creation. Use
validateornonein production. Never usecreateorcreate-dropin production — they destroy data.
Intermediate Level
Q8. What is the N+1 problem? How do you fix it?
When fetching a list of entities and then accessing their lazy-loaded associations, JPA fires one query for the parent list + N queries (one per parent) for the children.
Fix:
java// JPQL JOIN FETCH @Query("SELECT d FROM Department d JOIN FETCH d.employees") List<Department> findAllWithEmployees(); // or @EntityGraph @EntityGraph(attributePaths = {"employees"}) List<Department> findAll();
Q9. What is the difference between LAZY and EAGER fetching? Which should you prefer?
LAZY— related data is loaded only when accessed (proxy object until then)EAGER— related data is loaded immediately with the parentAlways prefer
LAZYfor collections (@OneToMany,@ManyToMany).EAGERcauses unnecessary data loading and is the main cause of N+1 and performance issues.
Q10. What is orphanRemoval? How is it different from CascadeType.REMOVE?
CascadeType.REMOVE— deletes children when the parent is deletedorphanRemoval = true— deletes children when they are removed from the parent's collection (even if the parent is not deleted)javaparent.getChildren().remove(child); // triggers DELETE if orphanRemoval=true
Q11. What is the difference between @Query with JPQL and native query?
JPQL — queries against entity model (portable, DB-independent, uses class/field names) Native — raw SQL (DB-specific syntax,
nativeQuery = true) Prefer JPQL; use native queries for complex SQL not expressible in JPQL.
Q12. How does @Modifying work? Why is @Transactional required with it?
@Modifyingtells Spring Data that the@Queryis an UPDATE or DELETE (not a SELECT), so it should returnint(affected rows) orvoid.@Transactionalis required because write operations must run inside a transaction. Without it, Spring throwsTransactionRequiredException.
Q13. What is the difference between Propagation.REQUIRED and Propagation.REQUIRES_NEW?
REQUIRED— joins existing transaction if one exists; creates new if noneREQUIRES_NEW— always creates a new transaction, suspending the outer one. Used when you need the inner operation to commit independently (e.g., audit logging that must persist even if the outer transaction rolls back).
Q14. What is a Specification in Spring Data JPA? When would you use it?
Specification<T>is an interface to build dynamic, composable JPA Criteria queries. Use it when query conditions vary at runtime (e.g., a search API with optional filters). It avoids hardcoding manyfindByXAndYmethods.
Q15. What is a projection? What are the types?
A projection fetches only a subset of entity fields instead of the whole entity. Types:
- Interface projection — Spring generates a proxy implementing the interface
- Class-based / DTO projection — uses constructor expressions in JPQL
- Dynamic projection — caller passes
Class<T>to determine return type at runtime
Q16. What is @MappedSuperclass? How is it different from @Inheritance?
@MappedSuperclass— parent class whose fields are mapped into each child entity's own table. The parent itself has no table.@Inheritance— maps an entity class hierarchy to DB tables using strategies (SINGLE_TABLE, TABLE_PER_CLASS, JOINED).
Q17. What is First-Level Cache (L1) and Second-Level Cache (L2)?
L1 (Session Cache) — Hibernate's default per-transaction cache. Within a session/transaction, the same entity is not fetched from DB twice. L2 (Shared Cache) — Optional, spans multiple sessions. Configured with providers like EhCache or Redis.
java@Entity @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) // L2 cache public class Product { }
Advanced Level
Q18. How does Spring Data JPA's @Transactional work internally?
Spring wraps the bean in a JDK dynamic proxy (or CGLIB proxy). When a
@Transactionalmethod is called from outside the bean, the proxy intercepts the call:
- Opens a transaction (or joins existing)
- Invokes the actual method
- Commits on success, rolls back on
RuntimeExceptionSelf-invocation problem: Calling a
@Transactionalmethod from within the same class bypasses the proxy, so the transaction is NOT applied.
Q19. What is the difference between EntityManager.persist(), merge(), detach(), and remove()?
| Method | State Transition |
|---|---|
persist(entity) | Transient → Managed (INSERT on flush) |
merge(entity) | Detached → Managed copy returned (UPDATE on flush) |
detach(entity) | Managed → Detached (no longer tracked) |
remove(entity) | Managed → Removed (DELETE on flush) |
refresh(entity) | Overwrites entity with DB state |
Q20. How would you implement soft delete globally using Hibernate?
Use
@SQLRestriction(Hibernate 6+) /@Where(older Hibernate) on the entity so all queries automatically filter deleted records:java@Entity @SQLRestriction("deleted = false") public class User { private boolean deleted = false; }Override
deletein the repository with a custom@Modifyingupdate query. To query deleted records, usenativeQuery = true(which bypasses the restriction).
Q21. What is the Open Session in View anti-pattern?
OSIV keeps the Hibernate session open for the entire HTTP request, allowing lazy loading in the view layer. It is enabled by default in Spring Boot.
Problem: Database connections are held for too long; lazy loading in views leads to unexpected queries.
Best practice: Disable OSIV and use DTOs/projections to load all data in the service layer.
propertiesspring.jpa.open-in-view=false
Q22. How do you handle optimistic locking in JPA?
Use
@Versionon a numeric field. JPA checks this version on UPDATE; if another transaction already changed it,OptimisticLockExceptionis thrown.java@Entity public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private int stock; @Version private int version; // auto-incremented on every update }
Q23. What is the difference between @NamedQuery and @Query?
@NamedQuery— defined at the entity class level, compiled/validated at startup, slightly faster@Query— defined at the repository method level, more flexible and readableSpring Data JPA's
@Queryis preferred in modern applications.
Q24. How do you perform batch inserts in Spring Data JPA?
- Set
spring.jpa.properties.hibernate.jdbc.batch_size=50- Use
SEQUENCEorTABLEID generation (notIDENTITY— it disables batching in Hibernate)- Use
saveAll()and callflush()+clear()periodicallyjava@Transactional public void batchInsert(List<User> users) { for (int i = 0; i < users.size(); i++) { entityManager.persist(users.get(i)); if (i % 50 == 0) { entityManager.flush(); entityManager.clear(); } } }
Q25. When would you use EntityManager directly instead of a repository?
Use
EntityManagerdirectly when:
- Performing complex Criteria API queries
- Bulk operations with
flush()/clear()for batch processing- Dynamic query building not suited for Specifications
- Fine-grained control over entity state (persist, merge, detach)
java@PersistenceContext private EntityManager entityManager;
Read. Learn. Build. Repeat.