Using JpaSpecificationExecutor with EntityGraph

Viewed 176

I am using a implementation of JpaSpecificationExecutor and trying to use in the repository the @EntityGraph for select which relationships entity they get in a complex query.

My entities examples (all relationships bidireccional)

@Entity
@Table(name = "trazabilidad_contenedor")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class TrazabilidadContenedor implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "traConSeq")
    @SequenceGenerator(name = "traConSeq")
    private Long id;

    @ManyToOne(optional = false)
    @NotNull
    @JsonIgnoreProperties(value = "trazabilidadContenedors", allowSetters = true)
    private PromoProGesCodLer promoProGesCodeLer;
       .
       .
       .

@Entity
@Table( name = "promo_pro_ges_cod_ler")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class PromoProGesCodLer implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
    @SequenceGenerator(name = "sequenceGenerator")
    private Long id;
   
    @ManyToOne(optional = false)
    @NotNull
    @JsonIgnoreProperties(value = "promoProGesCodLers", allowSetters = true)
    private ProGesCodLer procesoGestoraCodLer;

    @ManyToOne(optional = false)
    @NotNull
    @JsonIgnoreProperties(value = "promoProGesCodLers", allowSetters = true)
    private Promocion promocion;
       .
       .
       .

@Entity
@Table(name = "promocion")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Promocion implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
    @SequenceGenerator(name = "sequenceGenerator")
    private Long id;
       .
       .
       .
@Entity
@Table(name = "pro_ges_cod_ler")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@NextProGesCodLer
public class ProGesCodLer implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "pgclSeq")
    @SequenceGenerator(name = "pgclSeq")
    private Long id;

    @OneToMany(mappedBy = "procesoGestoraCodLer")
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    private Set<PromoProGesCodLer> promoProGesCodLers = new HashSet<>();

    @ManyToOne(optional = false)
    @NotNull
    @JsonIgnoreProperties(value = "proGesCodLers", allowSetters = true)
    private ProcesoGestora procesoGestora;
       .
       .
       .

And this is my repository

@Repository
public interface TrazabilidadContenedorRepository
    extends JpaRepository<TrazabilidadContenedor, Long>, JpaSpecificationExecutor<TrazabilidadContenedor> {
@EntityGraph (
            type = EntityGraph.EntityGraphType.FETCH,
            attributePaths = {
                    "promoProGesCodeLer",
                    "promoProGesCodeLer.promocion",
                    "promoProGesCodeLer.promocion.direccion",
                    "promoProGesCodeLer.promocion.direccion.municipio",
                    "promoProGesCodeLer.procesoGestoraCodLer.procesoGestora",
                    "promoProGesCodeLer.procesoGestoraCodLer.codLER",
                    "promoProGesCodeLer.procesoGestoraCodLer.codLER.lerType",
                    "promoProGesCodeLer.procesoGestoraCodLer.nextProGesCodLer",
                    "promoProGesCodeLer.procesoGestoraCodLer.procesoGestora",
                    "promoProGesCodeLer.procesoGestoraCodLer.procesoGestora.gestora",
                  
            }
    )
    List<TrazabilidadContenedor> findAll(Specification<TrazabilidadContenedor> var1);
}

The constructor of my Specification‹TrazabilidadContenedor›

protected Specification<TrazabilidadContenedor> createSpecification(TrazabilidadContenedorCriteria criteria) {
        Specification<TrazabilidadContenedor> specification = Specification.where(null);
        if (criteria != null) {
            if (criteria.getPromocionId() != null) {
                specification =
                    specification.and((root, query, builder) ->
                                    builder.equal(
                                            root
                                                .join(TrazabilidadContenedor_.promoProGesCodeLer, JoinType.LEFT)
                                                .join(PromoProGesCodLer_.promocion, JoinType.LEFT)
                                                .get(Promocion_.id),
                                            criteria.getPromocionId()
                                    )
                    );
            }
            if (criteria.getGestoraId() != null) {
                specification =
                    specification.and(
                        (root, query, builder) ->
                                    builder.equal(
                                            root
                                                    .join(TrazabilidadContenedor_.promoProGesCodeLer, JoinType.LEFT)
                                                    .join(PromoProGesCodLer_.procesoGestoraCodLer, JoinType.LEFT)
                                                    .join(ProGesCodLer_.procesoGestora, JoinType.LEFT)
                                                    .join(ProcesoGestora_.gestora, JoinType.LEFT)
                                                    .get(Gestora_.id),
                                            criteria.getGestoraId()
                                    )
                    );
            }
        }
        return specification;
    }

