Arndt Kubosch
Arndt Kubosch

Reputation: 41

How can I force Hibernate to use joins to fetch data for instances

I have a Spring Boot application using Hibernate as JPA provider. My application has two entities connected with a @OneToMany / @ManyToOne relation. The relation is annotated with @Fetch(FetchMode.JOIN) on both directions, and fetch = FetchType.EAGER.

My entities are called Car and Driver:

@Entity
@Table(name = "car")
@Data
public class Car implements Serializable, Cloneable {
   @Id
   @GenericGenerator(name = "car_seq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = {
   @Parameter(name = "sequence_name", value = "car_seq") })
   @GeneratedValue(generator = "car_seq")
   private Integer id;
    
   @OneToMany(mappedBy = "car", fetch = FetchType.EAGER)
   @Fetch(FetchMode.JOIN)
   private List<Driver> drivers = new ArrayList<>();
    
   @Column(name = "license_no", nullable = false)
   private String licenseNo;
}

@Entity
@Table(name = "driver")
@Data
public class Driver implements Serializable, Cloneable {
   @Id
   @GenericGenerator(name = "driver_seq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = {
   @Parameter(name = "sequence_name", value = "driver_seq") })
   @GeneratedValue(generator = "driver_seq")
   private Integer id;
    
   @ManyToOne(fetch = FetchType.EAGER)
   @JoinColumn(name = "car_id", nullable = true)
   @Fetch(FetchMode.JOIN)
   private Car car;
    
   @Column(name = "name", nullable = false)
   private String name;
}

When selecting a care (e.g. by calling Car.findById()), Hibernate joins the the two tables in a single SQL, and returns a Car object with a list of Drivers.

But if I select a single driver, Hibernate will join the Driver and Car table to give me the Driver object with the Car property populated, but it will run a second query to fetch all the driver objects on for the list on the car object.

For performance reasons I would like all the involved objects to be fetched in a single query, as is the case when I fetch a car. But I cannot find a way to make Hibernate do this. There is a property, hibernate.max_fetch_depth, which is supposed to do this, but I have found that it only affects the behavior of fetching a car, not when I fetch a driver.

I know I can use an EntityGraph to control the fetching, and by using an EntityGraph I have successfully retrieved a driver object with its car and all the car's drivers in one query. But to do that, I have to explicitly use a graph when retrieving the object, and I cannot do that in all the various cases where a Car object is needed. There are lots of other entities that have a relation to Car, and I don't want to write an EntityGraph for each and every one of those.

So is there a way to tell Hibernate how you want the fetching to be done by default on an entity? I would have thought that the annotations would be enough, but it seems that there either has to be something more, or that this simply cannot be done.

Arndt

Upvotes: 4

Views: 1018

Answers (1)

Oleksii Valuiskyi
Oleksii Valuiskyi

Reputation: 2851

FetchType.EAGER is one of the most common reasons for performance problems. You should use

@OneToMany(mappedBy = "car")
private List<Driver> drivers = new ArrayList<>();

And fetch drivers If needed

SELECT c FROM Car c JOIN FETCH c.drivers

Upvotes: 3

Related Questions