Class DBETool

java.lang.Object
io.r2mo.dbe.common.DBETool

public class DBETool extends Object

DBETool —— MyBatis-Plus 语境下的 Java 侧 GroupBy 与实体标识条件构造辅助工具。

 🧭 设计动机
 - 在 MyBatis-Plus 某些场景下(尤其是动态列、跨表、或开启 ONLY_FULL_GROUP_BY 的严格 SQL 模式时)、
   无法直接或不便在数据库侧编写「无聚合的 GROUP BY」来获得“分桶后的原始记录列表”。
 - 本工具提供在 Java 侧 对已查询的实体集合进行分组(grouping)的能力,
   以及从实体注解(@Identifiers)提取「复合业务主键/标识条件」的能力,便于后续查询或幂等更新。

 📌 使用边界与共识
 
 - ⚠️ 数据库侧 GROUP BY 更适于聚合(SUM/COUNT/AVG...);若仅需“分桶后的原始行”,推荐 Java 侧分组。
 - 🧮 大数据量统计请仍尽量落在数据库侧聚合,Java 侧 groupBy 适合中小批量数据或需要内存内进一步加工的场景。
 - 🧱 @Identifiers 约定用于定义业务标识字段(如 appId/tenantId/enabled 等),便于构造 WHERE 条件。
 
🛠️ 典型用法
 1) Java 侧分组(Map<K, List<T>>):
    List<Order> rows = orderMapper.selectList(...);
    Map<Long, List<Order>> grouped = DBETool.groupBy(rows, "buyerId", Order.class);

 2) 从实体构建查询条件(@Identifiers):
    Order entity = ... // 具有 @Identifiers 注解
    Map<String, Object> where = DBETool.getIdentifier(entity);
    // where 形如:{ "id": 1001, "appId": "...", "tenantId": "...", "enabled": true }
 
⚙️ 性能与并发注意
 - 🚦 当前 groupBy 使用 parallelStream() + groupingBy(...),并发 Collector 会在内部做合并;
   对于中小规模集合可接受,若在高并发/超大集合下更建议:
     a) 使用串行 stream() + groupingBy(...)(更易诊断);
     b) 使用 groupingByConcurrent(...) 获取 ConcurrentMap(需注意下游收集器是否线程安全)。
 - 🧰 若分组键计算较重(反射读取),可考虑预先缓存字段访问器或使用方法引用减少反射次数。
 
✅ 空值与健壮性
 - entities 为空/为 null:建议在调用前做判空,或在工具方法里显式返回空 Map(当前实现未做判空短路)。
 - field 为 null/错误字段:SourceReflect.value(...) 应抛出或返回 null,调用方应留意 NPE/Key 为 null 的分组桶。
 
🧩 与 MyBatis-Plus 的关系
 - 这里的 groupBy 完全在内存侧进行,不依赖 MyBatis-Plus 的 Wrapper 语法。
 - 若你需要 SUM/COUNT 等聚合统计,请优先使用数据库侧 GROUP BY,或编写 XML/@Select 自定义 SQL。
 
Author:
lang : 2025-08-28
  • Constructor Details

    • DBETool

      public DBETool()
  • Method Details

    • getIdentifier

      public static <T> Map<String,Object> getIdentifier(Object entity)
      根据实体类上的 Identifiers 注解,构造该实体的「业务标识条件」映射(形如 WHERE 条件的 K/V)。
       🧠 适用场景
       - ✅ 按业务唯一键做查询/更新/幂等判断:以注解元信息统一声明“标识字段”。
      
       🧩 行为说明
       - 若实体类未声明 @Identifiers:返回 null(调用方可据此回退到主键或其他策略)。
       - 若声明了 @Identifiers:
         1) 读取 identifiers.value() 中列出的字段名,types 到条件 Map;
         2) 根据 ifApp/ifTenant/ifEnabled 标志,附加默认字段:
            - appId    → DefaultField.APP_ID
            - tenantId → DefaultField.TENANT_ID
            - enabled  → DefaultField.IS_ENABLED(固定 true)
      
       🧪 示例
       
       // 假设实体 Order 上有:
       // @Identifiers(value = {"id"}, ifApp = true, ifTenant = true, ifEnabled = true)
      
       Map<String, Object> where = DBETool.getIdentifier(order);
       // 结果可能是:
       // {
       //   "id": 1001,
       //   "appId": "...",
       //   "tenantId": "...",
       //   "enabled": true
       // }
       
      🧰 健壮性 - entity 为 null:返回 null; - 注解存在但某字段值为 null:依然会 types(null),是否允许由上层 SQL 构造策略决定; - 字段名与实体不匹配:SourceReflect.value(...) 可能返回 null 或抛异常,调用方可按需捕获。
      Type Parameters:
      T - 实体类型
      Parameters:
      entity - 带有 Identifiers 注解的实体实例
      Returns:
      业务标识条件 Map;若无注解或 entity 为 null 返回 null