【问题标题】:Obscure NHibernate 3.2 Bulk Insert/HQL issue晦涩的 NHibernate 3.2 批量插入/HQL 问题
【发布时间】:2012-04-04 23:14:13
【问题描述】:

我需要根据用户传入的一组日期将事件记录“批量”插入到数据库中。

我的事件实体有一个 StartTime 属性和一个 StopTime 属性。 Event 实体是我的应用程序中的核心实体,因此有许多与之关联的集合和实体引用。这是一个沉重的实体。

所以,我的原始代码让我遍历日期并执行以下操作:

 Session.Save(new Event {StartTime=startdate, StopTime=endDate});

但是这并不能很好地扩展,我们在插入几百个日期时感受到了性能。

所以我想到了将所有日期插入临时表,但我不知道如何使用 HQL 来实现。

所以我的下一个想法是将所有日期作为子查询包含在内,这让我最接近。这是这种方法:

var hql = "INSERT INTO Event (Code, Comment, Description, Status, StartTime, StopTime)
           SELECT ev.Code, ev.Comment, ev.Description, ev.Status, Dates.SDate, Dates.EDate
           FROM Event ev, 
                (
                    SELECT '1/2/2012 3:00:00 PM' as SDate, '1/2/2012 6:00:00 PM' as EDate
                    UNION ALL SELECT '1/3/2012 3:00:00 PM', '1/3/2012 6:00:00 PM' 
                    UNION ALL SELECT '1/4/2012 3:00:00 PM', '1/4/2012 6:00:00 PM'
                ) as Dates
           WHERE ev.Id = :sourceEventId";

Session.CreateQuery(hql).SetInt32("sourceEventId", @event.Id).ExecuteUpdate();

但是 hql 失败,错误代码非常模糊。我在网上的研究告诉我,hql 还不支持 UNION 子句。

现在,我将这种方法重写为 SQL,并通过 Session.CreateSQLQuery() 执行,效果很好。但我不想将它作为 SQL 运行。无论如何,这违背了使用 NHibernate 的目的。

还有其他方法可以使用 HQL(或任何其他 NHibernate 技术)吗?

非常感谢这里的任何帮助!

更新

  1. 我忘了提到这是一个旧版 SQL Server 数据库(我在 sqlite 上运行单元测试),并且所有实体都使用 SQL 的 Identity 列生成它们的 ID。所以任何批处理的尝试都会失败。

  2. 正如我在下面的评论中所解释的,我还尝试了无状态会话方法。 NHibernate 将无法告诉我我需要先保存所有延迟加载的实体。我通过删除这些映射暂时解决了这个问题,它确实有效,而且比常规会话更快。但我最终还是不得不一次插入每个实体。

【问题讨论】:

    标签: c# .net nhibernate hql bulkinsert


    【解决方案1】:

    尝试使用无状态会话而不是会话

    using (var session = sessionFactory.OpenStatelessSession())
    using (var tx = session.BeginTransaction())
    {
    for (int i = 0; i < count; i++)
    {
        session.Insert(yourObjects[i]);
    }
    tx.Commit();
    }
    

    this link is also helpful

    此外,您可以使用 ADO.net SqlBulkCopy 代替无状态会话,check this

    【讨论】:

    • 您好,感谢您的回复!我忘了提到我确实尝试了无状态会话方法,虽然它比常规会话更快,但您仍然必须一次插入 1 个对象。此外,当我尝试无状态时,我在 Event 实体上遇到了延迟加载关系的一些问题。
    【解决方案2】:

    这个呢?

    var sdates = new []{ "1/2/2012 3:00:00 PM", "1/3/2012 3:00:00 PM", "1/4/2012 3:00:00 PM" };
    var edates = new []{ "1/2/2012 6:00:00 PM", "1/3/2012 6:00:00 PM", "1/4/2012 6:00:00 PM" };
    
    var hql = "INSERT INTO Event (Code, Comment, Description, Status, StartTime, StopTime)
           SELECT ev.Code, ev.Comment, ev.Description, ev.Status, :sdate, :edate
           FROM Event ev
           WHERE ev.Id = :sourceEventId";
    
    for(var i=0;i<sdates.Length;i++)
    {
         Session.CreateQuery(hql).SetInt32("sourceEventId", @event.Id)
                                 .SetString("sdate", sdates[i])
                                 .SetString("edate", edates[i])
                                 .ExecuteUpdate();
    }
    

    您可能需要使用 DateTime 而不是字符串并对代码进行一些调整,但这是一个想法。基本上,您将执行三个命令而不是一个,但也不是 n+1(这就是您的扩展问题,对吧?)

    希望对你有帮助。

    【讨论】:

    • 感谢您抽出宝贵时间回答!我很清楚:如果我需要保存 300 个开始/结束日期对,我会发送 300 个插入语句吗?我认为这是我最初的尝试所做的(虽然使用 Session.Save() 不使用 hql)。但这仍然存在每个新日期发送 1 个插入的问题......我真正想做的是发送完整的日期列表并在服务器内运行 1 个查询,并保存所有往返行程。
    • 是的,我以为你只需要 3 个 :)
    • 使用批量插入或仅使用普通 sql 怎么样?使用 NHibernate 的想法是解决 90% 的场景,当您遇到使用 NHibernate 无法解决的性能问题时(假设是这种情况),您转到 sql。
    猜你喜欢
    • 1970-01-01
    • 2011-03-19
    • 2017-05-28
    • 1970-01-01
    • 2011-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多