NHibernate HQL 生成器支持 SQL Server 2016 时态表
Posted
技术标签:
【中文标题】NHibernate HQL 生成器支持 SQL Server 2016 时态表【英文标题】:NHibernate HQL Generator to support SQL Server 2016 temporal tables 【发布时间】:2018-06-25 03:12:45 【问题描述】:我正在尝试在 NHibernate 4.x 中实现对 SQL Server 2016 时态表的基本支持。这个想法是从
更改 SQL 语句SELECT * FROM Table t0
到
SELECT * FROM Table FOR SYSTEM_TIME AS OF '2018-01-16 00:00:00' t0
您可以在 SQL Server 2016 here中找到有关临时表的更多信息
不幸的是,我还没有找到在表名和它的别名之间插入FOR FOR SYSTEM_TIME AS OF '...'
语句的任何方法。我不确定自定义方言是否支持这一点。我现在唯一可行的解决方案是在额外的WHERE
中附加FOR SYSTEM_TIME
语句,我的输出SQL 看起来像这样
SELECT * FROM Table t0 WHERE FOR SYSTEM_TIME AS OF '2018-01-16 00:00:00'=1
为此,我实现了生成器和方言如下:
public static class AuditableExtensions
public static bool AsOf(this IAuditable entity, DateTime date)
return true;
public static IQueryable<T> Query<T>(this ISession session, DateTime asOf) where T : IAuditable
return session.Query<T>().Where(x => x.AsOf(asOf));
public class ForSystemTimeGenerator : BaseHqlGeneratorForMethod
public static readonly string ForSystemTimeAsOfString = "FOR SYSTEM_TIME AS OF";
public ForSystemTimeGenerator()
SupportedMethods = new[]
ReflectionHelper.GetMethod(() => AuditableExtensions.AsOf(null, DateTime.MinValue))
;
public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject,
ReadOnlyCollection<Expression> arguments,
HqlTreeBuilder treeBuilder,
IHqlExpressionVisitor visitor)
return treeBuilder.BooleanMethodCall(nameof(AuditableExtensions.AsOf), new[]
visitor.Visit(arguments[1]).AsExpression()
);
public class MsSql2016Dialect : MsSql2012Dialect
public MsSql2016Dialect()
RegisterFunction(nameof(AuditableExtensions.AsOf), new SQLFunctionTemplate(
NHibernateUtil.Boolean,
$"ForSystemTimeGenerator.ForSystemTimeAsOfString ?1?2=1"));
谁能提供任何更好的方法或示例,我可以使用它来继续前进并在表名及其别名之间插入FOR SYSTEM_TIME AS OF
语句?目前我能看到的唯一解决方案是在OnPrepareStatement
中更改SessionInterceptor
中的SQL,但我相信有更好的方法...
【问题讨论】:
repo 中最新的方言是MsSql2012 此外,FOR SYSTEM_TIME AS OF
子句是表的提示,不是 Where
子句的布尔谓词。
我知道,是否可以从生成器中附加它?
根据@PanagiotisKanavos 的评论,您可以将this 作为一个注视点。您将无法使用直接追加,但必须搜索表名并插入......可能在别名之后?只是一个想法。
谢谢,但我正在寻找一种解决方案,我不必手动将生成的 SQL 更改为 SessionInterceptor 中的 SqlString。仍然相信使用 HqlGenerator 有更好的解决方案... :)
【参考方案1】:
在 NHibernate 参考 5.1 NHibernate Reference 中有关于使用 NHibernate 的临时表的信息
19.1 节中的示例展示了如何使用临时表:
首先定义一个过滤器:
<filter-def name="effectiveDate">
<filter-param name="asOfDate" type="date"/>
</filter-def>
然后将它附加到一个类:
<class name="Employee" table="Employee For System_Time All" ...>
...
<many-to-one name="Department" column="dept_id" class="Department"/>
<property name="EffectiveStartDate" type="date" column="eff_start_dt"/>
<property name="EffectiveEndDate" type="date" column="eff_end_dt"/>
...
<!--
Note that this assumes non-terminal records have an eff_end_dt set to
a max db date for simplicity-sake
-->
<filter name="effectiveDate"
condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
</class>
那么你需要在会话上启用过滤器:
ISession session = ...;
session.EnableFilter("effectiveDate").SetParameter("asOfDate", DateTime.Today);
var results = session.CreateQuery("from Employee as e where e.Salary > :targetSalary")
.SetInt64("targetSalary", 1000000L)
.List<Employee>();
希望这能让人们开始。
【讨论】:
在你的例子中,临时表在哪里?它只是添加到 WHERE 语句中的一个过滤器【参考方案2】:创建一个 UDF(将所需时间作为参数)并调用它,而不是直接使用表。
【讨论】:
【参考方案3】:DECLARE @String1 NVARCHAR(100),
@String2 NVARCHAR(100) ,
@DateTime DATETIME= '2018-01-16 00:00:00';
SET @String1 = 'SELECT * FROM Table2';
SET @String2 = @String1 + ' FOR SYSTEM_TIME AS OF '''
+ CONVERT(VARCHAR(20), @DateTime, 120) + '''';
SELECT @String2;
【讨论】:
以上是关于NHibernate HQL 生成器支持 SQL Server 2016 时态表的主要内容,如果未能解决你的问题,请参考以下文章