Reputation: 7058
I have the following entities, and a CrudRepository
for each:
@Entity
class Movie {
@Id Long id;
@Column String name;
@ManyToOne Person director;
}
@Entity
class Person {
@Id Long id;
@Column String name;
}
My controller looks like this:
@RestController
@RequestMapping("/movies")
class MovieController {
private MovieRepository movies = ...;
private PersonRepository people = ...;
@PostMapping
public Movie create(@RequestBody MovieRequest request) {
// Get the director
Person director = people.findById(request.directorId);
// Create the new movie
Movie movie = new Movie();
movie.name = request.name;
movie.director = director;
// Save the new movie
return movies.save(movie);
}
}
class MovieRequest {
String name;
Long directorId
}
As you can see, the create
method first loads the director by its id, then creates the new movie and finally saves it. This causes two trips to the database: the first one to retrieve the director and the second one to save the movie.
In this case it's not a big problem, but there could be an entity with lots of relations, which means potentially doing lots of queries to achieve a single insert.
Question: I would like to save the new movie in a singe database operation. Is there a way to avoid the initial person query? Is there a better way to handle cases like this?
Upvotes: 3
Views: 1528
Reputation: 12938
I don't think your MOVIE
table contains a 'DIRECTOR_NAME
' column (Assuming you follow 2nd rule of normalization). It should be only DIRECTOR_ID
.
So you can totally skip loading the director name in your scenario (provided that directId is being sent with the query parameters of the request).
Since you have(should have) a foreign key constraining between Movie.DIRECTOR_ID
and DIRECTOR.ID
, it will look after any constraint violation if you are trying to insert any DIRECTOR_ID
that doesn't exist. So you don't need to worry.
Upvotes: 0
Reputation: 720
It will be ugly but you have personId in your request, so you can map you Movie with you long personId
class Movie {
@Id Long id;
@Column String name;
@ManyToOne Person director;
@Column(name="PERSON_ID")
long personId;
}
in your controller
movie.setPersonId(request.directorId);
Upvotes: 0
Reputation: 689
There's no way to tell the code wich Person
it needs to relates to your new Movie
. So you really need to perform a query and manually make the association.
There is an alternative that would only be possible if your endpoint create the Person
at the same time it creates the Movie
. Then you could simply perform the 2 save actions or use a CascadeType=ALL
to make a single save action.
If you're able to change your request parameters, might be a good choice to receive a complete Person
object instead of accpeting a directorId
. This way you could just make the association movie.director = director;
.
Be careful with this kinf of approach: if the received Person
object is not stored in your database, you'll get an Exception.
Maybe you could create a cache for your Directors
. If you have all your Directors saved in a Redis for example, you could search the Director
corresponding to the received directorId
an then perform the association.
Of course you would still need to make a second operation, but it might be way cheaper than query the database.
Upvotes: 1