给定一个 jooq 查询如何找到所涉及的表?

Posted

技术标签:

【中文标题】给定一个 jooq 查询如何找到所涉及的表?【英文标题】:Given a jooq query how do find the tables involved? 【发布时间】:2021-10-08 05:16:26 【问题描述】:

我目前正在使用DefaultExecuteListener 挂钩到 jooq 查询生命周期。如何可靠地确定查询是否为 select 语句以及 select 中涉及哪些表(org.jooq.Table)?

@Override
public void executeStart(ExecuteContext context) 
    Set<Table> tables = new HashSet<>();
    if (context.query() != null) 
        context.query(); // How do you check if this is a select statement and retrieve the tables involved?
    

【问题讨论】:

【参考方案1】:

如何可靠地确定查询是否为选择语句

您可以在ExecuteListener 中通过检查context.query() instanceof Select 来做到这一点

select 中涉及到哪些表(org.jooq.Table)?

从 jOOQ 3.15 开始,在 SQL 生成期间访问表达式树的最佳方法是实现 VisitListener。本文展示了一个广泛的 how to implement client side row level security in jOOQ 示例。你需要的会更少。在最简单的情况下,这可能就足够了:

class TableCollector implements DefaultVisitListener 

    Set<Table<?>> tables = new HashSet<>();

    @Override
    public void visitStart(VisitContext ctx) 
        if (ctx.queryPart() instanceof Table)
            tables.add(ctx.queryPart());
    

现在,您只需将这两者粘合在一起即可。

【讨论】:

您能否详细说明如何将 VisitListener 的调用与同一查询上的 ExecuteListener 调用关联起来? 澄清一下:我需要在执行时涉及的表。 @ScottWiedemann:缓慢但简单的解决方案:您可以使用具有来自ExecuteListenerVisitListener 的派生配置重新呈现查询。更快但更棘手的解决方案:为两个听众找到某种方式进行交流,例如使用一些线程局部变量,或一些队列等......【参考方案2】:

通过使用 VisitListener 并结合 ExecuteListener,我能够在执行时获取查询中涉及的 Jooq 表 (org.jooq.Table):

import org.jooq.Table;
import org.jooq.VisitContext;
import org.jooq.impl.DefaultVisitListener;

import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;

public class TableCollector extends DefaultVisitListener 

    private final static ConcurrentHashMap<String, HashSet<Table<?>>> queryToTables = new ConcurrentHashMap<>();

    public static HashSet<Table<?>> getTables(String sqlQuery) 
        return queryToTables.get(sqlQuery);
    

    private <T> T getDataValue(VisitContext context, String key, T defaultValue) 
        T value = (T) context.data(key);
        if (value == null) 
            value = defaultValue;
        
        return value;
    

    private <T> T putDataValue(VisitContext context, String key, T value) 
        return (T) context.data(key, value);
    

    @Override
    public void visitStart(VisitContext context) 
        Integer stackSize = getDataValue(context, "stackSize", 0);
        stackSize++;
        putDataValue(context, "stackSize", stackSize);
        if (context.queryPart() instanceof Table) 
            Table<?> table = (Table<?>) context.queryPart();
            HashSet<Table<?>> tables = getDataValue(context, "tables", new HashSet<>());
            tables.add(table);
            putDataValue(context, "tables", tables);
        
    

    @Override
    public void visitEnd(VisitContext context) 
        Integer stackSize = getDataValue(context, "stackSize", -1);
        stackSize--;
        putDataValue(context, "stackSize", stackSize);
        if (stackSize == 0) 
            HashSet<Table<?>> tables = getDataValue(context, "tables", new HashSet<>());
            String sql = context.context().render();
            if (sql != null) 
                queryToTables.put(sql, new HashSet<>(tables));
            
            tables.clear();
            putDataValue(context, "tables", tables);
        
    

import org.jooq.ExecuteContext;
import org.jooq.Query;
import org.jooq.Table;
import org.jooq.impl.DefaultExecuteListener;

import java.util.HashSet;
import java.util.Set;

public class TableExecuteListener extends DefaultExecuteListener 

    @Override
    public void executeStart(ExecuteContext context) 
        super.executeStart(context);

        // pull the tables from the @link TableCollector for this query
        final Set<Table<?>> tablesInQuery = new HashSet<>();
        if (context.query() != null) 
            tablesInQuery.addAll(TableCollector.getTables(context.sql()));
         else 
            for (Query query : context.batchQueries()) 
                tablesInQuery.addAll(TableCollector.getTables(query.getSQL()));
            
        
    

【讨论】:

以上是关于给定一个 jooq 查询如何找到所涉及的表?的主要内容,如果未能解决你的问题,请参考以下文章

将 JOOQ 查询与 JDBC 事务混合

如何编写查询来清理给定问题的表

JOOQ 连接两个具有相同列名的表

Jooq 中的条件 onDuplicateKeyUpdate

给定两个区域的经度和纬度,如何找到它们之间的距离(以米为单位)。如何在 SQL 中查询..?

如何使用 jOOQ 执行特定查询