Hibernate & Spring Boot with GraphQL - Only Load Selected Values

1.1k Views Asked by At

I'm currently using GraphQL SPQR with Spring Boot. In order to improve database performance I have written custom logic using the @GraphQLEnvironment ResolutionEnvironment from this I have been able to find all the requested values and filter them for the @Entity annotation, I have then used these to create a dynamic EntityGraph that is used to Eager fetch those specific values.

Now, this seems to work for Entities mapped as @OneToMany however my @ManyToOne and @OneToOne Entities still make separate Hibernate SQL queries even if they are not selected (When they are selected they are Eager fetched as a Join).

I have tried lots of different things such as Hibernate Enhanced Bytecode and using LazyInitilization which does stop those entities being loaded - however when they are selected it seems to make a Join and a separate select query too.

Hibernate: 
    /* select
        generatedAlias0 
    from
        OwningJob as generatedAlias0 
    where
        generatedAlias0.jobNumber=:param0 */ select
            owningjob0_.pkId as pkid1_13_0_,
            customer1_.customerPkId as customer1_0_1_,
            owningjob0_.customerCode as customer7_13_0_,
            ...
        from
            dbo.OwningJob owningjob0_ 
        left outer join
            dbo.Customer customer1_ 
                on owningjob0_.CustomerCode=customer1_.customerId 
        where
            owningjob0_.OwningJobId=?
Hibernate: 
    /* sequential select
        uk.co.essl.jobloadapi.model.jobapi.OwningJob */ select
            owningjob_.CustomerCode as customer7_13_ 
        from
            dbo.OwningJob owningjob_ 
        where
            owningjob_.pkId=?

I can't figure out why that extra select request is being made, it seems like Hibernate is confused because it was already Eagerly loaded?

This leads to the N+1 problem too, any ideas on what's going wrong. I'm open to completely different approaches too!

Part of Entity Class:

@Entity
@Table(name = "OwningJob", schema = "dbo")
@GraphQLType(name = "Order", description = "Order description.")
@Getter
public class OwningJob {

  @ManyToOne
  @JoinColumn(name = "CustomerCode", referencedColumnName = "CustomerId")
  @LazyToOne(LazyToOneOption.NO_PROXY)
  @LazyGroup("owningJob_customer")
  public Customer customer;

}

Edit:

I have now tried using the JPA Criteria API and I still seem to get the same behaviour:

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Object> criteria =  builder.createQuery(Object.class);
    Root<OwningJob> root = criteria.from(OwningJob.class);

    Path<String> customerReference = root.get("customerReference");
    Path<String> jobNumber_ = root.get("jobNumber");
    Path<Object> customerPath = root.join("customer");

    criteria = criteria
        .multiselect(customerReference, jobNumber_, customerPath)
        .where(builder.equal(root.get("jobNumber"), jobNumber));

This results in both these Queries firing...

Hibernate: 
    select
        owningjob0_.customerReference as col_0_0_,
        owningjob0_.OwningJobId as col_1_0_,
        customer1_.customerPkId as col_2_0_,
        customer1_.customerPkId as customer1_0_,
        customer1_.customerId as customer2_0_,
        customer1_.customerName as customer3_0_ 
    from
        dbo.OwningJob owningjob0_ 
    inner join
        dbo.Customer customer1_ 
            on owningjob0_.CustomerCode=customer1_.customerId 
    where
        owningjob0_.OwningJobId=?
Hibernate: 
    select
        customer0_.customerPkId as customer1_0_0_,
        customer0_.customerId as customer2_0_0_,
        customer0_.customerName as customer3_0_0_ 
    from
        dbo.Customer customer0_ 
    where
        customer0_.customerId=?
0

There are 0 best solutions below