When i have only one criteria , criteria.getPromocionId() or criteria.getGestoraId() it's OK , but if i use both at the same time i obtain.

Caused by: java.lang.IllegalArgumentException: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=generatedAlias2,role=com.cocircular.greenadvisor.domain.PromoProGesCodLer.promocion,tableName=promocion,tableAlias=promocion2_,origin=promo_pro_ges_cod_ler promoproge1_,columns={promoproge1_.promocion_id,className=com.cocircular.greenadvisor.domain.Promocion}}] [select generatedAlias0 from com.cocircular.greenadvisor.domain.TrazabilidadContenedor as generatedAlias0 inner join generatedAlias0.promoProGesCodeLer as generatedAlias1 inner join generatedAlias1.promocion as generatedAlias2 inner join generatedAlias0.promoProGesCodeLer as generatedAlias3 inner join generatedAlias3.procesoGestoraCodLer as generatedAlias4 inner join generatedAlias4.procesoGestora as generatedAlias5 inner join generatedAlias5.gestora as generatedAlias6 where ( generatedAlias0.traceabilityStatus=:param0 ) and ( ( generatedAlias6.id=75304L ) and ( generatedAlias2.id=86754L ) )]
  at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:138)
  at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1542)
  at org.hibernate.query.Query.getResultList(Query.java:165)
  at org.hibernate.query.criteria.internal.compile.CriteriaQueryTypeQueryAdapter.getResultList(CriteriaQueryTypeQueryAdapter.java:76)

For this i'm usign Hibernate 5.4.15 and Spring-Boot 2.2.7.RELEASE.

1 Answers

Every item, presented in generated sql is bound to be present in graph. So, let's write a full graph path:

select generatedAlias0 
from com.cocircular.greenadvisor.domain.TrazabilidadContenedor as generatedAlias0 
    inner join generatedAlias0.promoProGesCodeLer as generatedAlias1 ---> promoProGesCodeLer
    inner join generatedAlias1.promocion as generatedAlias2          ---> promoProGesCodeLer.promocion
    inner join generatedAlias0.promoProGesCodeLer as generatedAlias3 ---> promoProGesCodeLer
    inner join generatedAlias3.procesoGestoraCodLer as generatedAlias4 ---> promoProGesCodeLer.procesoGestoraCodLer
    inner join generatedAlias4.procesoGestora as generatedAlias5        ---> promoProGesCodeLer.procesoGestoraCodLer.procesoGestora
    inner join generatedAlias5.gestora as generatedAlias6               ----> promoProGesCodeLer.procesoGestoraCodLer.procesoGestora.gestora
    
where ( generatedAlias0.traceabilityStatus=:param0 ) 
    and ( ( generatedAlias6.id=75304L ) and ( generatedAlias2.id=86754L ) )

Here's the provided graph:

attributePaths = {
    "promoProGesCodeLer",
    "promoProGesCodeLer.promocion",
    "promoProGesCodeLer.promocion.direccion",
    "promoProGesCodeLer.promocion.direccion.municipio",
    "promoProGesCodeLer.procesoGestoraCodLer.procesoGestora",
    "promoProGesCodeLer.procesoGestoraCodLer.codLER",
    "promoProGesCodeLer.procesoGestoraCodLer.codLER.lerType",
    "promoProGesCodeLer.procesoGestoraCodLer.nextProGesCodLer",
    "promoProGesCodeLer.procesoGestoraCodLer.procesoGestora",
    "promoProGesCodeLer.procesoGestoraCodLer.procesoGestora.gestora" }

Looks to me, that node promoProGesCodeLer.procesoGestoraCodLer is missing from graph

Related