当前位置: 首页 > 知识库问答 >
问题:

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

令狐凌
2023-03-14

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

假设我们有两个实体,MyEntity和TimeRangeEntity。我的实体只有一个ID和一个TimeRangeEntity的外键。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;
    }
}
@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();
    }
}

我还编写了一个测试,该测试应该在给定代码的情况下工作。我要做的只是根据ID过滤。需要注意的是,TimeRange的FK可以为空。我会注意到,这是一个虚构的例子,以使我的观点得到理解,解决方案不能真的是“只是强制FK不为空”。

@RunWith(SpringRunner.class)
@DataJpaTest(showSql = false)
@ContextConfiguration(classes = TestConfig.class)
@ActiveProfiles("test")
public class MyRepositoryTest {
    @Autowired
    private MyRepository sut;

    private static final int count = 3;

    @Before
    public void setup(){
        for (int i = 0; i < count; i++){
            sut.save(new MyEntity());
        }
    }

    @Test
    public void testPaginationWithStringFilter(){
        PaginationInfo info = new PaginationInfo();
        info.setOffset(0);
        info.setLimit(10);
        info.setSortDirection(Sort.Direction.ASC);
        info.setSortProperty("id");

        Page<MyEntity> page = sut.paginateFilter(info, "1");
        assertEquals(1, page.getTotalElements());

        page = sut.paginateFilter(info, "10");
        assertEquals(0, page.getTotalElements());
    }
}

我遇到的问题是,如果FK为null,它不会对ID进行过滤。我保存时所做的就是设置ID。我知道这个问题是因为当我注释掉filterbuilder.or(root.actiontime.starttime.IsNotNull());行时,我可以看到过滤工作正常,但当我有了它时,它就不工作了。

这将生成以下查询。第一个是“工作”过滤,我可以根据ID(行注释掉了)进行过滤。第二个是包含actionTime的过滤。

select myentity0_.id as id2_38_, myentity0_.action_time_id as action_t3_38_ from test_object myentity0_ where lower(cast(myentity0_.id as char)) like ? escape '!' order by myentity0_.id asc limit ?

select myentity0_.id as id2_38_, myentity0_.action_time_id as action_t3_38_ from test_object myentity0_ cross join time_range_entity timerangee1_ where myentity0_.action_time_id=timerangee1_.id and (lower(cast(myentity0_.id as char)) like ? escape '!' or timerangee1_.start_time is not null) order by myentity0_.id asc limit ?

综上所述,我几乎可以肯定,这是由于代码片断交叉连接time_range_entity timerangee1_where myEntity0_.action_time_id=timerangee1_.id所致,因为它验证实体是否匹配,如果range外键为NULL,它们就不能匹配。

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

编辑:只要翻译成直接的SQL,我就得到了对生成的JPQL的查询(由于我将它与真实数据一起使用,所以翻译成了这个示例):

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%';

如果FK为null,则没有返回预期的行。将它从交叉连接改为左连接最终可以正常工作。

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谓词执行器指定左联接?这好像是解决我问题的办法!

共有1个答案

胡俊弼
2023-03-14

尝试使用规范而不是谓词

  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")));
            }
        };
    }
 类似资料:
  • 我正在使用100个实体(使用JHipster)设置一个新的Spring Boot API,我的问题是:鉴于我有一组存储库层方法,我希望我的所有存储库都能够调用这些方法。 我已经尝试制作所有接口来扩展('RepositoryQuery'是我默认的自定义接口名称后缀),然后使用特定于实体的类。请注意,所有的类扩展了一个泛型实现类,名为。 请注意,给定正则表达式中的“.*”代表我的持久实体集中的任何实体

  • 使用laravel 7/livewire应用程序,我使用Repository制作crud,并获得了数据列表,在装载事件中,我分配了受保护的var$FacilityRepository,它在render方法中正常工作, 但在编辑方法中为空,我得到错误: 当用户单击“编辑链接”时 在模板中,编辑链接定义为: 为什么会出现错误以及如何修复? 修改#2: > 类设施扩展组件{...公共$FacilityR

  • 之后,我有了一个控制器,它自动将参数绑定到谓词,如下所示: 因此,我可以使用如下所示的特定过滤器从存储库查询order: 问题是我无法过滤列表中的值。我也尝试,但得到 注意:我使用spring boot 1.3.4和querydsl-jpa 3.7.4 谢了。

  • 我有一个控制器尝试用可选字段搜索。JPA实体类定义为:

  • 我有一个审计日志实体,在这个实体中,我有一个映射到另一个拥有字段审计类型的实体。我想使用JPA存储库实现后端过滤。我想通过REST GET方法返回包含传递给query param的AuditActionType的所有AuditLog<代码>审核操作类型是:、、、、 例子: http://localhost:8086/audit/action?action=lo 应返回其审核操作包含“lo”的所有审

  • 我尝试在本地运行此存储库 我安装了git,但当我运行此命令时: 编辑运行另一个命令时收到此错误: