NHibernate在使用JOIN条件创建DELETE SQL时出现异常。

huangapple go评论65阅读模式
英文:

NHibernate exception when creating SQL for DELETE with a condition from a JOIN

问题

技术栈

SQL Server 2019, NHibernate 5.3.5, .NET Framework 4.8

设置

数据库
CREATE TABLE First (
	Id UNIQUEIDENTIFIER PRIMARY KEY
);
CREATE TABLE Second (
	Id UNIQUEIDENTIFIER PRIMARY KEY,
	FirstId UNIQUEIDENTIFIER FOREIGN KEY REFERENCES First(Id),
	Criteria BIT NOT NULL,
);
映射
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Test" namespace="Test">
  <class name="First" table="First" lazy="false">
    <id name="Id" type="Guid" column="Id">
      <generator class="guid"/>
    </id>
  </class>
</hibernate-mapping>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Test" namespace="Test">
  <class name="Second" table="Second" lazy="false">
    <id name="Id" type="Guid" column="Id">
      <generator class="guid"/>
    </id>
    <property name="FirstId" column="FirstId"/>
    <property name="Criteria" column="Criteria"/>
  </class>
</hibernate-mapping>
配置

Second 表和 First 表之间是一对一映射关系。

我的方言是 NHibernate.Dialect.MsSql2012Dialect(我没有找到更新的版本),我的驱动程序是 NHibernate.Driver.MicrosoftDataSqlClientDriver(尽管我也尝试过 NHibernate.Driver.SqlClientDriver)。

场景

我想从 First 表中删除记录,但是要使用 LINQ 在 Second 表中筛选对应的记录。这是我的尝试:

CurrentSession.Query<First>()
	.Join(
		CurrentSession.Query<Second>(),
		first => first.Id,
		second => second.FirstId,
		(first, second) => new { First = first, Second = second }
	)
	.Where(joined => joined.Second.Criteria == 1)
	.Select(joined => joined.First)
	.Delete();

我得到了以下异常:

NHibernate.Exceptions.GenericADOException: could not execute update query[SQL: delete from First inner join Second second_1 on (FirstId=Id) where Criteria = 1] ---> System.Data.SqlClient.SqlException: Incorrect syntax near the keyword 'inner'.

稍微整理一下,这似乎生成了这个 SQL:

DELETE FROM First INNER JOIN Second s ON (FirstId=Id) WHERE Criteria = 1

这似乎是正确的通用 SQL,至少基于我在网上找到的示例。我不是一个 SQL 专家,所以我不确定这是否归因于 SQL 方言,但至少 SQL Server 似乎期望这种格式(特别是在 DELETE 后面注意表的别名):

DELETE f FROM First f INNER JOIN Second s ON (s.FirstId=f.Id) WHERE s.Criteria = 1

我确实手动在服务器上运行了第二个查询,它可以正常工作。

生成上述 SQL 的原因是配置吗?是否可以调整查询或映射以生成带有表别名的 SQL?我注意到它确实为连接的表创建了别名(如上面的 second),但它没有在查询中使用该别名。尽管仅有这还不足够,似乎第一个表(在 FROM 中指定的表)仍然需要使用别名。

是否可以在不手动编写 SQL 的情况下完成这个操作,如果可以,如何实现?

英文:

Tech Stack

SQL Server 2019, NHibernate 5.3.5, .NET Framework 4.8

Setup

Database
CREATE TABLE First (
	Id UNIQUEIDENTIFIER PRIMARY KEY
);
CREATE TABLE Second (
	Id UNIQUEIDENTIFIER PRIMARY KEY,
	FirstId UNIQUEIDENTIFIER FOREIGN KEY REFERENCES First(Id),
	Criteria BIT NOT NULL,
);
Mappings
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
&lt;hibernate-mapping xmlns=&quot;urn:nhibernate-mapping-2.2&quot; assembly=&quot;Test&quot; namespace=&quot;Test&quot;&gt;
  &lt;class name=&quot;First&quot; table=&quot;First&quot; lazy=&quot;false&quot;&gt;
    &lt;id name=&quot;Id&quot; type=&quot;Guid&quot; column=&quot;Id&quot;&gt;
      &lt;generator class=&quot;guid&quot;/&gt;
    &lt;/id&gt;
  &lt;/class&gt;
