Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SPQR Dataloder doesn't allow null values - Exception while fetching data : null #404

Closed
BenNeighbour opened this issue Jul 24, 2021 · 3 comments

Comments

@BenNeighbour
Copy link

@kaqqao @ellebrecht @jestan @heruan @vojtechhabarta Hi all,

I have set up some batch DataLoaders for each entity (that have relationships with others) in order to solve the N+1 Problem with GraphQL/SPQR - and it works. HOWEVER, when I have a @OneToOne or @ManyToMany relation on some entity that is null, an Exception while fetching data (/getAllEmployees[6]/company) : null.

Here is the resolver for the company field of each employee:

@GraphQLQuery(name = "company")
public CompletableFuture<Company> getCompany(
        @GraphQLContext @Valid @GraphQLNonNull Employee employee, @GraphQLEnvironment ResolutionEnvironment env) {
  DataLoader<UUID, Company> loader = env.dataFetchingEnvironment.getDataLoader("company");
  return loader.load(employee.getCompany().getId());
}

And the DataFetcher,

@Component
public class DataLoaderFactory implements DataLoaderRegistryFactory {

  private static CompanyDao companyDao;
  private static final BatchLoader<UUID, Company> companyLoader = DataLoaderFactory::companies;
  /* ...... */

  @Autowired
  public DataLoaderFactory(CompanyDao companyDao, /* ...... */) {
    DataLoaderFactory.companyDao = companyDao;
    /* ...... */
  }

  public static CompletableFuture<List<Company>> companies(List<UUID> ids) {
    return CompletableFuture.completedFuture(companyDao.findAllById(ids));
  }

  /* ...... */

  @Override
  @Bean
  public DataLoaderRegistry createDataLoaderRegistry() {
    DataLoaderRegistry allLoaders = new DataLoaderRegistry();
    allLoaders.register("company", new DataLoader<>(companyLoader));
    /* ...... */

    return allLoaders;
  }
}

And for some context, here are the entities:

For the Employee

@Setter
@Getter
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class Employee implements Serializable {

  private static final long serialVersionUID = 684733882540759135L;

  @Id
  @GeneratedValue
  @Column(columnDefinition = "uuid", updatable = false)
  @GraphQLQuery(name = "id", description = "A Person's Id")
  private UUID id;

  @NotNull(message = "There must be a Person's Name!")
  @GraphQLQuery(name = "fullName", description = "A Person's Name")
  private String fullName;

  @NotNull(message = "A Person must have an Age!")
  @GraphQLQuery(name = "age", description = "A Person's Age")
  private int age;

  @NotNull(message = "A Person must have a Vehicle!")
  @GraphQLQuery(name = "personalVehicle", description = "A Person's Mode of Transport")
  private Vehicle personalVehicle;

  @ManyToOne(targetEntity = Company.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
  @GraphQLQuery(name = "company", description = "The Company a Person works for")
  private Company company;

  @GraphQLQuery(name = "roles", description = "The Role a Person plays in their Company")
  @OneToMany(
      targetEntity = Role.class,
      orphanRemoval = true,
      fetch = FetchType.LAZY,
      mappedBy = "employee",
      cascade = CascadeType.ALL)
  private List<Role> roles = new ArrayList<>();

  public enum Vehicle {
    CAR,
    BUS,
    VAN,
    BICYCLE,
    MOTORBIKE,
    SCOOTER
  }
}

For the Company

@Setter
@Getter
@Entity
@NoArgsConstructor
@AllArgsConstructor
public class Company implements Serializable {

  private static final long serialVersionUID = -6007975840330441233L;

  @Id
  @GeneratedValue
  @Column(columnDefinition = "uuid", updatable = false)
  @GraphQLQuery(name = "id", description = "A Company's Id")
  private UUID id;

  @NotNull(message = "There must be a Company Name!")
  @GraphQLQuery(name = "name", description = "A Company's Name")
  private String name;

  @Min(10000)
  @NotNull(message = "You gotta tell us how rich you are!")
  @GraphQLQuery(name = "balance", description = "A Company's Dollar")
  private BigDecimal balance;

  @NotNull(message = "There must be a Company Type!")
  @GraphQLQuery(name = "type", description = "The type of company")
  private CompanyType type;

  @GraphQLQuery(name = "offices", description = "The Company's offices")
  @OneToMany(
      targetEntity = Office.class,
      orphanRemoval = true,
      fetch = FetchType.LAZY,
      mappedBy = "company",
      cascade = CascadeType.ALL)
  private List<Office> offices;

  public enum CompanyType {
    PRIVATE_LIMITED,
    SOLE_TRADER,
    PUBLIC
  }
}

Any help, comments or clarification would be much, much appreciated - I'm probably missing out something stupid!!

@BenNeighbour
Copy link
Author

Also my build.gradle dependency for this is: implementation "io.leangen.graphql:graphql-spqr-spring-boot-starter:0.0.6"

@BenNeighbour
Copy link
Author

BenNeighbour commented Jul 24, 2021

This is a duplicate of this issue - trying to get more traction as I've tried everything to make it work...

@kaqqao
Copy link
Member

kaqqao commented Jul 16, 2023

Closing this as all the activity is happening on the linked original.

@kaqqao kaqqao closed this as completed Jul 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants