问题
I am using NHibernate 3.2.1. The following query
return session.Query<TmTranslation>()
.Where(x => x.TranslationUnit.Document.Job == job)
.OrderBy(x => x.Id)
.ToList();
produces this SQL:
select tmtranslation.id,
tmtranslation.text,
tmtranslation.fk_id_translation_unit
from "TRANSLATION" tmtranslation
inner join "TRANSLATION_UNIT" tmunit
on tmtranslation.fk_id_translation_unit = tmunit.id
inner join "TRANSLATION_UNIT" tmunit2
on tmtranslation.fk_id_translation_unit = tmunit2.id
inner join "DOCUMENT" tmdocument
on tmunit2.fk_id_document = tmdocument.id
where tmdocument.fk_id_job = 174
order by tmtranslation.id asc
My mapping:
public class TmTranslationMap : ClassMap<TmTranslation>
{
public TmTranslationMap()
{
Table("\"TRANSLATION\"");
LazyLoad();
Id(x => x.Id, "id").GeneratedBy.HiLo("hilo", "hilo_translation", "200");
Map(x => x.Text).Column("text");
References<TmTranslationUnit>(x => x.TranslationUnit, "fk_id_translation_unit").Cascade.None();
DynamicUpdate();
}
}
public class TmTranslationUnitMap: ClassMap<TmTranslationUnit>
{
public TmTranslationUnitMap()
{
Table("\"TRANSLATION_UNIT\"");
LazyLoad();
Id(x => x.Id, "id").GeneratedBy.HiLo("hilo", "hilo_translation_unit", "200");
HasMany(x => x.Translations).Inverse().KeyColumn("fk_id_translation_unit").Cascade.None();
References<TmDocument>(x => x.Document, "fk_id_document").Not.Nullable().Cascade.None();
}
}
public class TmDocumentMap : ClassMap<TmDocument>
{
public TmDocumentMap()
{
Table("\"DOCUMENT\"");
LazyLoad();
Id(x => x.Id, "id").GeneratedBy.HiLo("hilo", "hilo_document", "50");
References<TmJob>(x => x.Job, "fk_id_job").Not.Nullable();
HasMany(x => x.TranslationUnits).Inverse().KeyColumn("fk_id_document").Cascade.SaveUpdate();
}
}
As you can see, one of the JOINs is useless and only makes the query run slower. Is there any way I can make the query not produce unnecessary JOINs using Linq?
Thank you.
回答1:
It seems that the LINQ to NHibernate provider has trouble navigating associations from the target to the source table and generates a separate join each time it encounters such an association: one for Translation -> TranslationUnit and one for TranslationUnit to Document.
The trick is help the provider navigate in the other direction: Document -> TranlationUnit -> Translation like this:
var items=(from doc in session.Query<Document>()
from tu in doc.TranslationUnits
from translation in tu.Translations
where doc.Job ==job
orderby translation.Id
select translation).ToList();
The resulting SQL query is:
SELECT translatio2_.Id as Id1_, translatio2_.Text as Text1_, translatio2_.TmTranslationUnitId as TmTransl3_1_
FROM [Document] AS document0_ INNER JOIN
TmTranslationUnit AS translatio1_ ON document0_.Id = translatio1_.DocumentId INNER JOIN
TmTranslation AS translatio2_ ON translatio1_.Id = translatio2_.TmTranslationUnitId
WHERE (document0_.JobId = @p0)
ORDER BY translatio2_.Id
来源:https://stackoverflow.com/questions/7291616/linq-to-nhibernate-produces-unnecessary-joins