Java 使用带有可空外键的QueryDSL在Spring数据JPA存储库中进行筛选

Java 使用带有可空外键的QueryDSL在Spring数据JPA存储库中进行筛选,java,spring,hibernate,spring-data-jpa,querydsl,Java,Spring,Hibernate,Spring Data Jpa,Querydsl,我一直有这样一个问题,我无法使用querydsl对一个表进行正确的筛选,querydsl有一个可为空的外键。我将用例简化为一个非常简单的场景 假设我们有两个实体,MyEntity和TimeRangeEntity。我的实体只有一个ID和TimeRange实体的外键。TimeRangeEntity只有一个开始时间和结束时间以及一个ID。BaseEntity(这两者都是从其扩展而来)只有一个带有@ID注释的ID @Entity @Table(name = "TEST_OBJECT") public c

我一直有这样一个问题,我无法使用querydsl对一个表进行正确的筛选,querydsl有一个可为空的外键。我将用例简化为一个非常简单的场景

假设我们有两个实体,MyEntity和TimeRangeEntity。我的实体只有一个ID和TimeRange实体的外键。TimeRangeEntity只有一个开始时间和结束时间以及一个ID。BaseEntity(这两者都是从其扩展而来)只有一个带有@ID注释的ID

@Entity
@Table(name = "TEST_OBJECT")
public class MyEntity extends BaseEntity {
    @OneToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST })
    private TimeRangeEntity actionTime;

    public TimeRangeEntity getActionTime() {
        return actionTime;
    }

    public void setActionTime(TimeRangeEntity actionTime) {
        this.actionTime = actionTime;
    }
}


@Entity
@DiscriminatorValue("static")
public class TimeRangeEntity extends BaseEntity {

    @Column(name = "START_TIME")
    private Instant startTime;

    @Column(name = "END_TIME")
    private Instant endTime;

    public Instant getStartTime() {
        return startTime;
    }

    public void setStartTime(Instant startTime) {
        this.startTime = startTime;
    }

    public Instant getEndTime() {
        return endTime;
    }

    public void setEndTime(Instant endTime) {
        this.endTime = endTime;
    }
}
我在我的存储库中构造了一个默认方法来运行findAll,并使用querydsl来构建SQL语法

@Repository
public interface MyRepository extends JpaRepository<MyEntity, Long>, QueryDslPredicateExecutor<MyEntity> {

    default Page<MyEntity> paginateFilter(PaginationInfo info, String filter){
        int page = info.getOffset() > 0 ? info.getOffset() / info.getLimit() : 0;
        PageRequest pageRequest = new PageRequest(page, info.getLimit(), new Sort(new Sort.Order(info.getSortDirection(), info.getSortProperty())));
        return findAll(createFilterPredicate(filter, myEntity), pageRequest);
    }

    default Predicate createFilterPredicate(String filter, QMyEntity root){
        BooleanBuilder filterBuilder = new BooleanBuilder();
        filterBuilder.or(root.id.stringValue().containsIgnoreCase(filter));
        filterBuilder.or(root.actionTime.startTime.isNotNull());
        return filterBuilder.getValue();
    }
}
看看这个,我几乎可以肯定这是由于snipper
交叉连接时间\范围\实体时间范围1\其中myentity0\动作\时间\ id=timerangee1\范围id
,因为它验证实体匹配,如果范围外键为空,则无法验证实体匹配

我一直在努力实现这个条件工作,如果FK不为null,它只检查时间范围的表属性,但我找不到使用querydsl的方法。任何建议/指导/代码片段都是一流的

编辑:仅转换为直接SQL,我得到了生成的JPQL的查询(由于我将其用于实际数据,因此转换为本例):

如果FK为空,则不会按预期返回行。将此连接从交叉连接更改为左连接最终工作正常

select * from test_object left join time_range on test_object.action_time_id=time_range.id where lower(cast(test_object.id as char)) like '%1%';

这样,有没有办法指定querydsl谓词执行器的左连接?这似乎是解决我问题的办法

尝试使用规范而不是谓词

  private Specification<QMyEntity> createFilterPredicate(final String filter, final QMyEntity root) {
        return new Specification<QMyEntity>() {
            @Nullable
            @Override
            public Predicate toPredicate(Root<QMyEntity> root, CriteriaQuery<?> query,
                    CriteriaBuilder criteriaBuilder) {
                Join<Object, Object> actionTime = root.join("actionTime", JoinType.LEFT);
                return criteriaBuilder.or(criteriaBuilder.like(criteriaBuilder.lower(root.get("id")), "%" + filter + "%"), criteriaBuilder.isNotNull(actionTime.get("startTime")));
            }
        };
    }
专用规范createFilterPredicate(最终字符串筛选器,最终QMyEntity根){
返回新规范(){
@可空
@凌驾
公共谓词toPredicate(根根、标准查询、,
CriteriaBuilder(标准编辑器){
Join actionTime=root.Join(“actionTime”,JoinType.LEFT);
返回criteriaBuilder.or(criteriaBuilder.like(criteriaBuilder.lower(root.get(“id”)),“%”“+”filter+“%”),criteriaBuilder.isNotNull(actionTime.get(“startTime”);
}
};
}

这看起来很有希望。我现在要试一试。这是一个相当重要的改变,我一直在做的事情,但这可能是必要的。为了获得CriteriaQuery/CriteriaBuilder,我必须创建一个新接口和一个类实现来获得实体管理器。我们将看到当我艰难地完成改变时会发生什么!谢谢,这就是我需要做的!不像其他谓词方法那么干净,但我将接管clean;)
select * from test_object cross join time_range range where test_object.action_time_id=range.id and lower(cast(test_object.id as char)) like '%1%';
select * from test_object left join time_range on test_object.action_time_id=time_range.id where lower(cast(test_object.id as char)) like '%1%';
  private Specification<QMyEntity> createFilterPredicate(final String filter, final QMyEntity root) {
        return new Specification<QMyEntity>() {
            @Nullable
            @Override
            public Predicate toPredicate(Root<QMyEntity> root, CriteriaQuery<?> query,
                    CriteriaBuilder criteriaBuilder) {
                Join<Object, Object> actionTime = root.join("actionTime", JoinType.LEFT);
                return criteriaBuilder.or(criteriaBuilder.like(criteriaBuilder.lower(root.get("id")), "%" + filter + "%"), criteriaBuilder.isNotNull(actionTime.get("startTime")));
            }
        };
    }