覆盖使用 EntityGraph 注释的 Spring-Data-JPA 默认方法会导致 QueryException [英] Overriding Spring-Data-JPA default method annotating with EntityGraph causes QueryException

查看:53
本文介绍了覆盖使用 EntityGraph 注释的 Spring-Data-JPA 默认方法会导致 QueryException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 Data-JPA 实现 EntityGraph,因为使用 QueryDslPredicateExecutor 公开了我需要的方法 findAll(Predicate, Pageable),我试图覆盖它以使用 @EntityGraph 进行注释,然后麻烦开始了:

I'm trying to implement a EntityGraph with Data-JPA, since using QueryDslPredicateExecutor<T> exposes the method findAll(Predicate, Pageable) the one I need, I tried to override it to annotate with @EntityGraph then the troubles began it's throwing :

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}}]

当我使用默认方法时,即使使用相同的 Predicate,也不会发生任何不好的事情,但是我不能使用 EntityGraph,我的实现或 Predicate 有问题吗?

When I use the default method the way it's comes nothing bad happens, even using the same Predicate, but I can't use EntityGraph, Is there some problem with my implementation or with the Predicate?

跟随场景中的对象:

实体

@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;
    }
}

存储库

public interface AppointmentRepository extends JpaRepository<Appointment, Long>,
                                               QueryDslPredicateExecutor<Appointment> {

    @EntityGraph(value = "graph.Appointment.default")
    Page<Appointment> findAll(Predicate predicate, Pageable pageable);
}

谓词

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);
    }

}

推荐答案

我遇到了完全相同的问题,并且在调试 spring 源代码时获得了很多乐趣.所以根本原因是:spring 正在对导致该错误的计数查询应用提示.

I've faced with exactly the same problem and had a lot of fun debugging the spring source code. So the root cause is: spring is applying hints on the count query which leads to that error.

QueryDslJpaRepository.java:

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 the workaround is: create a supporting interface to your repository interface and implement it overriding the query creation logic. Here is my midnight implementation (it is just a prototype which must be improved of course).

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);
    }

}

看起来是个bug,我准备提交给spring jpa jira.

Looks like it is a bug and I'm going to submit it to the spring jpa jira.

这篇关于覆盖使用 EntityGraph 注释的 Spring-Data-JPA 默认方法会导致 QueryException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