过滤器未应用于多对一参考

时间:2010-09-09 19:26:09

标签: nhibernate fluent-nhibernate nhibernate-mapping

我有一个奇怪的场景,我有一个表引用一个包含用户特定行的视图的表。视图具有唯一的行ID,但从不用于引用该项。而是从客户端ID,用户ID和对象密钥的组合派生唯一值。

    public BarMap()
    {
        Table(DatabaseObject.UserBars);

        Id(x => x.Id, "Resource_Id");

        Map(x => x.ClientId, "Client_Id");
        Map(x => x.UserId, "User_Id");

        Map(x => x.Key, "Bar_Key")
            .Length(10);
        Map(x => x.Name, "Bar_Name")
            .Length(255);

我有一个过滤器应用于应该基于当前用户和客户端上下文在WHERE子句中启动的类。实际上,当我尝试直接检索Bar时,我会看到这些过滤器适用。但是,当我检索一个名为Foo的对象引用一个Bar对象时,将在辅助SELECT调用中跳过这些过滤器。

public class FooMap : ClassMap<Foo>
{
    public FooMap()
    {
        Table(DatabaseObject.Foos);

        References<Bar>(x => x.Bar, "BarId")
            .PropertyRef(x => x.Key)
            .Cascade.None()
            .Fetch.Join()
            .NotFound.Exception();

基于上面你会发现Foo会执行JOIN来水合Bar对象,但它似乎跳过它并坚持检索Foo对象。我怀疑发生了辅助SELECT调用,因为NH库正在尝试根据Key的唯一值来检索对象(在日志中

Loader.Loader - SELECT this_.Id as Id12_2_, this_.ClientId as ClientId12_2_,  this_.BarId as BarId12_2_, Foo1_.Id as Id13_0_, Foo1_.ClientId as ClientId13_0_, Foo1_.Name as Name13_0_, Bar2_.Resource_Id as Resource1_4_1_, Bar2_.Client_Id as Client7_4_1_, Bar2_.User_Id as User8_4_1_, Bar2_.Bar_Key as Bar9_4_1_, Bar2_.Bar_Name as Bar10_4_1_ FROM Foos this_ inner join FooSchedules Foo1_ on this_.ScheduleId=Foo1_.Id inner join User_Bars Bar2_ on this_.BarId=Bar2_.Bar_Key and (Bar2_.Client_Id = :p0 OR Bar2_.Client_Id IS NULL) and Bar2_.User_Id = :p1 WHERE (Foo1_.ClientId = :p2 OR Foo1_.ClientId IS NULL) and (this_.ClientId = :p3 OR this_.ClientId IS NULL) AND Foo1_.Name = :p4 and Bar2_.Bar_Key = :p5 and Bar2_.Client_Id = :p6 and Bar2_.User_Id = :p7
Loader.Loader - processing result set
Loader.Loader - result set row: 0

我可以在这里看到它加载了Bar,但由于某些原因它没有保湿吗?

Loader.Loader - result row: EntityKey[Core.Domain.FooSchedule#2929992], EntityKey[Core.Domain.Bar#470090], EntityKey[Core.Domain.Foo#3211664]
Loader.Loader - Initializing object from DataReader: [Core.Domain.FooSchedule#2929992]
Loader.Loader - Initializing object from DataReader: [Core.Domain.Foo#3211664]
Loader.Loader - done processing result set (1 rows)
Loader.Loader - total objects hydrated: 2
Engine.TwoPhaseLoad - resolving associations for [Core.Domain.FooSchedule#2929992]
Engine.Loading.LoadContexts - creating collection wrapper:[Core.Domain.FooSchedule.Foos#2929992]
Engine.TwoPhaseLoad - done materializing entity [Core.Domain.FooSchedule#2929992]
Engine.TwoPhaseLoad - resolving associations for [Core.Domain.Foo#3211664]
Engine.Loading.LoadContexts - creating collection wrapper:[Core.Domain.Foo.Items#3211664]

这是尝试重新加载Bar的地方,但不包括导致它返回多行/炸毁的过滤器....

Loader.Entity.AbstractEntityLoader - Static select for entity Core.Domain.Bar: SELECT Bar0_.Resource_Id as Resource1_4_0_, Bar0_.Client_Id as Client7_4_0_, Bar0_.User_Id as User8_4_0_, Bar0_.Bar_Key as Bar9_4_0_, Bar0_.Bar_Name as Bar10_4_0_ FROM User_Bars Bar0_ WHERE Bar0_.Bar_Key=?
Loader.Loader - loading entity: [Core.Domain.Bar#ABFDBC01]
Engine.QueryParameters - BindParameters(Named:NHibernate.Type.StringType) ABFDBC01 -> [0]
Loader.Loader - SELECT Bar0_.Resource_Id as Resource1_4_0_, Bar0_.Client_Id as Client7_4_0_, Bar0_.User_Id as User8_4_0_, Bar0_.Bar_Key as Bar9_4_0_, Bar0_.Bar_Name as Bar10_4_0_ FROM User_Bars Bar0_ WHERE Bar0_.Bar_Key=:p0
Loader.Loader - processing result set

我喜欢重构Bar视图,但除非绝对必要,否则时间限制将无法实现。有关如何应用过滤器的任何想法?或者是否有关于如何进行特定于上下文的对象引用的指导?

我在JBoss方面找到了一些参考,Hibernate也可能会遇到potentially related design issue。查看NHibernate源代码的代码似乎以EntityJoinWalker为中心:

SqlStringBuilder whereCondition = WhereString(Alias, uniqueKey, batchSize)
    //include the discriminator and class-level where, but not filters
    .Add(persister.FilterFragment(Alias, new CollectionHelper.EmptyMapClass<string, IFilter>()));

如果我将其更改为一切正常,但我不确定更改的影响是什么,并且评论并未真正指出为什么应该排除过滤器。

SqlStringBuilder whereCondition = WhereString(Alias, uniqueKey, batchSize)
    .Add(persister.FilterFragment(Alias, enabledFilters));

帮助?!

1 个答案:

答案 0 :(得分:0)

通过在地图类中应用过滤来解决(NH 2.1.2中的新内容):

Where("Client_Id = :clientFilter.clientId AND User_Id = :userFilter.userId");