问题
I have some difficulties with Spring and Hibernate and lazy loading. There are a lot of questions and answers, but not really what i am looking for.
So lets say i have a Car
class. With an id
, name
and one to many relationships with doors
, windows
and wheels
. As they are oneToMany, they are default lazy loaded which is preferred because when i want to view the name of the car
i dont want to see the tires and stuff. This works in my case, using the default findOne()
methods of my repositories.
But when i want to view the tire pressure, i need to initialize the tires
relationship. I used to do that with Hibernate.initialize(car.getTires())
. This produces another SELECT
query for the database. Now I want to improve my database queries and select only the car
with the tires
but leave out the windows
and doors
. (Which is possible in MySQL with joins). The option to make them load eager is out of question because i dont always want to load the tires
relationship (I have some big relationships on my objects).
I tried the following approach:
@Query("SELECT c FROM Car c JOIN FETCH c.tires WHERE c.id = :id")
Car findOneWithTiresLoaded(@Param("id") Long id);
This does provide the right data. But after analyzing the object returned from the repository, i noticed all of the relationships are loaded. So this does not only return the car
with the tires
relationship, but also the doors
and windows
.
The following gives me the same output (with the moneToMany relationship loaded)
@Query("SELECT c FROM Car c WHERE id = :id")
Car findOneWithTiresLoaded(@Param("id") Long id);
Something that is not wanted. I expected this to output only the Car
object without all its lazy relationships.
Also suggested by people on the internet is to call Car.getTires().size()
. Which will also produce another SELECT
query.
Is there any way to just select the Car
with only the Tires
relation ship loaded? Without the fetch = FetchType.LAZY
, Hibernate.initialize()
or size()
method? How is it not possible to just join one table? Also, i do not use XML for any configuration.
Thanks!
回答1:
I would always suggest implementing this using entity graphs. I will be giving an example using Spring Data. Also lazy loading should always be used no matter what, all other relationships can be joined using specific graphs.
This way you can be very specific about your queries and ONLY fetch the data that is necessary for your business logic. You can even define sub-graphs to show what you want to select from tires
entities as well. That would mean you always have lazy fetches on all Tire
entity relationships. By default all you get is tires
(as requested) and no other relationships from them. If you also want anything else from tires
, then only thing left for you to do is define another set of graph definitions there and reference them from your repository where you make the query as sub-graphs.
@Entity
@Table(name = "car")
@NamedEntityGraph(name = Car.TIRES_GRAPH, attributeNodes = @NamedAttributeNode("tires"))
public class Car {
public static final String TIRES_GRAPH = "Car.tires";
@OneToMany(mappedBy = "car", fetch = FetchType.LAZY}
private Set<Tire> tires = new HashSet<>();
}
And for your repository you can have a method
@Query("SELECT c FROM Car c")
@EntityGraph(Car.TIRES_GRAPH)
Set<Car> findAllWithTires();
Even if you are not using Spring Data, then the approach is the same and you can easily find good examples of that.
EDIT
Another tested working example. Just make sure your field names match from the domain for Spring Data to resolve them.
public interface CarRepository extends JpaRepository<Car, Long> {
@EntityGraph(attributePaths = { "tires" })
Set<Car> findAllWithTiresByCarId(Long id)
}
Link to documentation
来源:https://stackoverflow.com/questions/40932584/hibernate-spring-jpa-load-only-specific-lazy-relationship