&lt;/hibernate-mapping&gt;
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&gt;
&lt;hibernate-mapping xmlns=&quot;urn:nhibernate-mapping-2.2&quot; assembly=&quot;Test&quot; namespace=&quot;Test&quot;&gt;
  &lt;class name=&quot;Second&quot; table=&quot;Second&quot; lazy=&quot;false&quot;&gt;
    &lt;id name=&quot;Id&quot; type=&quot;Guid&quot; column=&quot;Id&quot;&gt;
      &lt;generator class=&quot;guid&quot;/&gt;
    &lt;/id&gt;
    &lt;property name=&quot;FirstId&quot; column=&quot;FirstId&quot;/&gt;
    &lt;property name=&quot;Criteria&quot; column=&quot;Criteria&quot;/&gt;
  &lt;/class&gt;
&lt;/hibernate-mapping&gt;
Configuration

The Second and First table map one-to-one.

My dialect is NHibernate.Dialect.MsSql2012Dialect (I didn't find anything more recent), my driver is NHibernate.Driver.MicrosoftDataSqlClientDriver (though I also tried with NHibernate.Driver.SqlClientDriver).

Scenario

I want to delete records from the First table, but filter on the corresponding record from the Second table using LINQ. This was my attempt:

CurrentSession.Query&lt;First&gt;()
	.Join(
		CurrentSession.Query&lt;Second&gt;(),
		first =&gt; first.Id,
		second =&gt; second.FirstId,
		(first, second) =&gt; new { First = first, Second = second }
	)
	.Where(joined =&gt; joined.Second.Criteria == 1)
	.Select(joined =&gt; joined.First)
	.Delete();

I get the following exception:

> NHibernate.Exceptions.GenericADOException: could not execute update query[SQL: delete from First inner join Second second_1 on (FirstId=Id) where Criteria = 1] ---> System.Data.SqlClient.SqlException: Incorrect syntax near the keyword 'inner'.

Cleaned up a bit, this seems to generate this SQL:

DELETE FROM First INNER JOIN Second s ON (FirstId=Id) WHERE Criteria = 1

This seems to be correct generic SQL, at least based on examples I'm finding online. I'm not an SQL expert, so I'm not sure if this is down to the SQL dialect, but it seems that SQL Server at least expects this format (notice the table aliases, especially after DELETE):

DELETE f FROM First f INNER JOIN Second s ON (s.FirstId=f.Id) WHERE s.Criteria = 1

I did manually run the second query in the server, and it worked.

Is the reason the above is generated down to the configuration? Can I adjust the query or mapping somehow to generate SQL with table aliases? I noticed that it does create an alias for the joined table (second, above), but it doesn't use that alias in the query. Though that alone is still not enough, the first table (designated in the FROM) still needs to be aliased, it seems.

Can this be done without writing the SQL manually, and if so, how?

答案1

得分: 1

NHibernate在主查询中使用联接的DML查询存在问题。但将其移到子查询中应该可以工作(至少在NHibernate 5.3+中是如此)。请尝试以下代码:

var itemsToDeleteSubquery = CurrentSession.Query<First>()
    .Join(
        CurrentSession.Query<Second>(),
        first => first.Id,
        second => second.FirstId,
        (first, second) => new { First = first, Second = second }
    )
    .Where(joined => joined.Second.Criteria == 1)
    .Select(joined => joined.First);

CurrentSession.Query<First>().Where(e => itemsToDeleteSubquery.Contains(e)).Delete();
英文:

NHibernate has troubles with DML queries with joins in main query. But moving it to subquery should work (at least with NHibernate 5.3+). Try this:

var itemsToDeleteSubquery = CurrentSession.Query&lt;First&gt;()
    .Join(
        CurrentSession.Query&lt;Second&gt;(),
        first =&gt; first.Id,
        second =&gt; second.FirstId,
        (first, second) =&gt; new { First = first, Second = second }
    )
    .Where(joined =&gt; joined.Second.Criteria == 1)
    .Select(joined =&gt; joined.First);

    CurrentSession.Query&lt;First&gt;().Where(e =&gt; itemsToDeleteSubquery.Contains(e)).Delete();

huangapple
  • 本文由 发表于 2023年6月30日 02:28:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76583751.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定