UNIT- IV JPA ASSOCIATIONS AND MAPPING
JPA Mapping of One-to-One Associations - fetching entities using queries – Loading optimization
technique - Two-way One-to-One Relationship Mapping with JPA - single entity instance
associated with multiple instances - Adding Data with One-to-One and One- to-Many
Associations using JPA.
MAPPING CONSTRAINTS (CARDINALITY)
Mapping refers to defining the relationships and correspondences between different data
structures or view levels, particularly through mapping constraints and external-conceptual-
internal mappings. Mapping constraints, also known as cardinality ratios, specify the number of
entities that can relate to each other, such as one-to-one (1:1), one-to-many (1:N), many-to-one
(M:1), and many-to-many (M:N). Mapping between view levels involves translating user
requests from an external view to a conceptual view, and then to an internal (physical) schema
to retrieve and store data.
Mapping constraints determine the nature and extent of the relationship between entities. These
are commonly defined by cardinality ratios:
• One-to-One (1:1):
Each instance of an entity relates to only one instance of another entity. For example, each
student may have one unique student ID.
• One-to-Many (1:N):
One instance of an entity can be related to multiple instances of another entity, while an instance
of the second entity relates to only one instance of the first. For example, one teacher can teach
many different classes.
• Many-to-One (M:1):
Multiple instances of one entity can relate to a single instance of another entity. For example,
many students might enroll in a single course.
• Many-to-Many (M:N):
Multiple instances of one entity can relate to multiple instances of another entity. For example,
many customers can purchase many different products.
1
1. One-to-One (1:1)
Each entity instance relates to exactly one other instance.
Student ──── 1 : 1 ──── StudentID
• One student has one unique student ID.
• One student ID belongs to only one student.
2. One-to-Many (1:N)
One entity relates to multiple instances of another.
Teacher ──── 1 : N ──── Class
• One teacher teaches many classes.
• Each class is taught by only one teacher.
3. Many-to-One (M:1)
Multiple entities relate back to one instance.
Student ──── N : 1 ──── Course
• Many students enroll in the same course.
• Each student belongs to only one course (in this example).
4. Many-to-Many (M:N)
Many instances of one entity relate to many instances of another.
Customer ──── M : N ──── Product
• One customer can purchase many products.
• One product can be purchased by many customers.
2
1. JPA MAPPING OF ONE-TO-ONE ASSOCIATIONS
Definition:
A one-to-one relationship is when one entity instance is associated with exactly one other entity
instance.
Example:
• User ↔ Profile
• Each user has exactly one profile, and each profile belongs to exactly one user.
3
1. Unidirectional One-to-One
Only one side knows about the relationship.
Example
Model Layer:
@Entity
public class User {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
private String username;
// User → Profile (owning side)
@OneToOne(cascade = [Link])
@JoinColumn(name = "profile_id") // foreign key in user table
private Profile profile;
// getters & setters
}
@Entity
public class Profile {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
private String address;
private String phone;
// No reference to User (unidirectional)
}
4
Repository Layer
import [Link];
// User Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
import [Link];
// Profile Repository (optional, only if you need direct Profile queries)
public interface ProfileRepository extends JpaRepository<Profile, Long> {
}
Service Layer
import [Link];
import [Link];
import [Link];
import [Link];
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
// Save user with profile
public User saveUser(User user) {
return [Link](user);
}
// Get all users
public List<User> getAllUsers() {
return [Link]();
}
// Get user by id
public Optional<User> getUserById(Long id) {
return [Link](id);
}
5
// Delete user
public void deleteUser(Long id) {
[Link](id);
}
}
Controller Layer
import [Link];
import [Link];
import [Link].*;
import [Link];
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
// Create User with Profile
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User savedUser = [Link](user);
return [Link](savedUser);
}
// Get all users
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
return [Link]([Link]());
}
// Get user by id
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
return [Link](id)
.map(ResponseEntity::ok)
.orElse([Link]().build());
}
6
// Delete user by id
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
[Link](id);
return [Link]().build();
}
}
Full Flow Recap
1. Entities (already done)
o User has @OneToOne with Profile.
o Profile has no reference back (unidirectional).
2. Repositories
o UserRepository (mainly used).
o ProfileRepository (optional, use if needed).
3. Service
o UserService handles save, get, delete.
4. Controller
o UserController exposes REST APIs.
Example Usage
Create User with Profile
POST /users
{
"username": "bob456",
"profile": {
"address": "Bangalore",
"phone": "9876501234"
}
}
Response
{
"id": 1,
"username": "bob456",
"profile": {
"id": 1,
"address": "Bangalore",
"phone": "9876501234"
}
}
7
Table Structure
• user → has profile_id column (FK).
• profile → no FK.
Database Tables
user
id username profile_id
1 bob456 1
profile
id address phone
1 Bangalore 9876501234
8
2. BIDIRECTIONAL ONE-TO-ONE
Both sides know about each other.
Here, both User and Profile know each other, but the owning side is Profile (since it holds the
user_id foreign key).
Entities
[Link]
import [Link].*;
@Entity
public class User {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
private String username;
// Inverse side
@OneToOne(mappedBy = "user", cascade = [Link])
private Profile profile;
// getters & setters
public Long getId() { return id; }
public void setId(Long id) { [Link] = id; }
public String getUsername() { return username; }
public void setUsername(String username) { [Link] = username; }
public Profile getProfile() { return profile; }
public void setProfile(Profile profile) { [Link] = profile; }
}
9
[Link]
import [Link].*;
@Entity
public class Profile {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
private String address;
private String phone;
// Owning side
@OneToOne
@JoinColumn(name = "user_id") // foreign key in profile table
private User user;
// getters & setters
public Long getId() { return id; }
public void setId(Long id) { [Link] = id; }
public String getAddress() { return address; }
public void setAddress(String address) { [Link] = address; }
public String getPhone() { return phone; }
public void setPhone(String phone) { [Link] = phone; }
public User getUser() { return user; }
public void setUser(User user) { [Link] = user; }
}
10
Repositories
import [Link];
public interface UserRepository extends JpaRepository<User, Long> { }
import [Link];
public interface ProfileRepository extends JpaRepository<Profile, Long> { }
Service
import [Link];
import [Link];
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User saveUser(User user) {
if ([Link]() != null) {
[Link]().setUser(user); // maintain bidirectional link
}
return [Link](user);
}
}
Controller
import [Link];
import [Link].*;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public User createUser(@RequestBody User user) {
return [Link](user);
}
}
11
Sample Input (POST /users)
{
"username": "bob123",
"profile": {
"address": "Bangalore",
"phone": "9876501111"
}
}
Sample Output
{
"id": 1,
"username": "bob123",
"profile": {
"id": 1,
"address": "Bangalore",
"phone": "9876501111",
"user": {
"id": 1,
"username": "bob123"
}
}
}
Table Structure
user
id username
1 bob123
profile
id address phone user_id
1 Bangalore 9876501111 1
12
4. Choosing Owning Side
• The owning side contains the @JoinColumn annotation.
• The inverse side uses mappedBy.
• Owning side decides how the relationship is managed in DB.
Summary
• Unidirectional → Only one entity holds the relationship (simpler, fewer joins).
• Bidirectional → Both entities reference each other (flexible, but more complex).
Comparison table for One-to-One JPA associations:
Feature Unidirectional Bidirectional
Only one entity knows the
Definition Both entities know the relationship
relationship
The side with @JoinColumn is owning;
Owning Side Entity with @JoinColumn
other uses mappedBy
Inverse Side None Uses mappedBy
Foreign Key
Owning entity table Owning entity table
Location
Optional, usually [Link] for
Cascade Optional, as needed
sync
Complexity Simple Medium
Simple one-to-one, When both sides need access,
Use Case
e.g., Employee → ParkingSpace e.g., User ↔ Profile
Key Tips
1. Use unidirectional if the reverse lookup isn’t needed.
2. Use bidirectional if both entities must reference each other.
13
2. FETCHING ENTITIES USING QUERIES
In JPA (Java Persistence API), you can fetch entities using different types of queries depending
on your requirement. JPA provides several ways to retrieve data from the database:
1. JPQL (Java Persistence Query Language) Queries
JPQL looks like SQL, but it works with entities and their fields (not directly with
tables/columns).
Example: Fetch all students
@Query("SELECT s FROM Student s")
List<Student> findAllStudents();
With condition:
@Query("SELECT s FROM Student s WHERE [Link] > :age")
List<Student> findStudentsOlderThan(@Param("age") int age);
2. Native SQL Queries
Sometimes you need database-specific queries. In that case, you can use native queries.
Example:
@Query(value = "SELECT * FROM students WHERE age > ?1", nativeQuery = true)
List<Student> findStudentsOlderThanNative(int age);
3. Derived Queries (Spring Data JPA)
Spring Data JPA allows you to fetch entities by defining method names according to a
convention.
Example:
List<Student> findByAgeGreaterThan(int age);
List<Student> findByName(String name);
14
4. Named Queries
Predefine the queries inside the entity class using annotations.
Entity:
@Entity
@NamedQuery(
name = "[Link]",
query = "SELECT s FROM Student s WHERE [Link] = :name"
)
public class Student {
@Id
private Long id;
private String name;
private int age;
}
Repository:
@Query(name = "[Link]")
List<Student> findByName(@Param("name") String name);
5. Using @Modifying with @Query
In Spring Data JPA, we annotate the repository method with @Modifying and @Query.
Example: Update
@Transactional
@Modifying
@Query("UPDATE Student s SET [Link] = :age WHERE [Link] = :id")
int updateStudentAge(@Param("id") Long id, @Param("age") int age);
@Modifying → tells Spring this is not a SELECT query.
@Transactional → required because modifying queries change the DB state.
int return type → number of rows updated.
Example: Delete
@Transactional
@Modifying
@Query("DELETE FROM Student s WHERE [Link] < :age")
int deleteStudentsYoungerThan(@Param("age") int age);
15
Summary:
• JPQL → Entity-based, portable.
• Native Queries → Raw SQL, DB-specific.
• Derived Queries → No query writing, method name-based.
• Named Queries → Predefined, reusable.
• Modifying queries → Updation/ Deletion.
3. LOADING OPTIMIZATION TECHNIQUE
In JPA, loading optimization techniques help reduce unnecessary database queries and improve
performance when fetching entities and their associations.
1. Lazy Loading ([Link])
• Associations are loaded only when accessed.
• Default for @OneToMany and @ManyToMany.
• Helps avoid loading large collections unnecessarily.
@Entity
class Student {
@OneToMany(mappedBy = "student", fetch = [Link])
private List<Course> courses; // Loaded only when accessed
}
2. Eager Loading ([Link])
• Associations are loaded immediately with the entity.
• Default for @ManyToOne and @OneToOne.
@Entity
class Profile {
@OneToOne(fetch = [Link])
private User user; // Loaded immediately
}
16
3. Entity Graphs
Define graphs to specify which associations to load eagerly.
@Entity
@NamedEntityGraph(
name = "[Link]",
attributeNodes = @NamedAttributeNode("courses")
)
class Student { ... }
Repository:
@EntityGraph(value = "[Link]")
Student findById(Long id);
4. Batch Fetching
Fetch multiple related entities in batches instead of one by one.
@OneToMany(mappedBy = "student")
@BatchSize(size = 10)
private List<Course> courses;
6. Pagination
Load data in chunks instead of fetching all records at once.
Page<Student> findAll(Pageable pageable);
Summary
• Lazy Loading → Load on demand (default for collections).
• Eager Loading → Load immediately (can be costly).
• Entity Graphs → Fine-grained control over fetching.
• Batch Fetching → Load associations in groups.
• Pagination → Fetch data in small chunks.
17
4. TWO-WAY ONE-TO-ONE RELATIONSHIP MAPPING WITH JPA
A full working example of a Two-way One-to-One Relationship Mapping in JPA, including:
• Entity Models (User & Profile)
• Repository Interfaces
• Controller with endpoints
• Sample Input & Output (JSON)
• Table Structure
Step 1: Model (Entities)
[Link]
import [Link];
import [Link].*;
@Entity
public class User {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
private String username;
@OneToOne(mappedBy = "user", cascade = [Link])
@JsonManagedReference // handles JSON recursion
private Profile profile;
// Getters & Setters
public Long getId() { return id; }
public void setId(Long id) { [Link] = id; }
public String getUsername() { return username; }
public void setUsername(String username) { [Link] = username; }
public Profile getProfile() { return profile; }
public void setProfile(Profile profile) {
[Link] = profile;
[Link](this); // keep bidirectional in sync
}
}
18
[Link]
import [Link];
import [Link].*;
@Entity
public class Profile {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
private String address;
private String phone;
@OneToOne
@JoinColumn(name = "user_id") // foreign key
@JsonBackReference
private User user;
// Getters & Setters
public Long getId() { return id; }
public void setId(Long id) { [Link] = id; }
public String getAddress() { return address; }
public void setAddress(String address) { [Link] = address; }
public String getPhone() { return phone; }
public void setPhone(String phone) { [Link] = phone; }
public User getUser() { return user; }
public void setUser(User user) { [Link] = user; }
}
19
Step 2: Repository Interfaces
[Link]
import [Link];
public interface UserRepository extends JpaRepository<User, Long> {
}
[Link]
import [Link];
public interface ProfileRepository extends JpaRepository<Profile, Long> {
}
Step 3: Controller
[Link]
import [Link].*;
import [Link];
@RestController
@RequestMapping("/users")
public class UserController {
private final UserRepository userRepository;
public UserController(UserRepository userRepository) {
[Link] = userRepository;
}
// Create a User with Profile
@PostMapping
public User createUser(@RequestBody User user) {
return [Link](user);
}
20
// Get all Users
@GetMapping
public List<User> getAllUsers() {
return [Link]();
}
// Get single User by ID
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return [Link](id).orElseThrow();
}
}
Step 4: Table Structure (Generated by JPA)
User Table
id Username
1 alice
2 bob
3 charlie
Profile Table
id address phone user_id
1 Chennai 9876543210 1
2 Bangalore 8765432109 2
3 Delhi 7654321098 3
Here:
• Each User has exactly one Profile.
• Each Profile points back to exactly one User (user_id is a foreign key).
21
Step 5: Sample Input & Output
Input (POST /users)
{
"username": "alice",
"profile": {
"address": "Chennai",
"phone": "9876543210"
}
}
Output (Response)
{
"id": 1,
"username": "alice",
"profile": {
"id": 1,
"address": "Chennai",
"phone": "9876543210"
}
}
Fetch All Users (GET /users)
[
{
"id": 1,
"username": "alice",
"profile": {
"id": 1,
"address": "Chennai",
"phone": "9876543210"
}
}
]
Summary
• Owning side: Profile (has @JoinColumn).
• Inverse side: User (uses mappedBy = "user").
• Cascade ensures when we save User, Profile is also saved.
• @JsonManagedReference + @JsonBackReference prevents infinite recursion in JSON.
22
5. SINGLE ENTITY INSTANCE ASSOCIATED WITH MULTIPLE INSTANCES
One-to-Many relationship in JPA: a single entity instance is associated with multiple
instances of another entity. Let’s break it down with a simple example.
Concept: One-to-Many
• One-to-Many → One parent entity relates to many child entities.
• Example: One Author can write many Books.
Step 1: Entity Classes
[Link]
import [Link].*;
import [Link];
@Entity
public class Author {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
private String name;
// One Author → Many Books
@OneToMany(mappedBy = "author", cascade = [Link])
private List<Book> books;
// Getters & Setters
public Long getId() { return id; }
public void setId(Long id) { [Link] = id; }
public String getName() { return name; }
public void setName(String name) { [Link] = name; }
public List<Book> getBooks() { return books; }
public void setBooks(List<Book> books) {
[Link] = books;
for (Book b : books) {
[Link](this); // maintain bidirectional relationship
}
}
}
23
[Link]
import [Link].*;
@Entity
public class Book {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
private String title;
// Many Books → One Author
@ManyToOne
@JoinColumn(name = "author_id")
private Author author;
// Getters & Setters
public Long getId() { return id; }
public void setId(Long id) { [Link] = id; }
public String getTitle() { return title; }
public void setTitle(String title) { [Link] = title; }
public Author getAuthor() { return author; }
public void setAuthor(Author author) { [Link] = author; }
}
24
Step 2: Table Structure (Auto-generated by JPA)
Author Table
id name
1 J.K. Rowling
2 George R.R. Martin
Book Table
id Title author_id
1 Harry Potter 1 1
2 Harry Potter 2 1
3 A Game of Thrones 2
4 A Clash of Kings 2
Step 3: Sample Input (JSON to create Author with Books)
{
"name": "J.K. Rowling",
"books": [
{"title": "Harry Potter 1"},
{"title": "Harry Potter 2"}
]
}
Output (Saved Author with Books)
{
"id": 1,
"name": "J.K. Rowling",
"books": [
{"id": 1, "title": "Harry Potter 1"},
{"id": 2, "title": "Harry Potter 2"}
]
}
Summary
• One-to-Many → One parent entity, multiple child entities.
• Bidirectional → Use mappedBy in parent (@OneToMany) and @ManyToOne in child.
• Cascading helps save child entities when parent is saved.
25
6. ADDING DATA WITH ONE-TO-ONE AND ONE- TO-MANY ASSOCIATIONS
USING JPA.
Step by step on how to add data in JPA when you have One-to-One and One-to-Many
associations. I’ll use simple examples for both cases.
1. Adding Data in One-to-One Association
Scenario
• User ↔ Profile (bidirectional one-to-one)
Entities (simplified)
@Entity
class User {
@Id @GeneratedValue
private Long id;
private String username;
@OneToOne(mappedBy = "user", cascade = [Link])
private Profile profile;
// getters & setters
public void setProfile(Profile profile) {
[Link] = profile;
[Link](this); // sync both sides
}
}
@Entity
class Profile {
@Id @GeneratedValue
private Long id;
private String address;
@OneToOne
@JoinColumn(name = "user_id")
private User user;
}
26
Adding Data
User user = new User();
[Link]("alice");
Profile profile = new Profile();
[Link]("Chennai");
// set bidirectional association
[Link](profile);
// Save user → profile will also be saved due to [Link]
[Link](user);
Tables after insert
User
id username
1 alice
Profile
id address user_id
1 Chennai 1
27
2. Adding Data in One-to-Many Association
Scenario
• Author ↔ Book (one author has many books)
Entities (simplified)
@Entity
class Author {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "author", cascade = [Link])
private List<Book> books;
public void setBooks(List<Book> books) {
[Link] = books;
for (Book b : books) {
[Link](this); // sync both sides
}
}
}
@Entity
class Book {
@Id @GeneratedValue
private Long id;
private String title;
@ManyToOne
@JoinColumn(name = "author_id")
private Author author;
}
28
Adding Data
Author author = new Author();
[Link]("J.K. Rowling");
Book b1 = new Book();
[Link]("Harry Potter 1");
Book b2 = new Book();
[Link]("Harry Potter 2");
[Link]([Link](b1, b2));
// Save author → books will also be saved due to cascade
[Link](author);
Tables after insert
Author
id name
1 J.K. Rowling
Book
Id title author_id
1 Harry Potter 1 1
2 Harry Potter 2 1
Key Points
1. [Link] ensures child entities are automatically saved when the parent is
saved.
2. Bidirectional associations must be kept in sync in code ([Link]() or
[Link]()).
3. One-to-One → single child per parent
4. One-to-Many → multiple children per parent.
29