【问题标题】:Given a jooq query how do find the tables involved?给定一个 jooq 查询如何找到所涉及的表?
【发布时间】: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?
    }
}

【问题讨论】:

    标签: java sql jooq


    【解决方案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()));
                }
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-25
      • 1970-01-01
      • 2015-05-12
      • 2012-08-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多