javahibernatejpareactive-streamsmicronaut-data

How to use Hibernate lazy loading and reactive streams?


I am currently building a REST service with Micronaut Data. I have defined two JPA entities, bounded by a bidirectional @OneToMany relationship, and lazy loading.

@Entity
@Getter
@Setter
public class ScoringEntity {
    @Id
    private String id;

    @OneToMany(mappedBy = "scoring", fetch = FetchType.LAZY)
    private Set<ContributionEntity> contributions;
}
@Entity
@Getter
@Setter
public class ContributionEntity {
    @Id
    private String id;

    @ManyToOne(fetch = FetchType.LAZY)
    @MapsId
    private ScoringEntity scoring;
}
@Repository
public interface ScoringRepository extends GenericRepository<ScoringEntity, String> {

    Page<ScoringEntity> findAll(Pageable pageable);

}

In the controller, I return a Mono which is set to call the repository, then perform a mapping to a DTO (Scoring).

@Get
public Mono<Page<Scoring>> getScoring(Pageable pageable) {
    return Mono.fromCallable(() -> scoringRepository.findAll(pageable))
        .map(scoringEntities -> scoringEntities.map(scoringMapper::mapScoring));
}

Everything works fine in the findAll() method, but things go south when the mapper tries to access the contributions set :

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: ScoringEntity.contributions, could not initialize proxy - no Session

While I understand why this happens (the transaction probably ends with the repository method), I can't find a satisfying solution. Setting the relationship to eager loading works, but it significantly decreases performance (and I've read elsewhere it would be a code smell, which I tend to believe).

Besides I can't imagine that reactive streams be incompatible with hibernate and lazy loading.

What would be the best practice in this situation ?


Solution

  • There are a few options:

    1. Add @Join("contributions") annotation to your repository method
    2. Add @EntityGraph ... annotation to your repository method
    3. Do fetching and mapping in one method annotated @ReadOnly or @Transactional so the session is open when the mapper is called

    In this case, having reactive streams doesn't make much sense. Just return Page from the controller annotated @ExecuteOn(TaskExecutors.IO).