核心是运行时通过反射识别实体类字段注解,结合主题上下文动态拼装SQL字段与WHERE条件;遍历字段获取@Column、@TenantField等元数据,按schema-per-tenant或column-based策略重写字段引用,并缓存SqlField结构化结果。

在多主题切换的后端数据引擎中,用反射动态分析注解类来重组 SQL 字段,核心在于:**运行时识别实体类上的字段级注解(如 @Column、@Table、@TenantId 等),结合当前主题(tenant / schema / catalog)上下文,动态拼装出符合该主题语义的 SQL 字段列表和 WHERE 条件**。不依赖编译期 APT 或 XML 配置,全程靠反射 + 注解元数据驱动。
提取实体类字段与注解元数据
遍历目标实体类的所有声明字段(Class.getDeclaredFields()),跳过静态/瞬态字段,对每个字段调用 field.getAnnotation(...) 获取自定义注解:
- @Column(name = "user_name", alias = "name") → 提取物理列名、别名、是否参与查询
-
@TenantField(strategy = SCHEMA) → 标记该字段需按主题隔离,可能用于自动追加
WHERE tenant_id = ?或切换 schema - @JsonAlias("nick")(可选扩展)→ 支持字段别名映射到 API 层,反向影响 SELECT 别名生成
根据主题上下文动态重写字段引用
拿到字段元数据后,不是直接拼 SELECT user_name AS name,而是注入主题策略:
- 若主题模式为 schema-per-tenant:字段前缀自动加上
tenant_abc.(如tenant_abc.user_name AS name) - 若主题模式为 column-based:对所有非主键字段,自动包裹
COALESCE(user_name, '')防空(按主题规则定制) - 若字段标注
@TenantField且主题启用了租户强制过滤,则该字段自动加入 WHERE 条件,且参数绑定由引擎统一管理
组装 SQL 字段片段并缓存解析结果
将处理后的字段生成结构化中间表示(如 SqlField{raw: "tenant_xyz.user_name", alias: "name", isTenantFilter: true}),再聚合为完整 SELECT 子句或 WHERE 条件。关键点:
- 用
ConcurrentHashMap<class>, List<sqlfield>></sqlfield></class>缓存已解析的字段列表,避免重复反射开销 - 缓存 key 应包含主题类型(如
SchemaMode.SCHEMA_PER_TENANT),不同主题策略下视为不同解析结果 - 字段别名冲突时(如两个字段都设 alias="id"),抛出
SqlMappingException并附带类名+字段路径,便于定位
与 MyBatis / JPA 兼容的轻量集成方式
不侵入 ORM 框架,而是作为前置处理器:
- MyBatis:实现
Interceptor,拦截StatementHandler.prepare(),在 SQL 解析前替换#{entity}占位符为动态生成的字段串 - JPA:提供
@Query的扩展注解(如@DynamicSelect(entity = User.class)),通过RepositoryMethodMetadata在执行前重写 Query 对象的 JPQL 或原生 SQL - 纯 JDBC 场景:封装
SqlBuilder工具类,传入 Class + ThemeContext,返回PreparedSql(含 sql 字符串 + 参数顺序列表)

















