Exercise 03: One-to-One Relationship
Learning Objective
Learn how to implement a bidirectional @OneToOne relationship between Movie and MovieDetails entities.
Background
A movie has exactly one set of details (plot, budget, runtime, production company), and those details belong to exactly one movie. This is a one-to-one relationship.
In a bidirectional relationship, both entities reference each other. One side must be marked as the owner of the relationship (the side with the foreign key).
Your Task
Create a bidirectional one-to-one relationship between Movie and MovieDetails, then add an endpoint to attach details to a movie.
Step 1: Add Relationship to Movie Entity
Modify Movie.java to reference MovieDetails:
@Entity
public class Movie {
// ... existing fields (id, title, releaseYear, genre, rating, actors)
// TODO: Add @OneToOne relationship
// Use @JoinColumn(name = "movie_details_id")
// This makes Movie the owner of the relationship
// TODO: Add getter and setter for movieDetails
}
Step 2: Add Reverse Relationship to MovieDetails Entity
Modify MovieDetails.java to reference back to Movie:
@Entity
public class MovieDetails {
// ... existing fields (id, plot, budget, runtime, productionCompany)
// TODO: Add @OneToOne(mappedBy = "movieDetails")
// mappedBy indicates this is NOT the owner side
// TODO: Add getter and setter for movie
}
Step 3: Add Method to MovieService
Add a method to attach details to a movie:
public Movie addDetailsToMovie(Long movieId, MovieDetails details) {
// TODO: Find the movie by ID
// TODO: Save the details first using MovieDetailsRepository
// TODO: Set the details on the movie
// TODO: Set the movie reference on details (for bidirectional consistency)
// TODO: Save and return the updated movie
}
Hint: You'll need to inject MovieDetailsRepository into the service.
Step 4: Add Controller Endpoint
Add an endpoint to MovieController.java:
@PostMapping("/{id}/details")
public ResponseEntity<Movie> addDetailsToMovie(
@PathVariable Long id,
@RequestBody MovieDetails details) {
// TODO: Call the service method
// TODO: Return the updated movie
}
Step 4: Use @JsonManagedReference and @JsonBackReference
To prevent infinite recursion during JSON serialization, modify the Movie and MovieDetails entities, adding @JsonManagedReference and @JsonBackReference annotations.
Testing Your Implementation
-
Restart your application
-
Create a movie (using .http in IntelliJ):
### ADD MOVIEPOST http://localhost:8080/api/moviesContent-Type: application/json{"title": "The Dark Knight","releaseYear": 2008,"genre": "Action","rating": {"score": 9.0, "voteCount": 2500000}} -
Add details to the movie:
### ADD MOVIE DETAILSPOST http://localhost:8080/api/movies/1/detailsContent-Type: application/json{"plot": "Batman faces the Joker in a battle for Gotham soul.","budget": 185000000,"runtime": 152,"productionCompany": "Warner Bros"} -
Get the movie and verify it includes the details:
### VERIFY MOVIE WITH DETAILSGET http://localhost:8080/api/movies/1 -
Check the H2 console:
- Verify the
MOVIEtable has amovie_details_idforeign key column - Verify the
MOVIE_DETAILStable does NOT have a foreign key toMOVIE
- Verify the
Expected Result
The movie JSON should include the movieDetails object. The foreign key should be in the MOVIE table (owner side), not in the MOVIE_DETAILS table.
Questions to Consider
- Which entity is the owner of the relationship? How can you tell?
- Why do we need
mappedByin theMovieDetailsentity? - What happens if you try to save a movie with details without saving the details first?
Continue to Exercise 04 to learn about cascading operations!