-3

JPA/Hibernate EntityGraph with Specification and Pagination not loading all related entities correctly

I'm working on a JHipster-generated application and need to create APIs that load entities from the database using JPA Specifications and Pagination, while correctly loading related entities. However, I'm struggling to make this work properly.

Entity Structure

I have three main entities with the following relationships:

BuildingProject Entity:

@Entity
@Table(name = "building_project")
public class BuildingProject implements Serializable {
    @Id
    @GeneratedValue(strategy = SEQUENCE)
    private Long id;

    @NotNull
    private String name;

    @NotNull
    private BuildingType type;

    @NotNull
    private String address;

    private String description;
    private BigDecimal minPrice;
    private Instant completionDate;

    @OneToMany(fetch = LAZY, mappedBy = "project")
    @JsonIgnoreProperties(value = { "photos", "bookings", "project" }, allowSetters = true)
    private Set<Unit> units = new HashSet<>();

    @OneToMany(fetch = LAZY, mappedBy = "project")
    @JsonIgnoreProperties(value = { "project", "unit" }, allowSetters = true)
    private Set<Photo> photos = new HashSet<>();
}

Unit Entity:

@Entity
@Table(name = "unit")
public class Unit implements Serializable {
    @Id
    @GeneratedValue(strategy = SEQUENCE)
    private Long id;

    private String location;

    @NotNull
    private BigDecimal price;

    private String description;

    @NotNull
    private BigDecimal area;

    @NotNull
    private Integer floor;

    @NotNull
    private UnitType type;

    @NotNull
    private UnitStatus status;

    private Instant completionDate;

    @OneToMany(fetch = LAZY, mappedBy = "unit")
    @BatchSize(size = 20)
    @JsonIgnoreProperties(value = { "project", "unit" }, allowSetters = true)
    private Set<Photo> photos = new HashSet<>();

    @OneToMany(fetch = LAZY, mappedBy = "unit")
    @JsonIgnoreProperties(value = { "client", "unit" }, allowSetters = true)
    private Set<Booking> bookings = new HashSet<>();

    @ManyToOne(optional = false)
    @NotNull
    @JsonIgnoreProperties(value = { "units", "photos" }, allowSetters = true)
    private BuildingProject project;
}

Photo Entity:

@Entity
@Table(name = "photo")
public class Photo implements Serializable {
    @Id
    @GeneratedValue(strategy = SEQUENCE)
    private Long id;

    @NotNull
    private String url;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "project_id")
    @JsonIgnoreProperties(value = { "units", "photos" }, allowSetters = true)
    private BuildingProject project;

    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "unit_id")
    @JsonIgnoreProperties(value = { "photos", "bookings", "project" }, allowSetters = true)
    private Unit unit;
}

API Requirements

I need two APIs:

  1. Load BuildingProject with all Units and all Photos.
  2. Load all Unit entities with all Photos.

Both APIs must maintain filtering (using Specifications) and pagination for performance and user requirements.

Current Implementation

I implemented this method:

@Transactional(readOnly = true)
public void findFullByCriteria(BuildingProjectCriteria criteria, Pageable page) {
    log.debug("find full by criteria : {}, page: {}", criteria, page);
    final Specification<BuildingProject> specification = createSpecification(criteria);
    buildingProjectRepository
            .findAll(specification, page)
            .forEach(b -> log.error("{} with {} photos and {} units",
                    b.getId(),
                    b.getPhotos().size(),
                    b.getUnits().size()));
}

Repository with EntityGraph:

@Repository
public interface BuildingProjectRepository extends JpaRepository<BuildingProject, Long>, JpaSpecificationExecutor<BuildingProject> {
    @EntityGraph(attributePaths = {"photos", "units"})
    Page<BuildingProject> findAll(Specification<BuildingProject> spec, Pageable pageable);
}

application.yml:

hibernate.query.fail_on_pagination_over_collection_fetch: false

The Problem

The related entities are only loading one item each, even though there are more in the database.

Sample Data from photo table:

id, url, project_id, unit_id
1, https://straight-hepatitis.net/, 1, 1
2, https://funny-serial.net/, 1, 1
3, https://bewitched-efficiency.name/, 2, 2  
4, https://partial-chili.info, 2, 2

→ At least two photos per entity

Sample Data from unit table:

id, location, price, project_id
1, finally powerfully, 5169.79, 2
2, helplessly, 19973.88, 2
3, mechanically, 9074.87, 3

→ Multiple units per project

But in logs I get:

1 with 1 photos and 0 units
2 with 1 photos and 1 units  
3 with 0 photos and 1 units
4 with 0 photos and 1 units
5 with 0 photos and 1 units

→ Only loading one related entity each instead of all

What I've Tried

  • Using @EntityGraph with attributePaths
  • Setting hibernate.query.fail_on_pagination_over_collection_fetch: false
  • Verified that all relationships are properly mapped with mappedBy

About project

Spring Boot: 3.2.5 Java: 17 JHipster Framework: 8.4.0 Source: https://github.com/GGlebux/Construction.git

The Question

Why are my related entities not loading completely when using EntityGraph with Specification and Pagination? How can I properly load all related entity while maintaining filtering and pagination capabilities?

I've already tried many ways. Any help or guidance would be greatly appreciated!

7
  • 2
    I'm uncertain why this is attracting so many downvotes, but I speculate people may be missing "The Question" at the end of this pretty long post, but seeing "API Requirements" and "I need two APIs" nearer the top and taking that to be an expression of the ask. Commented Nov 7 at 21:26
  • 1
    Something is very wrong here. With or without an EntityGraph, every relationship of every entity retrieved should be either (eagerly) fetched or (lazily) not (yet) fetched, never fetched only partially. Moreover, as long as the entity is attached to the persistence context, any lazy relationships should be demand-fetched, so that you cannot observe the unfetched state. Commented Nov 7 at 21:36
  • 1
    It might be worthwhile debugging the SQL generated. I speculate that there may be a bug in how the paging is implemented, such that the intention is to eagerly fetch the relationships via JOINs, but the paging is causing fewer rows to be considered than should be. Commented Nov 7 at 21:41
  • @JohnBollinger, Thanks for the feedback. I probably didn't ask the question correctly at the beginning, it's just that this is the first time I'm asking a question on stackoverflow.com. I will write the questions more clearly in the future. Commented Nov 10 at 13:02
  • 1
    I'm glad you solved your problem. If you think your discoveries might be of value to others then consider adding an answer to describe the nature of the issue and how you solved it. Commented Nov 10 at 13:22

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.