Java分布式数据范围API怎么过滤

wen java案例 1

本文目录导读:

Java分布式数据范围API怎么过滤

  1. 基于MyBatis-Plus的插件实现(最常见)
  2. 基于ShardingSphere的联邦查询过滤
  3. 使用Spring Data JPA的Specification
  4. 基于Redis缓存的范围过滤
  5. Elasticsearch范围过滤
  6. MongoDB范围过滤
  7. 最佳实践建议

在Java分布式系统中,实现数据范围API的过滤通常涉及以下几种主流方案和技术选型:

基于MyBatis-Plus的插件实现(最常见)

自定义拦截器

@Component
@Intercepts({@Signature(
    type = StatementHandler.class, 
    method = "prepare", 
    args = {Connection.class, Integer.class}
)})
public class RangeFilterInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaObject = MetaObject.forObject(statementHandler, 
            SystemMetaObject.DEFAULT_OBJECT_FACTORY, 
            SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, 
            new DefaultReflectorFactory());
        // 获取原始SQL
        String originalSql = statementHandler.getBoundSql().getSql();
        // 动态拼接范围过滤条件
        String filteredSql = addRangeFilter(originalSql);
        // 设置修改后的SQL
        metaObject.setValue("delegate.boundSql.sql", filteredSql);
        return invocation.proceed();
    }
    private String addRangeFilter(String sql) {
        // 从上下文获取当前用户的权限范围
        UserContext userContext = UserContextHolder.get();
        String rangeCondition = buildRangeCondition(userContext);
        // 在WHERE条件后追加范围过滤
        if (sql.toUpperCase().contains("WHERE")) {
            return sql + " AND " + rangeCondition;
        } else {
            return sql + " WHERE " + rangeCondition;
        }
    }
}

注解驱动模式

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataRange {
    String fieldName() default "org_id";
    Class<? extends RangeHandler> handler() default DefaultRangeHandler.class;
}
// 使用时
@Service
public class UserService {
    @DataRange(fieldName = "dept_id", handler = DeptRangeHandler.class)
    public List<User> queryUsers(QueryParam param) {
        // 正常查询逻辑,拦截器自动追加范围过滤
    }
}

基于ShardingSphere的联邦查询过滤

// 配置数据分片策略,自动实现范围路由
@Configuration
public class ShardingConfig {
    @Bean
    public ShardingRuleConfiguration shardingRuleConfig() {
        ShardingRuleConfiguration config = new ShardingRuleConfiguration();
        // 按组织ID范围分片
        TableRuleConfiguration orderRule = new TableRuleConfiguration("t_order");
        orderRule.setTableShardingStrategyConfig(
            new StandardShardingStrategyConfiguration("org_id", 
                new OrgRangeShardingAlgorithm()));
        config.getTableRuleConfigs().add(orderRule);
        return config;
    }
}
public class OrgRangeShardingAlgorithm 
    implements RangeShardingAlgorithm<Long> {
    @Override
    public Collection<String> doSharding(
            Collection<String> availableTargetNames, 
            RangeShardingValue<Long> shardingValue) {
        Range<Long> range = shardingValue.getValueRange();
        // 根据范围计算实际需要查询的分片
        return calculateTargetTables(range);
    }
}

使用Spring Data JPA的Specification

@Service
public class UserDataService {
    public Page<User> queryUsersWithRange(Pageable pageable) {
        Specification<User> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            // 获取当前用户的数据权限范围
            DataRange range = SecurityUtils.getCurrentUserDataRange();
            // 如果是部门范围
            if (range.getType().equals(DataRangeType.DEPT)) {
                predicates.add(cb.equal(
                    root.get("departmentId"), range.getDeptId()));
            }
            // 如果是自定义范围
            if (range.getType().equals(DataRangeType.CUSTOM)) {
                predicates.add(root.get("orgId").in(range.getOrgIds()));
            }
            // 如果是所有数据,不添加任何条件
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        return userRepository.findAll(spec, pageable);
    }
}

基于Redis缓存的范围过滤

@Component
public class RangeCacheFilter {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    // 构建数据权限缓存
    public void buildUserDataRangeCache(Long userId) {
        List<Long> accessibleOrgIds = getAccessibleOrgs(userId);
        // 存储到Redis,设置过期时间
        String key = "data:range:" + userId;
        redisTemplate.opsForSet().add(key, accessibleOrgIds.toArray());
        redisTemplate.expire(key, 1, TimeUnit.HOURS);
    }
    // 使用缓存进行过滤
    public <T> List<T> filterByRange(List<T> dataList, 
            Function<T, Long> idExtractor, Long userId) {
        Set<Object> accessibleIds = redisTemplate.opsForSet()
            .members("data:range:" + userId);
        return dataList.stream()
            .filter(item -> accessibleIds.contains(idExtractor.apply(item)))
            .collect(Collectors.toList());
    }
}

Elasticsearch范围过滤

@Service
public class ESDocumentService {
    @Autowired
    private ElasticsearchRestTemplate esTemplate;
    public SearchHits<Document> searchWithRangeFilter(
            String keyword, Long orgId, Pageable pageable) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        // 添加关键词搜索
        if (StringUtils.hasText(keyword)) {
            queryBuilder.withQuery(QueryBuilders.matchQuery("content", keyword));
        }
        // 添加组织范围过滤
        queryBuilder.withFilter(QueryBuilders.termQuery("orgId", orgId));
        // 或者范围查询
        queryBuilder.withFilter(QueryBuilders.rangeQuery("createTime")
            .gte("2024-01-01")
            .lte("2024-12-31"));
        queryBuilder.withPageable(pageable);
        return esTemplate.search(queryBuilder.build(), Document.class);
    }
}

MongoDB范围过滤

@Service
public class MongoDataService {
    @Autowired
    private MongoTemplate mongoTemplate;
    public List<Product> getProductsWithRange(ProductQuery query) {
        Criteria criteria = new Criteria();
        // 价格范围过滤
        if (query.getMinPrice() != null && query.getMaxPrice() != null) {
            criteria.and("price").gte(query.getMinPrice())
                .lte(query.getMaxPrice());
        }
        // 数据权限范围过滤
        UserContext user = UserContextHolder.get();
        if (user.getDataScope() == DataScope.DEPT) {
            criteria.and("deptId").is(user.getDeptId());
        } else if (user.getDataScope() == DataScope.ORG) {
            criteria.and("orgId").in(user.getAccessibleOrgIds());
        }
        Query mongoQuery = new Query(criteria);
        return mongoTemplate.find(mongoQuery, Product.class);
    }
}

最佳实践建议

  1. 分层设计:范围过滤应在Service层统一处理,DAO层保持纯净
  2. 缓存策略:频繁使用的范围规则应缓存,避免重复计算
  3. 性能监控:对大规模数据的范围过滤要进行性能监控和优化
  4. 兼容性处理:考虑MySQL、Elasticsearch、MongoDB等异构数据源的统一过滤逻辑
  5. 熔断机制:当范围过滤导致查询性能下降时,应有降级策略

选择具体的实现方案时,需要根据:

  • 数据量规模
  • 实时性要求
  • 系统复杂度
  • 团队技术栈

来权衡选择最适合的方案。

抱歉,评论功能暂时关闭!