Complete and Categorized JPA Notes with Detailed Explanation and Code Examples
🧩 1. Overview of JPA
- JPA (Java Persistence API) is a specification to manage relational data in Java applications.
- It provides a bridge between the object-oriented domain model and the relational database.
- Common implementations: Hibernate, EclipseLink, OpenJPA.
🏷️ 2. Entity Annotations
| Annotation | Purpose |
|---|---|
@Entity | Marks a class as a JPA entity |
@Table(name = "table_name") | Optional table name |
@Id | Primary key |
@GeneratedValue(strategy = GenerationType.AUTO) | Auto generation of primary key |
@Column(name = "col_name") | Customize column mapping |
@Transient | Field not persisted |
@Lob | For large binary/text data (e.g., BLOB) |
@Embedded | For embeddable classes |
@JoinColumn(name = "col") | Foreign key column for relationships |
🔗 3. Entity Relationships
One-to-One
Example: A user has exactly one profile. Both share a one-to-one relationship.
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String username;
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "profile_id") // foreign key in User table
private Profile profile;
}
@Entity
public class Profile {
@Id
@GeneratedValue
private Long id;
private String address;
private String phone;
}
One-to-Many
Example: A user can place many orders. This is a one-to-many relationship.
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String username;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Order> orders = new ArrayList<>();
}
@Entity
public class Order {
@Id
@GeneratedValue
private Long id;
private String item;
@ManyToOne
@JoinColumn(name = "user_id") // foreign key in Order table
private User user;
}
Many-to-One
Example: Each order is placed by one user — a many-to-one mapping (inverse of one-to-many).
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
Many-to-Many
Example: A user can have multiple roles and a role can belong to multiple users.
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String username;
@ManyToMany
@JoinTable(
name = "user_role",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
}
@Entity
public class Role {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<>();
}
🔍 4. Queries
JPQL
@Query("SELECT u FROM User u WHERE u.status = 1")
List<User> findAllActiveUsers();
Native Query
@Query(value = "SELECT * FROM users WHERE status = 1", nativeQuery = true)
List<User> findAllActiveUsersNative();
Named Query
@Entity
@NamedQuery(name = "User.findByEmail", query = "SELECT u FROM User u WHERE u.email = ?1")
public class User {}
📚 5. Pagination & Sorting
Page<User> findAll(Pageable pageable);
List<User> findAll(Sort sort);
@Query("SELECT u FROM User u ORDER BY id")
Page<User> findAllWithPagination(Pageable pageable);
Example usage:
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
private String email;
private int age;
}
//in repository
public interface UserRepository extends JpaRepository<User, Long> {
// Custom with pagination and sorting
Page<User> findByAgeGreaterThan(int age, Pageable pageable);
// Custom sorting using derived query
List<User> findByNameContaining(String keyword, Sort sort);
}
//in service / controller
@Autowired
private UserRepository userRepository;
public Page<User> getUsersPaged(int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return userRepository.findAll(pageable);
}
//pagination with sorting
public Page<User> getSortedUsersPaged(int page, int size, String sortBy, boolean desc) {
Sort sort = desc ? Sort.by(sortBy).descending() : Sort.by(sortBy).ascending();
Pageable pageable = PageRequest.of(page, size, sort);
return userRepository.findAll(pageable);
}
//example controller
@GetMapping("/users")
public Page<User> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "5") int size,
@RequestParam(defaultValue = "name") String sortBy,
@RequestParam(defaultValue = "false") boolean desc) {
Sort sort = desc ? Sort.by(sortBy).descending() : Sort.by(sortBy).ascending();
Pageable pageable = PageRequest.of(page, size, sort);
return userRepository.findAll(pageable);
}
// sample api call
GET /users?page=0&size=5 → First 5 users
GET /users?page=1&size=5&sortBy=age&desc=true → Second page sorted by age in descending order
🧠 6. Common Query Keywords
| Keyword | Example |
|---|---|
And | findByFirstNameAndLastName |
Or | findByAgeOrName |
Between | findByAgeBetween(20, 30) |
LessThan | findByAgeLessThan(18) |
Like | findByEmailLike("%gmail.com") |
StartingWith | findByNameStartingWith("A") |
In | findByIdIn(List<Long> ids) |
IsNull | findByEmailIsNull() |
True/False | findByActiveTrue() |
🛠️ 7. Modifying Queries (Update, Delete)
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.name = :name")
int updateStatus(@Param("status") Integer status, @Param("name") String name);
🔁 8. Entity Graphs (For Performance Tuning)
@Entity
@NamedEntityGraph(name = "Group.detail", attributeNodes = @NamedAttributeNode("members"))
public class Group {
@ManyToMany
List<Member> members;
}
Repository usage:
@EntityGraph(value = "Group.detail", type = EntityGraph.EntityGraphType.LOAD)
Group findByName(String name);
🚀 9. Cascade Types
| CascadeType | Description |
|---|---|
PERSIST | Child saved automatically |
MERGE | Merge changes to children |
REMOVE | Delete children too |
REFRESH | Refresh child state |
DETACH | Detach child from session |
ALL | Applies all of the above |
@OneToMany(cascade = CascadeType.ALL)
private List<Order> orders;
🗃️ 10. Fetch Types
| Type | Description |
|---|---|
| LAZY | Data loaded on-demand (recommended) |
| EAGER | Data loaded immediately |
@OneToMany(fetch = FetchType.LAZY)
private List<Order> orders;
🔢 11. Indexed and Named Parameters
Indexed
@Query("SELECT u FROM User u WHERE u.status = ?1 AND u.name = ?2")
User findByStatusAndName(Integer status, String name);
Named
@Query("SELECT u FROM User u WHERE u.status = :status AND u.name = :name")
User findByStatusAndNameNamed(@Param("status") Integer status, @Param("name") String name);
🆔 12. Natural IDs and UUIDs
Natural ID (e.g., Email, SSN)
@NaturalId
@Column(unique = true)
private String email;
UUID as Primary Key
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
📌 13. Additional Best Practices
- Use
@Repositoryto enable Spring Data exception translation. - Prefer
DTOsover exposing entities in REST responses. - Use
@EntityGraphfor optimizing performance instead of EAGER fetch.
