hibernate 带有分页和规范的Spring Data 3 FETCH JOIN无法正常工作,它正在内存中分页

wyyhbhjk  于 2023-03-08  发布在  Spring
关注(0)|答案(1)|浏览(126)

我正在使用Sping Boot 3和Jpa规范来构建动态查询,同时使用分页和获取连接来避免N+1查询问题。但是分页是在内存中的应用程序级别应用的,而不是在数据库级别应用查询。
我的用户实体是

@Entity
@Table(name = "users")
public class User implements UserDetails, Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;
    
    @Column(name = "names")
    private String names;
    
    
    @ManyToMany(cascade = {
            CascadeType.PERSIST,
            CascadeType.MERGE
    })
    @JoinTable(name = "users_roles",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles = new HashSet<>();

...
}

我的用户存储库为

@Repository
public interface UserRepository extends 
        JpaRepository<User, UUID>, JpaSpecificationExecutor<User> { ... }

我的UserGateway使用UserRepository.findAll(规范规范,可分页可分页)

@Repository
@RequiredArgsConstructor
public class UserGatewayImpl implements UserGateway {

    private final UserRepository userRepository;

    @Override
    public Page<User> findByParams(UserSearchCommand searchCriteria, Pageable pageable) {

        var userSpecification = buildCriteria(searchCriteria);

        return userRepository.findAll(userSpecification, pageable);
    }
}

我的构建标准。为了避免“org.hib.query.语义异常:查询指定了连接获取,但是获取的关联的所有者不在选择列表中”异常,因为JPA执行了2个查询,我检查resultType以了解它是否是计数查询。

private Specification<User> buildCriteria(UserSearchCommand queryCriteria) {

        return (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (Long.class != criteriaQuery.getResultType() && 
                    long.class != criteriaQuery.getResultType()) {
                root.fetch("roles", JoinType.LEFT);
            } else {
                root.join("roles", JoinType.LEFT);
            }

            if (nonNull(queryCriteria.getNames())) {
                predicates
                        .add(criteriaBuilder.and(
                                criteriaBuilder.equal(root.get("names"), queryCriteria.getNames())));
            }

            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
    }

这似乎起作用了。分页“起作用了”。但是当我们看一下控制台并检查Hibernate生成的查询时,我发现分页没有在查询中应用于数据库级别。分页在内存中应用于应用程序级别。
生成的查询为

...
    from
        users u1_0 
    left join
        (users_roles r1_0 
    join
        roles r1_1 
            on r1_1.id=r1_0.role_id) 
                on u1_0.id=r1_0.user_id 
        where
            1=1 
        order by
            u1_0.id desc

我想使用JPA规范,分页和FETCHJOIN一起使用,但是分页是在内存中应用于应用程序级别的。分页应该在查询中应用于数据库级别的。

xesrikrc

xesrikrc1#

在这种情况下,Hibernate实际上并不能做得更好,因为当集合被提取连接时,它不能应用SQL限制/偏移子句,因为SQL基数与实体结果基数不匹配。
您可以尝试使用Blaze-Persistence on top,它也与Spring Data集成在一起,可以自动将查询转换为允许高效分页的表单。给予一下吧。初始设置后,您只需要将@EnableJpaRepositories替换为@EnableBlazeRepositories

相关问题