What is JPA?
Java Persistence API (JPA) is a specification for managing relational data in Java applications. It provides an abstraction layer for object-relational mapping (ORM) and allows developers to interact with databases using Java objects rather than writing SQL queries manually.
1. Key Features of JPA
- ORM (Object-Relational Mapping): Maps Java classes to database tables.
- Annotations-Based Configuration: Uses annotations like
@Entity,@Table, and@Columnto define mappings. - Entity Lifecycle Management: Supports
persist,merge,remove, andrefreshoperations. - Query Language (JPQL): Allows querying using
JPQL(Java Persistence Query Language) instead of raw SQL. - Transaction Management: Works with transactions using
@TransactionalandEntityManager. - Caching Mechanism: Supports first-level and second-level caching to optimize performance.
- Lazy and Eager Loading: Provides control over when data is fetched.
- Event Listeners & Callbacks: Supports lifecycle hooks like
@PrePersist,@PostPersistfor auditing.
2. JPA Components
a. Entity
Entities are Java objects representing database tables.
@Entity
@Table(name = "employees")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "salary")
private Double salary;
}
b. EntityManager
The EntityManager is used to interact with the database.
@PersistenceContext
private EntityManager entityManager;
public void saveEmployee(Employee employee) {
entityManager.persist(employee);
}
c. JPQL (Java Persistence Query Language)
JPQL allows writing SQL-like queries but operates on entity objects.
TypedQuery<Employee> query = entityManager.createQuery("SELECT e FROM Employee e WHERE e.salary > :salary", Employee.class);
query.setParameter("salary", 50000);
List<Employee> results = query.getResultList();
3. JPA Relationships & When to Use @JoinTable and mappedBy
JPA supports various relationships between entities:
a. One-to-One Relationship
@Entity
public class Employee {
@Id @GeneratedValue
private Long id;
@OneToOne(mappedBy = "employee")
private Address address;
}
When to Use mappedBy:
- Use
mappedByin the non-owning side of the relationship to indicate that the mapping is already defined by the other entity. - Helps avoid unnecessary join tables and duplication.
b. One-to-Many Relationship
@Entity
public class Department {
@Id @GeneratedValue
private Long id;
@OneToMany(mappedBy = "department")
private List<Employee> employees;
}
When to Use mappedBy:
- Use
mappedByin the non-owning side (Employeeshould have@ManyToOne private Department department;). - Reduces unnecessary join tables.
c. Many-to-Many Relationship
@Entity
public class Student {
@Id @GeneratedValue
private Long id;
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private List<Course> courses;
}
When to Use @JoinTable:
- Used in
@ManyToManyrelationships to define a custom join table. - Necessary when JPA does not generate an intermediary table automatically.
4. JPA Transactions
JPA transactions are managed using @Transactional in Spring Boot or manually using EntityManager.
@Transactional
public void updateSalary(Long employeeId, Double newSalary) {
Employee emp = entityManager.find(Employee.class, employeeId);
emp.setSalary(newSalary);
}
5. JPA Best Practices
- Use DTOs for large queries instead of returning entity objects.
- Avoid N+1 query problems by using
@EntityGraphorJOIN FETCH. - Use caching effectively to reduce database load.
- Use batch processing for bulk inserts and updates.
6. Common JPA Interview Concepts with Examples
What are the different states of an entity in JPA?
JPA defines four entity states:
- New (Transient): Object is created but not yet persisted in the database.
- Managed: Object is associated with an active persistence context.
- Detached: Object was previously persisted but is now out of the persistence context.
- Removed: Object is marked for deletion but not yet deleted.
Example:
Employee emp = new Employee(); // New state entityManager.persist(emp); // Managed state entityManager.detach(emp); // Detached state entityManager.remove(emp); // Removed state
How does JPA handle concurrency?
JPA supports two types of locking:
- Optimistic Locking (
@Version): Prevents lost updates using a version field. - Pessimistic Locking (
PESSIMISTIC_WRITE): Prevents concurrent updates by locking the record.
Example:
@Entity
public class Product {
@Id
@GeneratedValue
private Long id;
@Version
private int version;
}
How can you improve JPA query performance?
- Use indexes on frequently queried columns.
- Enable second-level caching.
- Use batch fetching to reduce query count.
What is the difference between merge() and persist()?
persist(): Saves a new entity and makes it managed.merge(): Updates an existing entity or creates a new one if not found.
Example:
Employee emp = new Employee();
entityManager.persist(emp); // Saved in database
emp.setName("Updated");
entityManager.merge(emp); // Updates the entity
How can you handle database migrations with JPA?
- Use Flyway or Liquibase to manage schema changes.
- Example Flyway migration script:
CREATE TABLE employees (
id BIGINT PRIMARY KEY,
name VARCHAR(255),
salary DECIMAL(10,2)
);