Spring Data Cassandra传入CassandraPageRequest分页时paging state无效报错
问题根因
你代码有三个核心问题直接导致分页状态报错:
- 反序列化十六进制字符串为ByteBuffer后未调用
flip()重置指针
你把所有字节写入ByteBuffer后,buffer的position指针处于写入的末尾位置,Cassandra读取分页状态时会从当前position开始读,读到的是空内容,自然抛出无效分页状态错误。 - 序列化时修改了原ByteBuffer的指针位置
你写的toHexString方法直接调用原ByteBuffer的get()方法,会移动原buffer的position指针,导致原分页状态对象直接失效。 - 分页查询逻辑错误
你第一次查询拿到分页结果后,又重复调用了一次findAll查询下一页,导致返回给前端的分页状态和实际返回的内容不匹配,下次请求用这个状态自然报错。
修复方案
1. 修正序列化方法,操作副本不修改原ByteBuffer
public static String toHexString(ByteBuffer buffer) { // 先复制只读副本,操作副本不影响原buffer的指针位置 ByteBuffer copy = buffer.asReadOnlyBuffer(); final StringBuilder r = new StringBuilder(copy.remaining() * 2); while (copy.hasRemaining()) { final byte b = copy.get(); r.append(HEX_CODE[(b >> 4) & 0xF]); r.append(HEX_CODE[(b & 0xF)]); } return r.toString(); }
2. 修正反序列化逻辑,写入完成后重置指针
map(pageState ->{ int len = pageState.length(); ByteBuffer bytes = ByteBuffer.allocate(len / 2); for (int i = 0; i < len; i += 2) { bytes.put((byte) ((Character.digit(pageState.charAt(i), 16) << 4) + Character.digit(pageState.charAt(i+1), 16))); } // 重置position到0,后续读取可以拿到所有写入的字节 bytes.flip(); return CassandraPageRequest.of(pageRequest,bytes); })
3. 修正分页查询逻辑,删除多余的二次查询
@Override public SlicedResult<UserFeed> getUserFeed(String userId, Optional<String> pagingState) { final PageRequest pageRequest = PageRequest.of(0, constants.PAGE_SIZE); CassandraPageRequest cassandraPageRequest = pagingState. map(pageState ->{ int len = pageState.length(); ByteBuffer bytes = ByteBuffer.allocate(len / 2); for (int i = 0; i < len; i += 2) { bytes.put((byte) ((Character.digit(pageState.charAt(i), 16) << 4) + Character.digit(pageState.charAt(i+1), 16))); } bytes.flip(); return CassandraPageRequest.of(pageRequest,bytes); } ) .orElse(CassandraPageRequest.first(constants.PAGE_SIZE)); Slice<UserFeed> userFeedSlice = feedRepository.findAll(cassandraPageRequest); CassandraPageRequest nextPageable = ((CassandraPageRequest)userFeedSlice.getPageable()); ByteBuffer nextPagingState = nextPageable.getPagingState(); String nextPageStateString = nextPagingState == null ? null : toHexString(nextPagingState); // 删掉多余的第二次findAll调用 return SlicedResult.<UserFeed>builder().pageState(nextPageStateString).content(userFeedSlice.getContent()) .build(); }
其他更稳妥的序列化实现方式
你可以直接用Base64编码替代手动实现十六进制转换,出错概率更低:
import java.util.Base64; // 序列化逻辑 String nextPageStateString = nextPagingState == null ? null : Base64.getUrlEncoder().encodeToString(nextPagingState.array()); // 反序列化逻辑 byte[] bytes = Base64.getUrlDecoder().decode(pageState); ByteBuffer buffer = ByteBuffer.wrap(bytes);
你也可以直接用Spring Data Cassandra自带的工具类处理分页状态,避免手动操作ByteBuffer出错。
内容的提问来源于stack exchange,提问作者karun
相关产品推荐
相关产品推荐

