如何在Jooq 3.10中为所有增删查改查询添加动态org_id默认条件
嘿,我刚好有过类似的多租户场景需求,在jOOQ 3.10里可以通过**访问监听器(VisitListener)**来实现全局自动插入动态条件的功能,不用手动修改每一条查询,非常省心。下面给你详细拆解实现步骤:
1. 自定义VisitListener实现类
我们需要实现jOOQ的VisitListener接口(或者继承更方便的DefaultVisitListener),在查询生成的AST阶段自动为SELECT、UPDATE、DELETE语句追加org_id = 动态值的条件。
这里要注意区分两种情况:原查询有没有WHERE子句——如果有就用AND连接新条件,没有就直接添加WHERE子句。
代码示例如下:
import org.jooq.*; import org.jooq.impl.DefaultVisitListener; public class OrgIdGlobalFilterListener extends DefaultVisitListener { @Override public void visitStart(VisitContext ctx) { // 只处理SELECT、UPDATE、DELETE类型的查询 QueryPart queryPart = ctx.queryPart(); if (!(queryPart instanceof SelectQuery || queryPart instanceof UpdateQuery || queryPart instanceof DeleteQuery)) { super.visitStart(ctx); return; } // 从上下文获取动态org_id(这里可以换成你实际的动态值获取方式,比如ThreadLocal) Integer dynamicOrgId = getCurrentDynamicOrgId(); if (dynamicOrgId == null) { super.visitStart(ctx); return; } // 构造org_id条件 Condition orgCondition = DSL.field("org_id").eq(dynamicOrgId); // 针对不同查询类型处理 if (queryPart instanceof SelectQuery) { SelectQuery<?> selectQuery = (SelectQuery<?>) queryPart; if (selectQuery.getWhere() != null) { // 已有WHERE条件,追加AND连接的新条件 selectQuery.addConditions(selectQuery.getWhere().and(orgCondition)); } else { // 没有WHERE条件,直接添加 selectQuery.addConditions(orgCondition); } } else if (queryPart instanceof UpdateQuery) { UpdateQuery<?> updateQuery = (UpdateQuery<?>) queryPart; if (updateQuery.getWhere() != null) { updateQuery.addConditions(updateQuery.getWhere().and(orgCondition)); } else { updateQuery.addConditions(orgCondition); } } else if (queryPart instanceof DeleteQuery) { DeleteQuery<?> deleteQuery = (DeleteQuery<?>) queryPart; if (deleteQuery.getWhere() != null) { deleteQuery.addConditions(deleteQuery.getWhere().and(orgCondition)); } else { deleteQuery.addConditions(orgCondition); } } super.visitStart(ctx); } // 这里替换成你实际的动态值获取逻辑,比如从ThreadLocal、Session、请求上下文拿 private Integer getCurrentDynamicOrgId() { // 示例:从ThreadLocal获取(适合多租户/请求级动态值) return OrgIdContextHolder.getCurrentOrgId(); } }
2. 配置ThreadLocal存储动态值(可选但推荐)
如果你的org_id是请求级动态值(比如多租户场景,每个请求对应不同租户ID),建议用ThreadLocal来存储,这样监听器能随时拿到当前请求的动态值:
public class OrgIdContextHolder { private static final ThreadLocal<Integer> CURRENT_ORG_ID = new ThreadLocal<>(); public static void setCurrentOrgId(Integer orgId) { CURRENT_ORG_ID.set(orgId); } public static Integer getCurrentOrgId() { return CURRENT_ORG_ID.get(); } // 请求结束后记得清理,避免内存泄漏 public static void clear() { CURRENT_ORG_ID.remove(); } }
比如在请求拦截器里:
// 请求开始时设置 OrgIdContextHolder.setCurrentOrgId(当前请求的orgId); // 请求结束时清理 OrgIdContextHolder.clear();
3. 注册监听器到DSLContext
最后一步,把我们自定义的监听器注册到jOOQ的配置中,这样所有通过这个DSLContext执行的查询都会自动带上条件:
// 假设你已经有了数据库连接和方言配置 Connection dbConnection = ...; // 你的数据库连接 SQLDialect dialect = SQLDialect.MYSQL; // 根据你的数据库调整,比如SQLSERVER、POSTGRES等 // 创建jOOQ配置并注册监听器 Configuration jooqConfig = new DefaultConfiguration() .set(dbConnection) .set(dialect) .set(new DefaultVisitListenerProvider(new OrgIdGlobalFilterListener())); // 创建DSLContext实例 DSLContext dsl = DSL.using(jooqConfig);
4. 验证效果
现在执行你的查询,就能看到自动追加的条件了:
原查询:
dsl.selectFrom("table_name").fetch();
生成的SQL:SELECT * FROM table_name WHERE org_id = ?原查询:
dsl.selectFrom("table_name").where("column_name = ?", "testValue").fetch();
生成的SQL:SELECT * FROM table_name WHERE column_name = ? AND org_id = ?
UPDATE和DELETE查询也是一样的逻辑,会自动追加条件~
额外小技巧:跳过特定查询的全局条件
如果某些查询不需要自动追加org_id条件,可以给查询加个特殊标记,比如注释,然后在监听器里判断跳过:
// 不需要全局过滤的查询,加个特定注释 dsl.selectFrom("table_name") .setComment("SKIP_ORG_FILTER") .fetch();
然后在监听器的visitStart方法开头加判断:
// 跳过带有特定注释的查询 if (queryPart instanceof SelectQuery) { SelectQuery<?> selectQuery = (SelectQuery<?>) queryPart; if ("SKIP_ORG_FILTER".equals(selectQuery.getComment())) { super.visitStart(ctx); return; } } // UPDATE和DELETE查询同理
内容的提问来源于stack exchange,提问作者thirumalairaj

