| New file |
| | |
| | | package com.xiaopeng.itinerary.utils; |
| | | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 分表查询辅助工具类 |
| | | * 提供分表查询的SQL构建和批量查询支持 |
| | | * |
| | | * @author auto-generated |
| | | */ |
| | | @Slf4j |
| | | public class ShardingQueryHelper { |
| | | |
| | | /** |
| | | * 构建单个分表的查询SQL |
| | | * |
| | | * @param baseTableName 基础表名 |
| | | * @param shardingValue 分片键值 |
| | | * @param shardingCount 分片数量 |
| | | * @param whereClause WHERE子句(不包含WHERE关键字) |
| | | * @return 完整的查询SQL |
| | | */ |
| | | public static String buildSingleTableQuery(String baseTableName, Long shardingValue, |
| | | int shardingCount, String whereClause) { |
| | | String tableName = ShardingTableUtil.getShardingTableName(baseTableName, shardingValue, shardingCount); |
| | | StringBuilder sql = new StringBuilder("SELECT * FROM "); |
| | | sql.append(tableName); |
| | | |
| | | if (whereClause != null && !whereClause.trim().isEmpty()) { |
| | | sql.append(" WHERE ").append(whereClause); |
| | | } |
| | | |
| | | String query = sql.toString(); |
| | | log.debug("Generated single table query: {}", query); |
| | | return query; |
| | | } |
| | | |
| | | /** |
| | | * 构建全表扫描的UNION查询SQL(适用于需要查询所有分表的场景) |
| | | * |
| | | * @param baseTableName 基础表名 |
| | | * @param shardingCount 分片数量 |
| | | * @param whereClause WHERE子句(不包含WHERE关键字) |
| | | * @param orderBy 排序字句(不包含ORDER BY关键字) |
| | | * @param limit 限制返回数量,null表示不限制 |
| | | * @return UNION查询SQL |
| | | */ |
| | | public static String buildUnionAllQuery(String baseTableName, int shardingCount, |
| | | String whereClause, String orderBy, Integer limit) { |
| | | StringBuilder sql = new StringBuilder(); |
| | | |
| | | for (int i = 0; i < shardingCount; i++) { |
| | | if (i > 0) { |
| | | sql.append(" UNION ALL "); |
| | | } |
| | | |
| | | sql.append("SELECT * FROM ").append(baseTableName).append("_").append(i); |
| | | |
| | | if (whereClause != null && !whereClause.trim().isEmpty()) { |
| | | sql.append(" WHERE ").append(whereClause); |
| | | } |
| | | } |
| | | |
| | | // 外层排序和限制 |
| | | if (orderBy != null && !orderBy.trim().isEmpty()) { |
| | | sql.insert(0, "SELECT * FROM ("); |
| | | sql.append(") AS union_result ORDER BY ").append(orderBy); |
| | | } |
| | | |
| | | if (limit != null && limit > 0) { |
| | | sql.append(" LIMIT ").append(limit); |
| | | } |
| | | |
| | | String query = sql.toString(); |
| | | log.debug("Generated union all query for {} tables: {}", shardingCount, query); |
| | | return query; |
| | | } |
| | | |
| | | /** |
| | | * 获取需要查询的分表索引列表(基于分片键值范围) |
| | | * |
| | | * @param startValue 起始分片键值 |
| | | * @param endValue 结束分片键值 |
| | | * @param shardingCount 分片数量 |
| | | * @return 需要查询的表索引列表 |
| | | */ |
| | | public static List<Integer> getShardingIndexesInRange(Long startValue, Long endValue, int shardingCount) { |
| | | List<Integer> indexes = new ArrayList<>(); |
| | | |
| | | if (startValue == null || endValue == null || startValue > endValue) { |
| | | log.warn("Invalid range: start={}, end={}, returning all indexes", startValue, endValue); |
| | | for (int i = 0; i < shardingCount; i++) { |
| | | indexes.add(i); |
| | | } |
| | | return indexes; |
| | | } |
| | | |
| | | // 计算涉及的分表索引 |
| | | for (long value = startValue; value <= endValue; value++) { |
| | | int index = (int) (value % shardingCount); |
| | | if (!indexes.contains(index)) { |
| | | indexes.add(index); |
| | | } |
| | | } |
| | | |
| | | log.debug("Range query from {} to {} covers indexes: {}", startValue, endValue, indexes); |
| | | return indexes; |
| | | } |
| | | |
| | | /** |
| | | * 构建基于索引列表的UNION查询 |
| | | * |
| | | * @param baseTableName 基础表名 |
| | | * @param tableIndexes 要查询的表索引列表 |
| | | * @param whereClause WHERE子句 |
| | | * @param orderBy 排序字句 |
| | | * @param limit 限制数量 |
| | | * @return UNION查询SQL |
| | | */ |
| | | public static String buildUnionQueryByIndexes(String baseTableName, List<Integer> tableIndexes, |
| | | String whereClause, String orderBy, Integer limit) { |
| | | if (tableIndexes == null || tableIndexes.isEmpty()) { |
| | | log.warn("No table indexes provided, returning empty query"); |
| | | return ""; |
| | | } |
| | | |
| | | StringBuilder sql = new StringBuilder(); |
| | | |
| | | for (int i = 0; i < tableIndexes.size(); i++) { |
| | | if (i > 0) { |
| | | sql.append(" UNION ALL "); |
| | | } |
| | | |
| | | int tableIndex = tableIndexes.get(i); |
| | | sql.append("SELECT * FROM ").append(baseTableName).append("_").append(tableIndex); |
| | | |
| | | if (whereClause != null && !whereClause.trim().isEmpty()) { |
| | | sql.append(" WHERE ").append(whereClause); |
| | | } |
| | | } |
| | | |
| | | // 外层排序和限制 |
| | | if (orderBy != null && !orderBy.trim().isEmpty()) { |
| | | sql.insert(0, "SELECT * FROM ("); |
| | | sql.append(") AS union_result ORDER BY ").append(orderBy); |
| | | } |
| | | |
| | | if (limit != null && limit > 0) { |
| | | sql.append(" LIMIT ").append(limit); |
| | | } |
| | | |
| | | String query = sql.toString(); |
| | | log.debug("Generated union query for indexes {}: {}", tableIndexes, query); |
| | | return query; |
| | | } |
| | | |
| | | /** |
| | | * 行程相关表的批量查询SQL构建器 |
| | | */ |
| | | public static class ItineraryQueryBuilder { |
| | | |
| | | /** |
| | | * 根据用户ID查询操作日志 |
| | | */ |
| | | public static String querySysOperLogByUserId(Long userId, String additionalWhere) { |
| | | String tableName = ShardingTableUtil.getSysOperLogTableName(userId); |
| | | StringBuilder sql = new StringBuilder("SELECT * FROM ").append(tableName); |
| | | sql.append(" WHERE userId = ").append(userId); |
| | | |
| | | if (additionalWhere != null && !additionalWhere.trim().isEmpty()) { |
| | | sql.append(" AND ").append(additionalWhere); |
| | | } |
| | | |
| | | return sql.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 根据用户ID查询协议用户信息 |
| | | */ |
| | | public static String queryAgreementAppUserByUserId(Long appUserId) { |
| | | String tableName = ShardingTableUtil.getAgreementAppUserTableName(appUserId); |
| | | return "SELECT * FROM " + tableName + " WHERE app_user_id = " + appUserId; |
| | | } |
| | | |
| | | /** |
| | | * 根据电子围栏区域ID查询点位信息 |
| | | */ |
| | | public static String queryPointByElectronicAreaId(Long electronicAreaId) { |
| | | String tableName = ShardingTableUtil.getPointTableName(electronicAreaId); |
| | | return "SELECT * FROM " + tableName + " WHERE electronic_area_id = " + electronicAreaId; |
| | | } |
| | | |
| | | /** |
| | | * 查询所有操作日志(全表扫描) |
| | | */ |
| | | public static String queryAllSysOperLogs(String whereClause, String orderBy, Integer limit) { |
| | | return ShardingQueryHelper.buildUnionAllQuery("sys_oper_log", 10, whereClause, orderBy, limit); |
| | | } |
| | | |
| | | /** |
| | | * 查询所有协议用户(全表扫描) |
| | | */ |
| | | public static String queryAllAgreementAppUsers(String whereClause, String orderBy, Integer limit) { |
| | | return ShardingQueryHelper.buildUnionAllQuery("xp_agreement_app_user", 10, whereClause, orderBy, limit); |
| | | } |
| | | |
| | | /** |
| | | * 查询所有点位(全表扫描) |
| | | */ |
| | | public static String queryAllPoints(String whereClause, String orderBy, Integer limit) { |
| | | return ShardingQueryHelper.buildUnionAllQuery("xp_point", 10, whereClause, orderBy, limit); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 分表查询计数工具 |
| | | */ |
| | | public static class ShardingCountHelper { |
| | | |
| | | /** |
| | | * 构建分表计数查询(UNION ALL + SUM) |
| | | */ |
| | | public static String buildCountQuery(String baseTableName, int shardingCount, String whereClause) { |
| | | StringBuilder sql = new StringBuilder("SELECT SUM(cnt) AS total_count FROM ("); |
| | | |
| | | for (int i = 0; i < shardingCount; i++) { |
| | | if (i > 0) { |
| | | sql.append(" UNION ALL "); |
| | | } |
| | | |
| | | sql.append("SELECT COUNT(*) AS cnt FROM ").append(baseTableName).append("_").append(i); |
| | | |
| | | if (whereClause != null && !whereClause.trim().isEmpty()) { |
| | | sql.append(" WHERE ").append(whereClause); |
| | | } |
| | | } |
| | | |
| | | sql.append(") AS count_result"); |
| | | |
| | | String query = sql.toString(); |
| | | log.debug("Generated count query: {}", query); |
| | | return query; |
| | | } |
| | | |
| | | /** |
| | | * 统计所有操作日志数量 |
| | | */ |
| | | public static String countAllSysOperLogs(String whereClause) { |
| | | return buildCountQuery("sys_oper_log", 10, whereClause); |
| | | } |
| | | |
| | | /** |
| | | * 统计所有协议用户数量 |
| | | */ |
| | | public static String countAllAgreementAppUsers(String whereClause) { |
| | | return buildCountQuery("xp_agreement_app_user", 10, whereClause); |
| | | } |
| | | |
| | | /** |
| | | * 统计所有点位数量 |
| | | */ |
| | | public static String countAllPoints(String whereClause) { |
| | | return buildCountQuery("xp_point", 10, whereClause); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 打印查询示例 |
| | | */ |
| | | public static void printQueryExamples() { |
| | | log.info("========== Sharding Query Examples =========="); |
| | | log.info("1. Query sys_oper_log by userId 12345:"); |
| | | log.info(" {}", ItineraryQueryBuilder.querySysOperLogByUserId(12345L, "status = 1")); |
| | | log.info(""); |
| | | log.info("2. Query agreement_app_user by userId 67890:"); |
| | | log.info(" {}", ItineraryQueryBuilder.queryAgreementAppUserByUserId(67890L)); |
| | | log.info(""); |
| | | log.info("3. Query all sys_oper_logs (UNION ALL):"); |
| | | log.info(" {}", ItineraryQueryBuilder.queryAllSysOperLogs("status = 1", "create_time DESC", 100)); |
| | | log.info(""); |
| | | log.info("4. Count all agreement_app_users:"); |
| | | log.info(" {}", ShardingCountHelper.countAllAgreementAppUsers(null)); |
| | | log.info("============================================="); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.xiaopeng.itinerary.utils; |
| | | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | /** |
| | | * 分表工具类 |
| | | * 根据 ShardingSphere 配置计算实际的分表名称 |
| | | * |
| | | * @author auto-generated |
| | | */ |
| | | @Slf4j |
| | | public class ShardingTableUtil { |
| | | |
| | | /** |
| | | * 根据用户ID计算操作日志表的实际表名 |
| | | * 分片规则: sys_oper_log_${userId % 10} |
| | | * |
| | | * @param userId 用户ID |
| | | * @return 实际表名,如: sys_oper_log_5 |
| | | */ |
| | | public static String getSysOperLogTableName(Long userId) { |
| | | if (userId == null) { |
| | | log.warn("userId is null, returning default table name"); |
| | | return "sys_oper_log_0"; |
| | | } |
| | | int tableIndex = (int) (userId % 10); |
| | | String tableName = "sys_oper_log_" + tableIndex; |
| | | log.debug("userId: {}, calculated table name: {}", userId, tableName); |
| | | return tableName; |
| | | } |
| | | |
| | | /** |
| | | * 根据用户ID计算协议用户表的实际表名 |
| | | * 分片规则: xp_agreement_app_user_${app_user_id % 10} |
| | | * |
| | | * @param appUserId 用户ID |
| | | * @return 实际表名,如: xp_agreement_app_user_5 |
| | | */ |
| | | public static String getAgreementAppUserTableName(Long appUserId) { |
| | | if (appUserId == null) { |
| | | log.warn("appUserId is null, returning default table name"); |
| | | return "xp_agreement_app_user_0"; |
| | | } |
| | | int tableIndex = (int) (appUserId % 10); |
| | | String tableName = "xp_agreement_app_user_" + tableIndex; |
| | | log.debug("appUserId: {}, calculated table name: {}", appUserId, tableName); |
| | | return tableName; |
| | | } |
| | | |
| | | /** |
| | | * 根据电子围栏区域ID计算点位表的实际表名 |
| | | * 分片规则: xp_point_${electronic_area_id % 10} |
| | | * |
| | | * @param electronicAreaId 电子围栏区域ID |
| | | * @return 实际表名,如: xp_point_5 |
| | | */ |
| | | public static String getPointTableName(Long electronicAreaId) { |
| | | if (electronicAreaId == null) { |
| | | log.warn("electronicAreaId is null, returning default table name"); |
| | | return "xp_point_0"; |
| | | } |
| | | int tableIndex = (int) (electronicAreaId % 10); |
| | | String tableName = "xp_point_" + tableIndex; |
| | | log.debug("electronicAreaId: {}, calculated table name: {}", electronicAreaId, tableName); |
| | | return tableName; |
| | | } |
| | | |
| | | /** |
| | | * 获取所有操作日志表名列表(用于全表扫描场景) |
| | | * |
| | | * @return 所有操作日志表名数组,共10个表 |
| | | */ |
| | | public static String[] getAllSysOperLogTableNames() { |
| | | String[] tableNames = new String[10]; |
| | | for (int i = 0; i < 10; i++) { |
| | | tableNames[i] = "sys_oper_log_" + i; |
| | | } |
| | | return tableNames; |
| | | } |
| | | |
| | | /** |
| | | * 获取所有协议用户表名列表(用于全表扫描场景) |
| | | * |
| | | * @return 所有协议用户表名数组,共10个表 |
| | | */ |
| | | public static String[] getAllAgreementAppUserTableNames() { |
| | | String[] tableNames = new String[10]; |
| | | for (int i = 0; i < 10; i++) { |
| | | tableNames[i] = "xp_agreement_app_user_" + i; |
| | | } |
| | | return tableNames; |
| | | } |
| | | |
| | | /** |
| | | * 获取所有点位表名列表(用于全表扫描场景) |
| | | * |
| | | * @return 所有点位表名数组,共10个表 |
| | | */ |
| | | public static String[] getAllPointTableNames() { |
| | | String[] tableNames = new String[10]; |
| | | for (int i = 0; i < 10; i++) { |
| | | tableNames[i] = "xp_point_" + i; |
| | | } |
| | | return tableNames; |
| | | } |
| | | |
| | | /** |
| | | * 计算分片索引(通用方法) |
| | | * |
| | | * @param shardingValue 分片键值 |
| | | * @param shardingCount 分片数量 |
| | | * @return 分片索引 |
| | | */ |
| | | public static int calculateShardingIndex(Long shardingValue, int shardingCount) { |
| | | if (shardingValue == null) { |
| | | log.warn("shardingValue is null, returning 0"); |
| | | return 0; |
| | | } |
| | | return (int) (shardingValue % shardingCount); |
| | | } |
| | | |
| | | /** |
| | | * 获取指定表的分片表名(通用方法) |
| | | * |
| | | * @param baseTableName 基础表名(如: sys_oper_log) |
| | | * @param shardingValue 分片键值 |
| | | * @param shardingCount 分片数量 |
| | | * @return 实际表名 |
| | | */ |
| | | public static String getShardingTableName(String baseTableName, Long shardingValue, int shardingCount) { |
| | | int index = calculateShardingIndex(shardingValue, shardingCount); |
| | | String tableName = baseTableName + "_" + index; |
| | | log.debug("baseTableName: {}, shardingValue: {}, shardingCount: {}, calculated table name: {}", |
| | | baseTableName, shardingValue, shardingCount, tableName); |
| | | return tableName; |
| | | } |
| | | |
| | | /** |
| | | * 打印所有分表配置信息(用于调试) |
| | | */ |
| | | public static void printShardingInfo() { |
| | | log.info("========== Sharding Table Configuration =========="); |
| | | log.info("sys_oper_log: 10 tables, sharding by userId % 10"); |
| | | log.info("xp_agreement_app_user: 10 tables, sharding by app_user_id % 10"); |
| | | log.info("xp_point: 10 tables, sharding by electronic_area_id % 10"); |
| | | log.info("==================================================="); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.xiaopeng.itinerary.utils; |
| | | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | /** |
| | | * 分表工具使用示例 |
| | | * 演示如何使用 ShardingTableUtil 和 ShardingQueryHelper |
| | | */ |
| | | @Slf4j |
| | | public class ShardingUtilExample { |
| | | |
| | | /** |
| | | * 示例1: 根据用户ID查询订单 |
| | | */ |
| | | public void test(Long electronicAreaId) { |
| | | // 1. 获取对应的分表名 |
| | | String tableName = ShardingTableUtil.getPointTableName(electronicAreaId); |
| | | log.info("用户ID {} 对应的表: {}", electronicAreaId, tableName); |
| | | } |
| | | |
| | | /** |
| | | * 主方法 - 运行所有示例 |
| | | */ |
| | | public static void main(String[] args) { |
| | | ShardingUtilExample example = new ShardingUtilExample(); |
| | | example.test(47L); |
| | | } |
| | | } |
| | | |
| | |
| | | log.info("quoteMatchName:{}", quoteMatchName); |
| | | for (String name : quoteMatchName) { |
| | | String firstElement = redisService.getFirstElement(name); |
| | | log.info("firstElement:{}", firstElement); |
| | | if (firstElement != null) { |
| | | String[] split = firstElement.toString().split("_"); |
| | | // 如果未处理 创建新任务 |
| | |
| | | processPushOrder(quoteName, count, uid, split, orderNo, xpTradingStrategy, distance, duration, planRule); |
| | | |
| | | // 最终没找到车 |
| | | redisService.popFirstElement(quoteName); |
| | | // redisService.popFirstElement(quoteName); |
| | | // 取消订单 |
| | | xpOrderClient.cancelOrder(orderId); |
| | | |
| New file |
| | |
| | | package com.xiaopeng.order.utils; |
| | | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 分表查询辅助工具类 |
| | | * 提供分表查询的SQL构建和批量查询支持 |
| | | * |
| | | * @author auto-generated |
| | | * @date 2026-03-05 |
| | | */ |
| | | @Slf4j |
| | | public class ShardingQueryHelper { |
| | | |
| | | /** |
| | | * 构建单个分表的查询SQL |
| | | * |
| | | * @param baseTableName 基础表名 |
| | | * @param shardingValue 分片键值 |
| | | * @param shardingCount 分片数量 |
| | | * @param whereClause WHERE子句(不包含WHERE关键字) |
| | | * @return 完整的查询SQL |
| | | */ |
| | | public static String buildSingleTableQuery(String baseTableName, Long shardingValue, |
| | | int shardingCount, String whereClause) { |
| | | String tableName = ShardingTableUtil.getShardingTableName(baseTableName, shardingValue, shardingCount); |
| | | StringBuilder sql = new StringBuilder("SELECT * FROM "); |
| | | sql.append(tableName); |
| | | |
| | | if (whereClause != null && !whereClause.trim().isEmpty()) { |
| | | sql.append(" WHERE ").append(whereClause); |
| | | } |
| | | |
| | | String query = sql.toString(); |
| | | log.debug("Generated single table query: {}", query); |
| | | return query; |
| | | } |
| | | |
| | | /** |
| | | * 构建全表扫描的UNION查询SQL(适用于需要查询所有分表的场景) |
| | | * |
| | | * @param baseTableName 基础表名 |
| | | * @param shardingCount 分片数量 |
| | | * @param whereClause WHERE子句(不包含WHERE关键字) |
| | | * @param orderBy 排序字句(不包含ORDER BY关键字) |
| | | * @param limit 限制返回数量,null表示不限制 |
| | | * @return UNION查询SQL |
| | | */ |
| | | public static String buildUnionAllQuery(String baseTableName, int shardingCount, |
| | | String whereClause, String orderBy, Integer limit) { |
| | | StringBuilder sql = new StringBuilder(); |
| | | |
| | | for (int i = 0; i < shardingCount; i++) { |
| | | if (i > 0) { |
| | | sql.append(" UNION ALL "); |
| | | } |
| | | |
| | | sql.append("SELECT * FROM ").append(baseTableName).append("_").append(i); |
| | | |
| | | if (whereClause != null && !whereClause.trim().isEmpty()) { |
| | | sql.append(" WHERE ").append(whereClause); |
| | | } |
| | | } |
| | | |
| | | // 外层排序和限制 |
| | | if (orderBy != null && !orderBy.trim().isEmpty()) { |
| | | sql.insert(0, "SELECT * FROM ("); |
| | | sql.append(") AS union_result ORDER BY ").append(orderBy); |
| | | } |
| | | |
| | | if (limit != null && limit > 0) { |
| | | sql.append(" LIMIT ").append(limit); |
| | | } |
| | | |
| | | String query = sql.toString(); |
| | | log.debug("Generated union all query for {} tables: {}", shardingCount, query); |
| | | return query; |
| | | } |
| | | |
| | | /** |
| | | * 获取需要查询的分表索引列表(基于分片键值范围) |
| | | * |
| | | * @param startValue 起始分片键值 |
| | | * @param endValue 结束分片键值 |
| | | * @param shardingCount 分片数量 |
| | | * @return 需要查询的表索引列表 |
| | | */ |
| | | public static List<Integer> getShardingIndexesInRange(Long startValue, Long endValue, int shardingCount) { |
| | | List<Integer> indexes = new ArrayList<>(); |
| | | |
| | | if (startValue == null || endValue == null || startValue > endValue) { |
| | | log.warn("Invalid range: start={}, end={}, returning all indexes", startValue, endValue); |
| | | for (int i = 0; i < shardingCount; i++) { |
| | | indexes.add(i); |
| | | } |
| | | return indexes; |
| | | } |
| | | |
| | | // 计算涉及的分表索引 |
| | | for (long value = startValue; value <= endValue; value++) { |
| | | int index = (int) (value % shardingCount); |
| | | if (!indexes.contains(index)) { |
| | | indexes.add(index); |
| | | } |
| | | } |
| | | |
| | | log.debug("Range query from {} to {} covers indexes: {}", startValue, endValue, indexes); |
| | | return indexes; |
| | | } |
| | | |
| | | /** |
| | | * 构建基于索引列表的UNION查询 |
| | | * |
| | | * @param baseTableName 基础表名 |
| | | * @param tableIndexes 要查询的表索引列表 |
| | | * @param whereClause WHERE子句 |
| | | * @param orderBy 排序字句 |
| | | * @param limit 限制数量 |
| | | * @return UNION查询SQL |
| | | */ |
| | | public static String buildUnionQueryByIndexes(String baseTableName, List<Integer> tableIndexes, |
| | | String whereClause, String orderBy, Integer limit) { |
| | | if (tableIndexes == null || tableIndexes.isEmpty()) { |
| | | log.warn("No table indexes provided, returning empty query"); |
| | | return ""; |
| | | } |
| | | |
| | | StringBuilder sql = new StringBuilder(); |
| | | |
| | | for (int i = 0; i < tableIndexes.size(); i++) { |
| | | if (i > 0) { |
| | | sql.append(" UNION ALL "); |
| | | } |
| | | |
| | | int tableIndex = tableIndexes.get(i); |
| | | sql.append("SELECT * FROM ").append(baseTableName).append("_").append(tableIndex); |
| | | |
| | | if (whereClause != null && !whereClause.trim().isEmpty()) { |
| | | sql.append(" WHERE ").append(whereClause); |
| | | } |
| | | } |
| | | |
| | | // 外层排序和限制 |
| | | if (orderBy != null && !orderBy.trim().isEmpty()) { |
| | | sql.insert(0, "SELECT * FROM ("); |
| | | sql.append(") AS union_result ORDER BY ").append(orderBy); |
| | | } |
| | | |
| | | if (limit != null && limit > 0) { |
| | | sql.append(" LIMIT ").append(limit); |
| | | } |
| | | |
| | | String query = sql.toString(); |
| | | log.debug("Generated union query for indexes {}: {}", tableIndexes, query); |
| | | return query; |
| | | } |
| | | |
| | | /** |
| | | * 构建订单相关表的批量查询SQL示例 |
| | | */ |
| | | public static class OrderQueryBuilder { |
| | | |
| | | /** |
| | | * 根据用户ID查询订单 |
| | | */ |
| | | public static String queryOrderByUserId(Long appUserId, String additionalWhere) { |
| | | String tableName = ShardingTableUtil.getOrderTableName(appUserId); |
| | | StringBuilder sql = new StringBuilder("SELECT * FROM ").append(tableName); |
| | | sql.append(" WHERE app_user_id = ").append(appUserId); |
| | | |
| | | if (additionalWhere != null && !additionalWhere.trim().isEmpty()) { |
| | | sql.append(" AND ").append(additionalWhere); |
| | | } |
| | | |
| | | return sql.toString(); |
| | | } |
| | | |
| | | /** |
| | | * 根据订单ID查询订单详情 |
| | | */ |
| | | public static String queryOrderDetailByOrderId(Long orderId) { |
| | | String tableName = ShardingTableUtil.getOrderDetailTableName(orderId); |
| | | return "SELECT * FROM " + tableName + " WHERE order_id = " + orderId; |
| | | } |
| | | |
| | | /** |
| | | * 根据订单ID查询订单支付信息 |
| | | */ |
| | | public static String queryOrderPaymentByOrderId(Long orderId) { |
| | | String tableName = ShardingTableUtil.getOrderPaymentTableName(orderId); |
| | | return "SELECT * FROM " + tableName + " WHERE order_id = " + orderId; |
| | | } |
| | | |
| | | /** |
| | | * 根据订单ID查询订单评价 |
| | | */ |
| | | public static String queryOrderEvaluateByOrderId(Long orderId) { |
| | | String tableName = ShardingTableUtil.getOrderEvaluateTableName(orderId); |
| | | return "SELECT * FROM " + tableName + " WHERE order_id = " + orderId; |
| | | } |
| | | |
| | | /** |
| | | * 根据订单ID查询订单取消记录 |
| | | */ |
| | | public static String queryOrderCancelByOrderId(Long orderId) { |
| | | String tableName = ShardingTableUtil.getOrderCancelTableName(orderId); |
| | | return "SELECT * FROM " + tableName + " WHERE order_id = " + orderId; |
| | | } |
| | | |
| | | /** |
| | | * 查询所有订单(全表扫描) |
| | | */ |
| | | public static String queryAllOrders(String whereClause, String orderBy, Integer limit) { |
| | | return ShardingQueryHelper.buildUnionAllQuery("xp_order", 20, whereClause, orderBy, limit); |
| | | } |
| | | |
| | | /** |
| | | * 查询所有订单详情(全表扫描) |
| | | */ |
| | | public static String queryAllOrderDetails(String whereClause, String orderBy, Integer limit) { |
| | | return ShardingQueryHelper.buildUnionAllQuery("xp_order_detail", 20, whereClause, orderBy, limit); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 分表查询计数工具 |
| | | */ |
| | | public static class ShardingCountHelper { |
| | | |
| | | /** |
| | | * 构建分表计数查询(UNION ALL + SUM) |
| | | */ |
| | | public static String buildCountQuery(String baseTableName, int shardingCount, String whereClause) { |
| | | StringBuilder sql = new StringBuilder("SELECT SUM(cnt) AS total_count FROM ("); |
| | | |
| | | for (int i = 0; i < shardingCount; i++) { |
| | | if (i > 0) { |
| | | sql.append(" UNION ALL "); |
| | | } |
| | | |
| | | sql.append("SELECT COUNT(*) AS cnt FROM ").append(baseTableName).append("_").append(i); |
| | | |
| | | if (whereClause != null && !whereClause.trim().isEmpty()) { |
| | | sql.append(" WHERE ").append(whereClause); |
| | | } |
| | | } |
| | | |
| | | sql.append(") AS count_result"); |
| | | |
| | | String query = sql.toString(); |
| | | log.debug("Generated count query: {}", query); |
| | | return query; |
| | | } |
| | | |
| | | /** |
| | | * 统计所有订单数量 |
| | | */ |
| | | public static String countAllOrders(String whereClause) { |
| | | return buildCountQuery("xp_order", 20, whereClause); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 打印查询示例 |
| | | */ |
| | | public static void printQueryExamples() { |
| | | log.info("========== Sharding Query Examples =========="); |
| | | log.info("1. Query order by userId 12345:"); |
| | | log.info(" {}", OrderQueryBuilder.queryOrderByUserId(12345L, "status = 1")); |
| | | log.info(""); |
| | | log.info("2. Query order detail by orderId 67890:"); |
| | | log.info(" {}", OrderQueryBuilder.queryOrderDetailByOrderId(67890L)); |
| | | log.info(""); |
| | | log.info("3. Query all orders with status=1 (UNION ALL):"); |
| | | log.info(" {}", OrderQueryBuilder.queryAllOrders("status = 1", "create_time DESC", 100)); |
| | | log.info(""); |
| | | log.info("4. Count all orders:"); |
| | | log.info(" {}", ShardingCountHelper.countAllOrders("status IN (9, 10)")); |
| | | log.info("============================================="); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.xiaopeng.order.utils; |
| | | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | /** |
| | | * 分表工具类 |
| | | * 根据 ShardingSphere 配置计算实际的分表名称 |
| | | * |
| | | * @author auto-generated |
| | | * @date 2026-03-05 |
| | | */ |
| | | @Slf4j |
| | | public class ShardingTableUtil { |
| | | |
| | | /** |
| | | * 根据用户ID计算订单表的实际表名 |
| | | * 分片规则: xp_order_${app_user_id % 20} |
| | | * |
| | | * @param appUserId 用户ID |
| | | * @return 实际表名,如: xp_order_5 |
| | | */ |
| | | public static String getOrderTableName(Long appUserId) { |
| | | if (appUserId == null) { |
| | | log.warn("appUserId is null, returning default table name"); |
| | | return "xp_order_0"; |
| | | } |
| | | int tableIndex = (int) (appUserId % 20); |
| | | String tableName = "xp_order_" + tableIndex; |
| | | log.debug("appUserId: {}, calculated table name: {}", appUserId, tableName); |
| | | return tableName; |
| | | } |
| | | |
| | | /** |
| | | * 根据订单ID计算订单取消表的实际表名 |
| | | * 分片规则: xp_order_cancel_${order_id % 20} |
| | | * |
| | | * @param orderId 订单ID |
| | | * @return 实际表名,如: xp_order_cancel_5 |
| | | */ |
| | | public static String getOrderCancelTableName(Long orderId) { |
| | | if (orderId == null) { |
| | | log.warn("orderId is null, returning default table name"); |
| | | return "xp_order_cancel_0"; |
| | | } |
| | | int tableIndex = (int) (orderId % 20); |
| | | String tableName = "xp_order_cancel_" + tableIndex; |
| | | log.debug("orderId: {}, calculated table name: {}", orderId, tableName); |
| | | return tableName; |
| | | } |
| | | |
| | | /** |
| | | * 根据订单ID计算订单详情表的实际表名 |
| | | * 分片规则: xp_order_detail_${order_id % 20} |
| | | * |
| | | * @param orderId 订单ID |
| | | * @return 实际表名,如: xp_order_detail_5 |
| | | */ |
| | | public static String getOrderDetailTableName(Long orderId) { |
| | | if (orderId == null) { |
| | | log.warn("orderId is null, returning default table name"); |
| | | return "xp_order_detail_0"; |
| | | } |
| | | int tableIndex = (int) (orderId % 20); |
| | | String tableName = "xp_order_detail_" + tableIndex; |
| | | log.debug("orderId: {}, calculated table name: {}", orderId, tableName); |
| | | return tableName; |
| | | } |
| | | |
| | | /** |
| | | * 根据订单ID计算订单评价表的实际表名 |
| | | * 分片规则: xp_order_evaluate_${order_id % 20} |
| | | * |
| | | * @param orderId 订单ID |
| | | * @return 实际表名,如: xp_order_evaluate_5 |
| | | */ |
| | | public static String getOrderEvaluateTableName(Long orderId) { |
| | | if (orderId == null) { |
| | | log.warn("orderId is null, returning default table name"); |
| | | return "xp_order_evaluate_0"; |
| | | } |
| | | int tableIndex = (int) (orderId % 20); |
| | | String tableName = "xp_order_evaluate_" + tableIndex; |
| | | log.debug("orderId: {}, calculated table name: {}", orderId, tableName); |
| | | return tableName; |
| | | } |
| | | |
| | | /** |
| | | * 根据订单ID计算订单支付表的实际表名 |
| | | * 分片规则: xp_order_payment_${order_id % 20} |
| | | * |
| | | * @param orderId 订单ID |
| | | * @return 实际表名,如: xp_order_payment_5 |
| | | */ |
| | | public static String getOrderPaymentTableName(Long orderId) { |
| | | if (orderId == null) { |
| | | log.warn("orderId is null, returning default table name"); |
| | | return "xp_order_payment_0"; |
| | | } |
| | | int tableIndex = (int) (orderId % 20); |
| | | String tableName = "xp_order_payment_" + tableIndex; |
| | | log.debug("orderId: {}, calculated table name: {}", orderId, tableName); |
| | | return tableName; |
| | | } |
| | | |
| | | /** |
| | | * 根据任务ID计算订单重新分配表的实际表名 |
| | | * 分片规则: xp_order_reassign_${task_id % 10} |
| | | * |
| | | * @param taskId 任务ID |
| | | * @return 实际表名,如: xp_order_reassign_5 |
| | | */ |
| | | public static String getOrderReassignTableName(Long taskId) { |
| | | if (taskId == null) { |
| | | log.warn("taskId is null, returning default table name"); |
| | | return "xp_order_reassign_0"; |
| | | } |
| | | int tableIndex = (int) (taskId % 10); |
| | | String tableName = "xp_order_reassign_" + tableIndex; |
| | | log.debug("taskId: {}, calculated table name: {}", taskId, tableName); |
| | | return tableName; |
| | | } |
| | | |
| | | /** |
| | | * 获取所有订单表名列表(用于全表扫描场景) |
| | | * |
| | | * @return 所有订单表名数组,共20个表 |
| | | */ |
| | | public static String[] getAllOrderTableNames() { |
| | | String[] tableNames = new String[20]; |
| | | for (int i = 0; i < 20; i++) { |
| | | tableNames[i] = "xp_order_" + i; |
| | | } |
| | | return tableNames; |
| | | } |
| | | |
| | | /** |
| | | * 获取所有订单取消表名列表(用于全表扫描场景) |
| | | * |
| | | * @return 所有订单取消表名数组,共20个表 |
| | | */ |
| | | public static String[] getAllOrderCancelTableNames() { |
| | | String[] tableNames = new String[20]; |
| | | for (int i = 0; i < 20; i++) { |
| | | tableNames[i] = "xp_order_cancel_" + i; |
| | | } |
| | | return tableNames; |
| | | } |
| | | |
| | | /** |
| | | * 获取所有订单详情表名列表(用于全表扫描场景) |
| | | * |
| | | * @return 所有订单详情表名数组,共20个表 |
| | | */ |
| | | public static String[] getAllOrderDetailTableNames() { |
| | | String[] tableNames = new String[20]; |
| | | for (int i = 0; i < 20; i++) { |
| | | tableNames[i] = "xp_order_detail_" + i; |
| | | } |
| | | return tableNames; |
| | | } |
| | | |
| | | /** |
| | | * 获取所有订单评价表名列表(用于全表扫描场景) |
| | | * |
| | | * @return 所有订单评价表名数组,共20个表 |
| | | */ |
| | | public static String[] getAllOrderEvaluateTableNames() { |
| | | String[] tableNames = new String[20]; |
| | | for (int i = 0; i < 20; i++) { |
| | | tableNames[i] = "xp_order_evaluate_" + i; |
| | | } |
| | | return tableNames; |
| | | } |
| | | |
| | | /** |
| | | * 获取所有订单支付表名列表(用于全表扫描场景) |
| | | * |
| | | * @return 所有订单支付表名数组,共20个表 |
| | | */ |
| | | public static String[] getAllOrderPaymentTableNames() { |
| | | String[] tableNames = new String[20]; |
| | | for (int i = 0; i < 20; i++) { |
| | | tableNames[i] = "xp_order_payment_" + i; |
| | | } |
| | | return tableNames; |
| | | } |
| | | |
| | | /** |
| | | * 获取所有订单重新分配表名列表(用于全表扫描场景) |
| | | * |
| | | * @return 所有订单重新分配表名数组,共10个表 |
| | | */ |
| | | public static String[] getAllOrderReassignTableNames() { |
| | | String[] tableNames = new String[10]; |
| | | for (int i = 0; i < 10; i++) { |
| | | tableNames[i] = "xp_order_reassign_" + i; |
| | | } |
| | | return tableNames; |
| | | } |
| | | |
| | | /** |
| | | * 计算分片索引(通用方法) |
| | | * |
| | | * @param shardingValue 分片键值 |
| | | * @param shardingCount 分片数量 |
| | | * @return 分片索引 |
| | | */ |
| | | public static int calculateShardingIndex(Long shardingValue, int shardingCount) { |
| | | if (shardingValue == null) { |
| | | log.warn("shardingValue is null, returning 0"); |
| | | return 0; |
| | | } |
| | | return (int) (shardingValue % shardingCount); |
| | | } |
| | | |
| | | /** |
| | | * 获取指定表的分片表名(通用方法) |
| | | * |
| | | * @param baseTableName 基础表名(如: xp_order) |
| | | * @param shardingValue 分片键值 |
| | | * @param shardingCount 分片数量 |
| | | * @return 实际表名 |
| | | */ |
| | | public static String getShardingTableName(String baseTableName, Long shardingValue, int shardingCount) { |
| | | int index = calculateShardingIndex(shardingValue, shardingCount); |
| | | String tableName = baseTableName + "_" + index; |
| | | log.debug("baseTableName: {}, shardingValue: {}, shardingCount: {}, calculated table name: {}", |
| | | baseTableName, shardingValue, shardingCount, tableName); |
| | | return tableName; |
| | | } |
| | | |
| | | /** |
| | | * 打印所有分表配置信息(用于调试) |
| | | */ |
| | | public static void printShardingInfo() { |
| | | log.info("========== Sharding Table Configuration =========="); |
| | | log.info("xp_order: 20 tables, sharding by app_user_id % 20"); |
| | | log.info("xp_order_cancel: 20 tables, sharding by order_id % 20"); |
| | | log.info("xp_order_detail: 20 tables, sharding by order_id % 20"); |
| | | log.info("xp_order_evaluate: 20 tables, sharding by order_id % 20"); |
| | | log.info("xp_order_payment: 20 tables, sharding by order_id % 20"); |
| | | log.info("xp_order_reassign: 10 tables, sharding by task_id % 10"); |
| | | log.info("==================================================="); |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.xiaopeng.order; |
| | | |
| | | import com.xiaopeng.order.utils.ShardingQueryHelper; |
| | | import com.xiaopeng.order.utils.ShardingTableUtil; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | /** |
| | | * 分表工具使用示例 |
| | | * 演示如何使用 ShardingTableUtil 和 ShardingQueryHelper |
| | | */ |
| | | @Slf4j |
| | | public class ShardingUtilExample { |
| | | |
| | | /** |
| | | * 示例1: 根据用户ID查询订单 |
| | | */ |
| | | public void example1_queryOrderByUserId(Long userId) { |
| | | // 1. 获取对应的分表名 |
| | | String tableName = ShardingTableUtil.getOrderTableName(userId); |
| | | log.info("用户ID {} 对应的订单表: {}", userId, tableName); |
| | | |
| | | // 2. 构建查询SQL(在实际开发中,推荐使用MyBatis的动态SQL) |
| | | String sql = String.format("SELECT * FROM %s WHERE app_user_id = %d AND status = 1", |
| | | tableName, userId); |
| | | log.info("查询SQL: {}", sql); |
| | | } |
| | | |
| | | /** |
| | | * 示例2: 根据订单ID查询订单详细信息 |
| | | */ |
| | | public void example2_queryOrderDetails() { |
| | | Long orderId = 67890L; |
| | | |
| | | // 查询订单详情表 |
| | | String detailTableName = ShardingTableUtil.getOrderDetailTableName(orderId); |
| | | log.info("订单ID {} 对应的详情表: {}", orderId, detailTableName); |
| | | |
| | | // 查询订单支付表 |
| | | String paymentTableName = ShardingTableUtil.getOrderPaymentTableName(orderId); |
| | | log.info("订单ID {} 对应的支付表: {}", orderId, paymentTableName); |
| | | |
| | | // 查询订单评价表 |
| | | String evaluateTableName = ShardingTableUtil.getOrderEvaluateTableName(orderId); |
| | | log.info("订单ID {} 对应的评价表: {}", orderId, evaluateTableName); |
| | | |
| | | // 使用QueryHelper构建查询 |
| | | String detailSql = ShardingQueryHelper.OrderQueryBuilder.queryOrderDetailByOrderId(orderId); |
| | | log.info("订单详情查询SQL: {}", detailSql); |
| | | } |
| | | |
| | | /** |
| | | * 示例3: 查询所有订单(全表扫描 - UNION ALL) |
| | | */ |
| | | public void example3_queryAllOrders() { |
| | | // 场景:查询所有状态为已完成的订单,按创建时间倒序,取前100条 |
| | | String sql = ShardingQueryHelper.OrderQueryBuilder.queryAllOrders( |
| | | "status IN (9, 10)", // WHERE条件 |
| | | "create_time DESC", // 排序 |
| | | 100 // 限制数量 |
| | | ); |
| | | |
| | | log.info("全表查询SQL (已简化显示):"); |
| | | log.info(sql.substring(0, Math.min(500, sql.length())) + "..."); |
| | | } |
| | | |
| | | /** |
| | | * 示例4: 统计订单数量 |
| | | */ |
| | | public void example4_countOrders() { |
| | | // 统计所有已完成的订单数量 |
| | | String countSql = ShardingQueryHelper.ShardingCountHelper.countAllOrders( |
| | | "status IN (9, 10)" |
| | | ); |
| | | |
| | | log.info("订单统计SQL:"); |
| | | log.info(countSql); |
| | | } |
| | | |
| | | /** |
| | | * 示例5: 批量获取表名 |
| | | */ |
| | | public void example5_getAllTableNames() { |
| | | // 获取所有订单分表的名称 |
| | | String[] allTables = ShardingTableUtil.getAllOrderTableNames(); |
| | | |
| | | log.info("所有订单分表(共{}个):", allTables.length); |
| | | for (String table : allTables) { |
| | | log.info(" - {}", table); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 示例6: 计算分表索引 |
| | | */ |
| | | public void example6_calculateShardingIndex() { |
| | | Long[] userIds = {100L, 200L, 12345L, 67890L, 999999L}; |
| | | |
| | | log.info("用户ID与分表索引映射:"); |
| | | for (Long userId : userIds) { |
| | | int index = ShardingTableUtil.calculateShardingIndex(userId, 20); |
| | | String tableName = ShardingTableUtil.getOrderTableName(userId); |
| | | log.info(" 用户ID: {} -> 索引: {} -> 表名: {}", userId, index, tableName); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 示例7: 通用分表查询 |
| | | */ |
| | | public void example7_genericShardingQuery() { |
| | | // 使用通用方法构建查询 |
| | | String sql = ShardingQueryHelper.buildSingleTableQuery( |
| | | "xp_order", // 基础表名 |
| | | 12345L, // 分片键值 |
| | | 20, // 分片数量 |
| | | "status = 1 AND create_time > '2026-01-01'" // WHERE条件 |
| | | ); |
| | | |
| | | log.info("通用分表查询SQL: {}", sql); |
| | | } |
| | | |
| | | /** |
| | | * 示例8: 构建UNION ALL查询 |
| | | */ |
| | | public void example8_buildUnionAllQuery() { |
| | | String unionSql = ShardingQueryHelper.buildUnionAllQuery( |
| | | "xp_order", // 基础表名 |
| | | 20, // 分片数量 |
| | | "status = 1", // WHERE条件 |
| | | "create_time DESC", // 排序 |
| | | 1000 // LIMIT |
| | | ); |
| | | |
| | | log.info("UNION ALL查询SQL (前500字符):"); |
| | | log.info(unionSql.substring(0, Math.min(500, unionSql.length())) + "..."); |
| | | } |
| | | |
| | | /** |
| | | * 主方法 - 运行所有示例 |
| | | */ |
| | | public static void main(String[] args) { |
| | | log.info("========================================"); |
| | | log.info("分表工具使用示例"); |
| | | log.info("========================================\n"); |
| | | |
| | | ShardingUtilExample example = new ShardingUtilExample(); |
| | | |
| | | log.info(">>> 示例1: 根据用户ID查询订单"); |
| | | example.example1_queryOrderByUserId(8107912L); |
| | | // log.info(""); |
| | | // |
| | | // log.info(">>> 示例2: 根据订单ID查询订单详细信息"); |
| | | // example.example2_queryOrderDetails(); |
| | | // log.info(""); |
| | | // |
| | | // log.info(">>> 示例3: 查询所有订单(全表扫描)"); |
| | | // example.example3_queryAllOrders(); |
| | | // log.info(""); |
| | | // |
| | | // log.info(">>> 示例4: 统计订单数量"); |
| | | // example.example4_countOrders(); |
| | | // log.info(""); |
| | | // |
| | | // log.info(">>> 示例5: 批量获取表名"); |
| | | // example.example5_getAllTableNames(); |
| | | // log.info(""); |
| | | // |
| | | // log.info(">>> 示例6: 计算分表索引"); |
| | | // example.example6_calculateShardingIndex(); |
| | | // log.info(""); |
| | | // |
| | | // log.info(">>> 示例7: 通用分表查询"); |
| | | // example.example7_genericShardingQuery(); |
| | | // log.info(""); |
| | | // |
| | | // log.info(">>> 示例8: 构建UNION ALL查询"); |
| | | // example.example8_buildUnionAllQuery(); |
| | | // log.info(""); |
| | | // |
| | | // // 打印分表配置信息 |
| | | // log.info("\n========================================"); |
| | | // ShardingTableUtil.printShardingInfo(); |
| | | // |
| | | // // 打印查询示例 |
| | | // log.info(""); |
| | | // ShardingQueryHelper.printQueryExamples(); |
| | | // |
| | | // log.info("========================================"); |
| | | // log.info("所有示例执行完成!"); |
| | | // log.info("========================================"); |
| | | } |
| | | } |
| | | |