Overriding Spring -Data-JPA default method annotating with EntityGraph raises a QueryException

I am trying to implement EntityGraph with Data-JPA, since using QueryDslPredicateExecutor<T> provides the findAll(Predicate, Pageable) method that I need, I tried to override it to annotate with @EntityGraph , then problems started throwing it:

 org.springframework.dao.InvalidDataAccessApiUsageException: 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=appointment,role=com.physioclinic.entity.Appointment.createdBy,tableName=user,tableAlias=user5_,origin=appointment appointmen0_,columns={appointmen0_.createdBy_id ,className=com.physioclinic.entity.User}}] [select count(appointment) from com.physioclinic.entity.Appointment appointment where lower(concat(concat(appointment.patient.person.name,?1),appointment.patient.person.surname)) like ?2 escape '!']; nested exception is 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=appointment,role=com.physioclinic.entity.Appointment.createdBy,tableName=user,tableAlias=user5_,origin=appointment appointmen0_,columns={appointmen0_.createdBy_id ,className=com.physioclinic.entity.User}}] 

When I use the default method, the way it arrives, nothing bad happens, even using the same Predicate , but I can not use EntityGraph, is there any problem with my implementation or with Predicate?

Searches for objects in a script:

An object

 @Table(name = "appointment") @Entity @Getter @Setter @NamedEntityGraphs({@NamedEntityGraph(name = "graph.Appointment.default", includeAllAttributes = true, attributeNodes = {@NamedAttributeNode(value = "physiotherapist"), @NamedAttributeNode(value = "patient"), @NamedAttributeNode(value = "care")})}) public class Appointment extends PersistableAuditable<User, Long> { private static final long serialVersionUID = -4325126792470516159L; @DateTimeFormat(pattern = "dd/MM/yyyy") @NotNull @Column(name = "date") private LocalDate date; @DateTimeFormat(pattern = "HH:mm") @NotNull @Column(name = "schedule") private LocalTime schedule; @ManyToOne @JoinColumn(name = "physiotherapist", referencedColumnName = "id") private Physiotherapist physiotherapist; @ManyToOne @JoinColumn(name = "service", referencedColumnName = "id") private Service service; @ManyToOne @JoinColumn(name = "patient", referencedColumnName = "id") private Patient patient; @ManyToOne(cascade = CascadeType.REMOVE) @JoinColumn(name = "care", referencedColumnName = "id") private Care care; public Appointment(long id) { setId(id); } public Appointment() { } /** * Helpers */ public boolean isSpecialPrice() { return care.getPrivateCare() && care.getSpecial() && care.getSpecialPrice() != null; } public boolean isPrivatePrice() { return care.getPrivateCare() && care.getHealthCare() == null; } public boolean isHealthCarePrice() { return !care.getPrivateCare() && care.getHealthCare() != null; } } 

Repository

 public interface AppointmentRepository extends JpaRepository<Appointment, Long>, QueryDslPredicateExecutor<Appointment> { @EntityGraph(value = "graph.Appointment.default") Page<Appointment> findAll(Predicate predicate, Pageable pageable); } 

predicate

 public final class AppointmentPredicate { private AppointmentPredicate() { } public static Predicate bySearch(String search) { QPerson person = QAppointment.appointment.patient.person; return person.name.concat(" ").concat(person.surname).containsIgnoreCase(search); } } 
+6
source share
1 answer

I ran into exactly the same problem and had a lot of fun debugging the spring source code. So, the main reason: spring applies prompts to the count request, which leads to this error.

QueryDslJpaRepository.java:

 @Override public Page<T> findAll(Predicate predicate, Pageable pageable) { JPQLQuery countQuery = createQuery(predicate); JPQLQuery query = querydsl.applyPagination(pageable, createQuery(predicate)); .... } protected JPQLQuery createQuery(Predicate... predicate) { JPAQuery query = querydsl.createQuery(path).where(predicate); CrudMethodMetadata metadata = getRepositoryMethodMetadata(); if (metadata == null) { return query; } LockModeType type = metadata.getLockModeType(); query = type == null ? query : query.setLockMode(type); for (Entry<String, Object> hint : getQueryHints().entrySet()) { query.setHint(hint.getKey(), hint.getValue()); } return query; } 

And a workaround: create a supporting interface for your repository interface and implement it, overriding the logic for creating the request. Here is my midnight implementation (this is just a prototype, which, of course, needs to be improved) .

 public interface SomeRepository extends JpaRepository<SomeEntity, Long>, QueryDslPredicateExecutor<SomeEntity>, SomeRepositoryCustom { } public interface SomeRepositoryCustom { Page<SomeEntity> findAll(Predicate predicate, Pageable pageable); } public class SomeRepositoryImpl extends SimpleJpaRepository<SomeEntity, Long> implements SomeEntityRepositoryCustom { private final EntityManager entityManager; private final EntityPath<SomeEntity> path; private final PathBuilder<SomeEntity> builder; private final Querydsl querydsl; @Autowired public SomeRepositoryImpl(EntityManager entityManager) { super(SomeEntity.class, entityManager); CrudMethodMetadata metadata = getRepositoryMethodMetadata(); this.entityManager = entityManager; this.path = SimpleEntityPathResolver.INSTANCE.createPath(SomeEntity.class); this.builder = new PathBuilder<>(path.getType(), path.getMetadata()); this.querydsl = new Querydsl(entityManager, builder); } @Override public Page<SomeEntity> findAll(Predicate predicate, Pageable pageable) { JPAQuery countQuery = createQuery(predicate); JPAQuery query = (JPAQuery) querydsl.applyPagination(pageable, createQuery(predicate)); query.setHint(EntityGraph.EntityGraphType.LOAD.getKey(), entityManager.getEntityGraph("YOUR GRAPH KEY")); Long total = countQuery.count(); List<SomeEntity> content = total > pageable.getOffset() ? query.list(path) : Collections.<SomeEntity> emptyList(); return new PageImpl<>(content, pageable, total); } private JPAQuery createQuery(Predicate predicate) { return querydsl.createQuery(path).where(predicate); } } 

It looks like this is a bug and I will post it in spring jpa jira.

+5
source

Source: https://habr.com/ru/post/979669/


All Articles