Home > front end >  Is there a way to skip second level entity associations when fetching data into projections using JP
Is there a way to skip second level entity associations when fetching data into projections using JP

Time:06-08

Having entities structured in a tree with 3 levels I would like to fetch a list of projections of top level entity that include some properties of mid level entities, but skip fetching bottom level entities. I have a setup like one below:

@Entity
class TopLevelEntity {

    @EmbeddedId
    private TopLevelEntityId id;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "mid_level_entity_id")
    private MidLevelEntity midLevelEntity;

    @Column(name = "some_number")
    private Integer someNumber;

}

@Entity
class MidLevelEntity {

    @EmbeddedId
    private MidLevelEntityId id;

    @OneToOne(mappedBy = "midLevelEntity", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private BottomLevelEntityId bottomLevelEntityId;

}

@Entity
class BottomLevelEntity {

    @EmbeddedId
    private BottomLevelEntityId id;

    @OneToOne
    @JoinColumn(name = "mid_level_entity_id")
    private MidLevelEntityId midLevelEntityId;

}

interface TopLevelEntityProjection {

    TopLevelEntityId getTopLevelEntityId();

    @Value("#{target.midLevelEntity?.id}")
    MidLevelEntityId getMidLevelEntityId();

}

@Repository
public interface TopLevelEntityRepository extends JpaRepository<TopLevelEntity, TopLevelEntityId> {

    @EntityGraph(attributePaths = {"midLevelEntity.id"}, type = EntityGraph.EntityGraphType.FETCH)
    @Query("select e from TopLevelEntity e where e.someNumber > :someNumber")
    Page<TopLevelEntityProjection> findTopLevelEntitiesWithSomeNumberGreaterThanSomeOtherNumber(Integer someNumber, Pageable pageable);

}

First query that is being executed is fine -- it fetches top and mid level entities. However there is also a query for each of the bottom level entities -- they are lazily loaded even though they are not really used for anything. We could of course eagerly load bottom level entities to avoid multiple queries, but is there a way to skip those altogether?

Edit:

I can now see that this question can be simplified: same thing happens if I query MidLevelEntities directly. Despite projection not using BottomLevelEntities they are being lazily loaded anyway. At first I though that maybe that are accessed during some equals/hashCode call, but when I overriden the methods and setup a breakpoints in them, they were not called.

CodePudding user response:

Hibernate ORM always fetch @OneToOne associations because it needs to know if the association is null or it needs to return an object (the proxy when the association is lazy). Using FetchType.LAZY doesn't change this behaviour.

I think there are three possible approaches to this:

  • If you know that the association always exists, you can use @JoinColumn(nullable=false). This way Hibernate ORM doesn't have to check and it will fetch the entity lazily

  • Use a unidirectional one-to-one with @MapsId:

    @Entity
    class TopLevelEntity {
    
      @EmbeddedId
      private TopLevelEntityId id;
    
      @Column(name = "some_number")
      private Integer someNumber;
    
    }
    
    
    
    @Entity
    class MidLevelEntity {
    
      @EmbeddedId
      private MidLevelEntityId id;
    
      @OneToOne(fetch = FetchType.LAZY)
      @MapsId
      @JoinColumn(name = "top_level_entity_id")
      private TopLevelEntity topLevelEntity;
    }
    
  • You can enable bytecode enhancements and use the annotation @LazyToOne

You can find more info for these use cases in the Hibernate ORM documentation: Bidirectional one-to-one lazy association

  • Related