如何从 SelectConditionStep<Record> 中提取已用 table
How to extract used table from SelectConditionStep<Record>
我正在延长 。在 Hibernate 模型中,使用了 @Filter
注释,我想将同样的 'default filter' 应用于 jOOQ 查询。当我将 jOOQ 查询传递给 nativeQuery(org.jooq.Query query, Class<E> type)
时,我想知道是否可以从 jOOQ 查询的 FROM
子句中提取 table (TableImpl<?,?>
) ( org.jooq.Query
).
这是我试过的:
private static <E> SelectConditionStep<Record> applyDefaultFilters(Class<E> type, SelectConditionStep<Record> query)
{
if (BaseOrganizationModel.class.isAssignableFrom(type)) {
query
.getQuery()
.addConditions(
query
.getQuery()
.asTable()
.field("organization_id", Long.class)
.eq(currentOrganization().id));
if (SoftDeletableModel.class.isAssignableFrom(type)) {
query
.getQuery()
.addConditions(query.getQuery().asTable().field("deleted", Boolean.class).eq(false));
}
}
return query;
}
结果是这样SQL,这不是我想要的。我想让它过滤对应的table.
select distinct `EventGroup`.*
from `EventGroup`
where (
...
and `alias_100341773`.`organization_id` = ?
and `alias_17045196`.`deleted` = ?
)
我想要这个
select distinct `EventGroup`.*
from `EventGroup`
where (
...
and `EventGroup`.`organization_id` = ?
and `EventGroup`.`deleted` = ?
)
这可能吗?如果没有,还有哪些可能的其他路线? (除了明显将 table 传递给函数)
使用 jOOQ 3.16 查询对象模型API
jOOQ 3.16 引入了 new, experimental (as of 3.16) query object model API, which can be traversed.
在任何 Select
上,只需调用 Select.$from()
即可访问包含的 table 列表的不可修改视图。
针对临时案例的另一种动态 SQL 方法
每次尝试改变现有查询时,问问自己,是否有使用 a more functional, immutable approach do dynamic SQL 的更优雅的方法?与其将额外的谓词附加到查询中,不如从函数中生成谓词?
private static Condition defaultFilters(Class<?> type, Table<?> table) {
Condition result = noCondition();
if (BaseOrganizationModel.class.isAssignableFrom(type)) {
result = result.and(table.field("organization_id", Long.class)
.eq(currentOrganization().id));
if (SoftDeletableModel.class.isAssignableFrom(type))
result = result.and(not(table.field("deleted", Boolean.class)))
}
return result;
}
现在,当您构建查询时,您可以添加过滤器:
ctx.select(T.A, T.B)
.from(T)
.where(T.X.eq(1))
.and(defaultFilters(myType, T))
.fetch();
一种通用的方法来转换您的 SQL
如果您真的想改变查询(例如,在所有查询的实用程序中),那么转换方法可能更适合。有不同的方法来解决这个问题。
使用视图
一些 RDBMS 可以访问视图中的会话变量。在 Oracle 中,您将在视图内部为 organization_id
设置一些 SYS_CONTEXT
变量,然后仅查询(可能更新 table)视图而不是 table直接。 MySQL 不幸的是不能做同样的事情,见 Is there any equivalent to ORACLE SYS_CONTEXT('USERENV', 'OS_USER') in MYSQL?
I've described this approach here in this blog post。这种方法的优点是您永远不会忘记设置谓词(您可以使用 CI/CD 测试验证您的视图源代码),如果您忘记设置会话上下文变量,视图将不会 return 任何数据,所以这是一种非常安全的方法。
与 WITH CHECK OPTION
子句一起,您甚至可以防止插入错误 organization_id
,从而提高安全性。
在 jOOQ
中使用 VisitListener
这是在 jOOQ 中执行此操作的最强大的方法,并且正是您想要的,但对于所有边缘情况来说也是一种非常棘手的方法。 See this post about implementing row level security in jOOQ. Starting from jOOQ 3.16, there will be better ways to transform your SQL via https://github.com/jOOQ/jOOQ/issues/12425.
请注意,它不适用于不使用任何 jOOQ 查询部分的普通 SQL 模板,也不适用于基于 JDBC 的查询或您系统中可能有的其他查询,因此请谨慎使用此方法,因为您可能会泄露其他组织的数据。
当然,您也可以在 JDBC 层上实现此步骤,使用 jOOQ 的 ParsingConnection
or ParsingDataSource
,这样您也可以拦截第三方 SQL 并附加您的谓词。
这适用于所有 DML 语句,包括 UPDATE
、DELETE
。 INSERT
有点难,因为你必须将 INSERT .. VALUES
转换为 INSERT .. SELECT
,或者如果有人想插入错误的 organization_id
.[= 则抛出异常。 51=]
在 jOOQ 中使用 ExecuteListener
比上面的 VisitListener
方法更老套一些,但通常更容易正确,只需在 WHERE organization_id = ... AND
中用 WHERE organization_id = ... AND
正则表达式替换所有语句的 WHERE
子句=27=].
为了安全起见,您可以拒绝所有没有 WHERE
子句的查询,或者做一些额外的技巧以在正确的位置添加 WHERE
子句以防万一.
使用 jOOQ 相当于 Hibernate 的 @Filter
jOOQ 相当于 Hibernate 的 @Filter
是 Table.where(Condition)
子句。这不是完全等价的,您必须阻止在代码库中直接访问 T
,并确保用户仅通过将 T
替换为 [=40= 的方法访问 T
] 相反。
这种方法目前失去了 T
table 的类型安全性,参见:https://github.com/jOOQ/jOOQ/issues/8012
我正在延长 @Filter
注释,我想将同样的 'default filter' 应用于 jOOQ 查询。当我将 jOOQ 查询传递给 nativeQuery(org.jooq.Query query, Class<E> type)
时,我想知道是否可以从 jOOQ 查询的 FROM
子句中提取 table (TableImpl<?,?>
) ( org.jooq.Query
).
这是我试过的:
private static <E> SelectConditionStep<Record> applyDefaultFilters(Class<E> type, SelectConditionStep<Record> query)
{
if (BaseOrganizationModel.class.isAssignableFrom(type)) {
query
.getQuery()
.addConditions(
query
.getQuery()
.asTable()
.field("organization_id", Long.class)
.eq(currentOrganization().id));
if (SoftDeletableModel.class.isAssignableFrom(type)) {
query
.getQuery()
.addConditions(query.getQuery().asTable().field("deleted", Boolean.class).eq(false));
}
}
return query;
}
结果是这样SQL,这不是我想要的。我想让它过滤对应的table.
select distinct `EventGroup`.*
from `EventGroup`
where (
...
and `alias_100341773`.`organization_id` = ?
and `alias_17045196`.`deleted` = ?
)
我想要这个
select distinct `EventGroup`.*
from `EventGroup`
where (
...
and `EventGroup`.`organization_id` = ?
and `EventGroup`.`deleted` = ?
)
这可能吗?如果没有,还有哪些可能的其他路线? (除了明显将 table 传递给函数)
使用 jOOQ 3.16 查询对象模型API
jOOQ 3.16 引入了 new, experimental (as of 3.16) query object model API, which can be traversed.
在任何 Select
上,只需调用 Select.$from()
即可访问包含的 table 列表的不可修改视图。
针对临时案例的另一种动态 SQL 方法
每次尝试改变现有查询时,问问自己,是否有使用 a more functional, immutable approach do dynamic SQL 的更优雅的方法?与其将额外的谓词附加到查询中,不如从函数中生成谓词?
private static Condition defaultFilters(Class<?> type, Table<?> table) {
Condition result = noCondition();
if (BaseOrganizationModel.class.isAssignableFrom(type)) {
result = result.and(table.field("organization_id", Long.class)
.eq(currentOrganization().id));
if (SoftDeletableModel.class.isAssignableFrom(type))
result = result.and(not(table.field("deleted", Boolean.class)))
}
return result;
}
现在,当您构建查询时,您可以添加过滤器:
ctx.select(T.A, T.B)
.from(T)
.where(T.X.eq(1))
.and(defaultFilters(myType, T))
.fetch();
一种通用的方法来转换您的 SQL
如果您真的想改变查询(例如,在所有查询的实用程序中),那么转换方法可能更适合。有不同的方法来解决这个问题。
使用视图
一些 RDBMS 可以访问视图中的会话变量。在 Oracle 中,您将在视图内部为 organization_id
设置一些 SYS_CONTEXT
变量,然后仅查询(可能更新 table)视图而不是 table直接。 MySQL 不幸的是不能做同样的事情,见 Is there any equivalent to ORACLE SYS_CONTEXT('USERENV', 'OS_USER') in MYSQL?
I've described this approach here in this blog post。这种方法的优点是您永远不会忘记设置谓词(您可以使用 CI/CD 测试验证您的视图源代码),如果您忘记设置会话上下文变量,视图将不会 return 任何数据,所以这是一种非常安全的方法。
与 WITH CHECK OPTION
子句一起,您甚至可以防止插入错误 organization_id
,从而提高安全性。
在 jOOQ
中使用VisitListener
这是在 jOOQ 中执行此操作的最强大的方法,并且正是您想要的,但对于所有边缘情况来说也是一种非常棘手的方法。 See this post about implementing row level security in jOOQ. Starting from jOOQ 3.16, there will be better ways to transform your SQL via https://github.com/jOOQ/jOOQ/issues/12425.
请注意,它不适用于不使用任何 jOOQ 查询部分的普通 SQL 模板,也不适用于基于 JDBC 的查询或您系统中可能有的其他查询,因此请谨慎使用此方法,因为您可能会泄露其他组织的数据。
当然,您也可以在 JDBC 层上实现此步骤,使用 jOOQ 的 ParsingConnection
or ParsingDataSource
,这样您也可以拦截第三方 SQL 并附加您的谓词。
这适用于所有 DML 语句,包括 UPDATE
、DELETE
。 INSERT
有点难,因为你必须将 INSERT .. VALUES
转换为 INSERT .. SELECT
,或者如果有人想插入错误的 organization_id
.[= 则抛出异常。 51=]
在 jOOQ 中使用 ExecuteListener
比上面的 VisitListener
方法更老套一些,但通常更容易正确,只需在 WHERE organization_id = ... AND
中用 WHERE organization_id = ... AND
正则表达式替换所有语句的 WHERE
子句=27=].
为了安全起见,您可以拒绝所有没有 WHERE
子句的查询,或者做一些额外的技巧以在正确的位置添加 WHERE
子句以防万一.
使用 jOOQ 相当于 Hibernate 的 @Filter
jOOQ 相当于 Hibernate 的 @Filter
是 Table.where(Condition)
子句。这不是完全等价的,您必须阻止在代码库中直接访问 T
,并确保用户仅通过将 T
替换为 [=40= 的方法访问 T
] 相反。
这种方法目前失去了 T
table 的类型安全性,参见:https://github.com/jOOQ/jOOQ/issues/8012