JPA规范实例使用后发生变化?

问题描述 投票:0回答:1

我在 Carl Mapada 的高级搜索和过滤文章中使用了经过大量修改的代码版本,它实际上对我来说效果很好,直到我不得不添加可分页。和许多其他使用 Pageable 的规范一样,我在计数查询方面遇到了问题。所以我写了一个 CustomRepository 并覆盖了 readPage 所以它不算数。一切都很好,分页正在工作,但除了我相信如果我想显示“1 到 100 个结果中的 1 到 100 个结果”之类的内容,我需要一个单独的计数查询。 (当前总数只是分页的总数。)

因此,我尝试找出一种方法来通过使用我拥有的SpecificationBuilder实现构建的动态查询来获得总数。我在最后一天左右还没有成功,但是为了尝试解决我的问题而采取极端措施,我发现了一些有趣的事情......

如果在我的服务中我调用具有相同规范实例的存储库方法两次,那么我会收到错误(即使没有可分页)...

MyEntitySpecBuilder builder = new MyEntitySpecBuilder(filter, specs);
Specification<MyEntity> spec = builder.build();
List<MyEntity> entities1 = myEntityRepository.findAll(spec);
List<MyEntity> entities2 = myEntityRepository.findAll(spec);

JpaSystemException:第二次调用 findAll(...) 时发生无法在 blah.blah.EntityClassAttribute 上定位名为 xyz 的属性

但是,如果我这样做,就没有错误......

MyEntitySpecBuilder builder = new MyEntitySpecBuilder(filter, specs);
Specification<MyEntity> spec = builder.build();
List<MyEntity> entities1 = myEntityRepository.findAll(spec)
List<MyEntity> entities2 = myEntityRepository.findAll(builder.build());

我偶然发现的唯一原因是我试图进行自定义计数查询,但使用与我用于获取页面信息相同的规范实例,但它不起作用,所以我决定从使用该服务开始使用相同的规范实例执行两个不同的查询(虽然效率低下,但这是一个开始),我偶然发现了这一点。

这是我的问题...

  1. 如果我遇到使用多个规范部分来创建查询的情况(如高级搜索和过滤示例中所示)并且我想要一个计数,是否有建议的方法来做到这一点,希望在 CustomRepository 中(而不是在服务)? 我正在考虑采用一种自定义存储库方法,其中我通过规范生成器并在存储库方法中获取规范的两个单独实例,一个用于获取计数,一个用于获取结果。
  2. 但我现在的主要问题是... 规范实例在第一次使用后是否会以某种方式进行修改,或者为什么两个单独的实例相继工作但不是同一个实例?
jpa specifications
1个回答
0
投票

这是我第二个问题的解决方案。这是放在一起的东西,而不是在搜索中找到它,所以我不确定是否有更好的方法,但它有效。我忘记指出我正在使用 spring data jpa 2.2.7,所以升级后的 spring 版本可能会有所不同(我们升级库的速度很慢。)

// CustomRepositoryImpl.java (public methods here are in CustomRepository.java)
@NoRepositoryBean
public class CustomRepositoryImpl<T> extends SimpleJpaRepository<T, Long>
    implements CustomRepository<T, Long {

    private EntityManager em;

    public CustomRepositoryImpl(JpaMetamodelEntityInformation<T, Long> entityInfo, EntityManager em) {
        super(entityInfo, em);
        this.em = em;
    }

    public CustomImpl(Class<T> domainClass, EntityManager em) {
        super(domainClass, em);
        this.em = em;
    }


    public Page<T> findAll(SpecificationBuilder<T> builder, Pageable pageable, EntityGraphType entityGraphType, String entityGraphName) {

        // Note: in my case, if spec was declared here and used for the count & the query
        // there would be an error but no error when done this way...

        long count = this.count(builder.build());
        //OR for more control...
        // TypedQuery<Long> countQuery = getCountQuery(builder.build(), getDomainClass());
        // countQuery.setHint(EntityGraphType.FETCH.getKey(), countEntityGraphNameCouldBePassedInToMethodIfDesired);
        // long count = countQuery.getSingleResult();

        // need a diff instance of the Specification for some reason
        Specification<T> spec = builder.build();
        TypedQuery<T> query = getQuery(spec, Sort.unsorted());
        query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraph));
        return readPage(query, pageable, (int) count);
    }

    public Page<T> findAll(SpecificationBuilder<T> builder, Pageable pageable, String entityGraphName) {  
        return this.findAll(builder, pageable, EnitityGraphType.FETCH, entityGraphName);
    }

    private <S extends T> Page<S> readPage(TypedQuery<S> query, Pageable pageable, int count) {
        query.setFirstResult((int) pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());

        List<S> content = query.getResultList();
        return new PageImpl<S>(content, pageable, count);
    }
}


// SpecificationBuilder.java
public interface SpecificationBuilder<T> {
    Specification<T> build();
}



// MyEntitySpecificationBuilder.java
// search for Advanced Search and Filtering on the internet for more details
public class MyEntitySpecificationBuilder implements SpecificationBuilder<MyEntity> {

    public Specification<MyEntity> build() {
        ...
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.