Spring Data Slice使用指南

目录

一、基本概念

[与 Page 的区别](#与 Page 的区别)

主要特点

二、核心接口方法

三、基本使用示例

[1. Repository 接口定义](#1. Repository 接口定义)

[2. 服务层使用](#2. 服务层使用)

[3. 控制器层](#3. 控制器层)

四、高级用法

[1. 自定义查询 + Slice](#1. 自定义查询 + Slice)

[2. 动态排序](#2. 动态排序)

[3. DTO 投影](#3. DTO 投影)

[五、与 Stream 结合使用](#五、与 Stream 结合使用)

六、性能优化建议

[使用 Slice 的场景:](#使用 Slice 的场景:)

[使用 Page 的场景:](#使用 Page 的场景:)

七、注意事项

八、测试示例

Slice 是 Spring Data 中用于分页查询的核心接口之一,用于表示查询结果的一部分(分片),特别适用于大数据集的分页处理。

一、基本概念

Page 的区别

  • Slice: 只知道是否有下一页,不知道总页数和总数

  • Page : 扩展自 Slice,包含总页数和总记录数

主要特点

  • 轻量级,不执行总数统计,性能更好

  • 适用于无限滚动、流式加载等场景

  • 内存占用更小

二、核心接口方法

复制代码
public interface Slice<T> extends Streamable<T> {
    int getNumber();                    // 当前页码(从0开始)
    int getSize();                      // 每页大小
    int getNumberOfElements();          // 当前页实际元素数
    List<T> getContent();               // 获取当前页数据
    boolean hasContent();               // 是否有数据
    Sort getSort();                     // 排序信息
    boolean isFirst();                  // 是否是第一页
    boolean isLast();                   // 是否是最后一页
    boolean hasNext();                  // 是否有下一页
    boolean hasPrevious();              // 是否有上一页
    Pageable getPageable();             // 分页信息
    Pageable nextPageable();            // 下一页的分页信息
    Pageable previousPageable();        // 上一页的分页信息
    <U> Slice<U> map(Function<T, U> converter); // 转换数据
}

三、基本使用示例

1. Repository 接口定义

复制代码
public interface UserRepository extends JpaRepository<User, Long> {
    // 返回 Slice(不查询总数)
    Slice<User> findByAgeGreaterThan(int age, Pageable pageable);
    
    // 返回 Page(查询总数)
    Page<User> findByNameContaining(String name, Pageable pageable);
}

2. 服务层使用

复制代码
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    // 使用 Slice 进行分页查询
    public Slice<User> getUsersByAge(int age, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("createdAt").descending());
        return userRepository.findByAgeGreaterThan(age, pageable);
    }
    
    // 处理分页数据
    public void processUsersSlice() {
        int page = 0;
        int size = 20;
        
        do {
            Pageable pageable = PageRequest.of(page, size);
            Slice<User> slice = userRepository.findByAgeGreaterThan(18, pageable);
            
            // 处理当前页数据
            List<User> users = slice.getContent();
            users.forEach(user -> {
                // 业务处理
                System.out.println(user.getName());
            });
            
            // 如果有下一页,继续查询
            if (slice.hasNext()) {
                page++;
            } else {
                break;
            }
        } while (true);
    }
}

3. 控制器层

复制代码
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping("/slice")
    public ResponseEntity<Map<String, Object>> getUsersBySlice(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size,
            @RequestParam int minAge) {
        
        Pageable pageable = PageRequest.of(page, size);
        Slice<User> slice = userRepository.findByAgeGreaterThan(minAge, pageable);
        
        Map<String, Object> response = new HashMap<>();
        response.put("data", slice.getContent());
        response.put("currentPage", slice.getNumber());
        response.put("pageSize", slice.getSize());
        response.put("hasNext", slice.hasNext());
        response.put("hasPrevious", slice.hasPrevious());
        response.put("isFirst", slice.isFirst());
        response.put("isLast", slice.isLast());
        
        return ResponseEntity.ok(response);
    }
}

四、高级用法

1. 自定义查询 + Slice

复制代码
public interface UserRepository extends JpaRepository<User, Long> {
    
    @Query("SELECT u FROM User u WHERE u.active = true AND u.age > :age")
    Slice<User> findActiveUsersByAge(@Param("age") int age, Pageable pageable);
    
    // 使用原生查询
    @Query(value = "SELECT * FROM users WHERE status = 'ACTIVE'",
           countQuery = "SELECT count(*) FROM users WHERE status = 'ACTIVE'",
           nativeQuery = true)
    Slice<User> findActiveUsers(Pageable pageable);
}

2. 动态排序

复制代码
public Slice<User> getUsersWithDynamicSort(int page, int size, String sortBy, String direction) {
    Sort sort = direction.equalsIgnoreCase("desc") 
        ? Sort.by(sortBy).descending() 
        : Sort.by(sortBy).ascending();
    
    Pageable pageable = PageRequest.of(page, size, sort);
    return userRepository.findAll(pageable);
}

3. DTO 投影

复制代码
public interface UserProjection {
    String getName();
    String getEmail();
}

public interface UserRepository extends JpaRepository<User, Long> {
    Slice<UserProjection> findSliceBy(Pageable pageable);
}

// 使用
Slice<UserProjection> slice = userRepository.findSliceBy(pageable);
slice.getContent().forEach(projection -> {
    System.out.println(projection.getName());
});

五、与 Stream 结合使用

复制代码
public void processAllUsersWithSlice() {
    int pageSize = 100;
    int page = 0;
    
    do {
        Pageable pageable = PageRequest.of(page, pageSize);
        Slice<User> slice = userRepository.findAll(pageable);
        
        // 使用 Stream 处理
        slice.getContent().stream()
            .filter(user -> user.isActive())
            .forEach(user -> processUser(user));
        
        if (!slice.hasNext()) {
            break;
        }
        page++;
    } while (true);
}

六、性能优化建议

使用 Slice 的场景:

  1. 大数据集分页:不需要知道总记录数时

  2. 无限滚动:移动端或Web端的懒加载

  3. 导出数据:分批处理大量数据

  4. 实时数据:数据频繁变化,总数意义不大

使用 Page 的场景:

  1. 需要显示总页数:传统分页组件

  2. 数据量相对较小:总数查询不会造成性能问题

  3. 需要统计信息:如总记录数、总页数等

七、注意事项

  1. 页码从0开始:Spring Data 的页码通常从0开始

  2. 排序字段索引:确保分页排序字段有数据库索引

  3. 内存管理:处理大量数据时注意内存使用

  4. 连接池配置:长时间分页查询注意连接超时设置

  5. 事务管理:确保分页查询在事务范围内

八、测试示例

复制代码
@SpringBootTest
class UserRepositoryTest {
    
    @Autowired
    private UserRepository userRepository;
    
    @Test
    void testSliceQuery() {
        // 准备测试数据
        for (int i = 0; i < 50; i++) {
            User user = new User("User" + i, 20 + i);
            userRepository.save(user);
        }
        
        // 测试 Slice 查询
        Pageable pageable = PageRequest.of(0, 20);
        Slice<User> slice = userRepository.findByAgeGreaterThan(25, pageable);
        
        assertThat(slice.getContent()).hasSize(20);
        assertThat(slice.hasNext()).isTrue();
        assertThat(slice.isFirst()).isTrue();
        
        // 测试下一页
        Slice<User> nextSlice = userRepository.findByAgeGreaterThan(
            25, slice.nextPageable());
        assertThat(nextSlice.getNumber()).isEqualTo(1);
    }
}

Slice 是 Spring Data 中非常实用的分页工具,特别适合大数据量和性能敏感的场景。根据实际需求选择使用 SlicePage

相关推荐
夕除7 小时前
js--7
java
布谷歌7 小时前
面试题整理
java·开发语言
爬山算法7 小时前
Hibernate(74)如何在CQRS架构中使用Hibernate?
java·架构·hibernate
jjjava2.07 小时前
深入解析Set与Map的奥秘
java·开发语言
白宇横流学长7 小时前
基于Java的火车票订票系统的设计与开发
java·开发语言
黎雁·泠崖7 小时前
Java核心基础API学习总结:从Object到包装类的核心知识体系
java·开发语言·学习
Yvonne爱编码8 小时前
JAVA数据结构 DAY1-集合和时空复杂度
java·数据结构·python
win x8 小时前
JavaSE(基础)高频面试点及 知识点
java·面试·职场和发展
Terio_my8 小时前
简要 Java 面试题学习
java·开发语言·学习
好好研究8 小时前
Spring Boot - Thymeleaf模板引擎
java·spring boot·后端·thymeleaf