Кажется, что NHibernate не выполняет массовую вставку в PostgreSQL - PullRequest
6 голосов
/ 06 января 2011

Я взаимодействую с базой данных PostgreSQL с помощью NHibernate.

Фон

Я сделал несколько простых тестов ... кажется, что для сохранения 300 записей требуется 2 секунды. У меня есть Perl-программа с идентичной функциональностью, но вместо этого я использую прямой SQL, это занимает всего 70% времени. Я не уверен, ожидается ли это. Я думал, что C # / NHibernate будет быстрее или по крайней мере на уровне

Вопросы

Одно из моих наблюдений заключается в том, что (с включенным show_sql) NHibernate выдает INSERT несколько сотен раз вместо объемной INSERT, которая обрабатывает несколько строк. И обратите внимание, я назначаю первичный ключ сам, не используя «родной» генератор.

Это ожидается? Могу ли я в любом случае заставить его выдавать вместо INSERT оператор? Мне кажется, что это может быть одной из областей, где я мог бы ускорить представление.

Ответы [ 2 ]

6 голосов
/ 23 сентября 2011

Как правильно выяснил Стачу: в NHibernate нет * BatchingBatcher (Factory) для PostgreSQL (Npgsql) Как спрашивает стачу: кому-нибудь удалось заставить Nhibarnate делать пакетные вставки в PostgreSQL

Я написал Batcher, который не использует пакетную обработку Npgsql, но манипулирует строкой SQL "oldschool style" (INSERT INTO [..] VALUES (...), (...), ... )

using System;
using System.Collections;
using System.Data;
using System.Diagnostics;
using System.Text;
using Npgsql;

namespace NHibernate.AdoNet
{
    public class PostgresClientBatchingBatcherFactory : IBatcherFactory
    {
        public virtual IBatcher CreateBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
        {
            return new PostgresClientBatchingBatcher(connectionManager, interceptor);
        }
    }

    /// <summary>
    /// Summary description for PostgresClientBatchingBatcher.
    /// </summary>
    public class PostgresClientBatchingBatcher : AbstractBatcher
    {

        private int batchSize;
        private int countOfCommands = 0;
        private int totalExpectedRowsAffected;
        private StringBuilder sbBatchCommand;
        private int m_ParameterCounter;

        private IDbCommand currentBatch;

        public PostgresClientBatchingBatcher(ConnectionManager connectionManager, IInterceptor interceptor)
            : base(connectionManager, interceptor)
        {
            batchSize = Factory.Settings.AdoBatchSize;
        }


        private string NextParam()
        {
            return ":p" + m_ParameterCounter++;
        }

        public override void AddToBatch(IExpectation expectation)
        {
            if(expectation.CanBeBatched && !(CurrentCommand.CommandText.StartsWith("INSERT INTO") && CurrentCommand.CommandText.Contains("VALUES")))
            {
                //NonBatching behavior
                IDbCommand cmd = CurrentCommand;
                LogCommand(CurrentCommand);
                int rowCount = ExecuteNonQuery(cmd);
                expectation.VerifyOutcomeNonBatched(rowCount, cmd);
                currentBatch = null;
                return;
            }

            totalExpectedRowsAffected += expectation.ExpectedRowCount;
            log.Info("Adding to batch");


            int len = CurrentCommand.CommandText.Length;
            int idx = CurrentCommand.CommandText.IndexOf("VALUES");
            int endidx = idx + "VALUES".Length + 2;

            if (currentBatch == null)
            {
                // begin new batch. 
                currentBatch = new NpgsqlCommand();   
                sbBatchCommand = new StringBuilder();
                m_ParameterCounter = 0;

                string preCommand = CurrentCommand.CommandText.Substring(0, endidx);
                sbBatchCommand.Append(preCommand);
            }
            else
            {
                //only append Values
                sbBatchCommand.Append(", (");
            }

            //append values from CurrentCommand to sbBatchCommand
            string values = CurrentCommand.CommandText.Substring(endidx, len - endidx - 1);
            //get all values
            string[] split = values.Split(',');

            ArrayList paramName = new ArrayList(split.Length);
            for (int i = 0; i < split.Length; i++ )
            {
                if (i != 0)
                    sbBatchCommand.Append(", ");

                string param = null;
                if (split[i].StartsWith(":"))   //first named parameter
                {
                    param = NextParam();
                    paramName.Add(param);
                }
                else if(split[i].StartsWith(" :")) //other named parameter
                {
                    param = NextParam();
                    paramName.Add(param);
                }
                else if (split[i].StartsWith(" "))  //other fix parameter
                {
                    param = split[i].Substring(1, split[i].Length-1);
                }
                else
                {
                    param = split[i];   //first fix parameter
                }

                sbBatchCommand.Append(param);
            }
            sbBatchCommand.Append(")");

            //rename & copy parameters from CurrentCommand to currentBatch
            int iParam = 0;
            foreach (NpgsqlParameter param in CurrentCommand.Parameters)
            {
                param.ParameterName = (string)paramName[iParam++];

                NpgsqlParameter newParam = /*Clone()*/new NpgsqlParameter(param.ParameterName, param.NpgsqlDbType, param.Size, param.SourceColumn, param.Direction, param.IsNullable, param.Precision, param.Scale, param.SourceVersion, param.Value);
                currentBatch.Parameters.Add(newParam);
            }

            countOfCommands++;
            //check for flush
            if (countOfCommands >= batchSize)
            {
                DoExecuteBatch(currentBatch);
            }
        }

        protected override void DoExecuteBatch(IDbCommand ps)
        {
            if (currentBatch != null)
            {
                //Batch command now needs its terminator
                sbBatchCommand.Append(";");

                countOfCommands = 0;

                log.Info("Executing batch");
                CheckReaders();

                //set prepared batchCommandText
                string commandText = sbBatchCommand.ToString();
                currentBatch.CommandText = commandText;

                LogCommand(currentBatch);

                Prepare(currentBatch);

                int rowsAffected = 0;
                try
                {
                    rowsAffected = currentBatch.ExecuteNonQuery();
                }
                catch (Exception e)
                {
                    if(Debugger.IsAttached)
                        Debugger.Break();
                    throw;
                }

                Expectations.VerifyOutcomeBatched(totalExpectedRowsAffected, rowsAffected);

                totalExpectedRowsAffected = 0;
                currentBatch = null;
                sbBatchCommand = null;
                m_ParameterCounter = 0;
            }
        }

        protected override int CountOfStatementsInCurrentBatch
        {
            get { return countOfCommands; }
        }

        public override int BatchSize
        {
            get { return batchSize; }
            set { batchSize = value; }
        }
    }
}
2 голосов
/ 06 апреля 2011

Я также обнаружил, что NHibernate не выполняет пакетную вставку в PostgreSQL. Я определил две возможные причины:

1) Драйвер Npgsql не поддерживает пакетные вставки / обновления ( см. Форум )

2) NHibernate не имеет * BatchingBatcher (Factory) для PostgreSQL (Npgsql). Я попытался использовать драйвер Devart dotConnect с NHibernate (я написал собственный драйвер для NHibernate), но он все еще не работал.

Полагаю, этот драйвер должен также реализовывать интерфейс IEmbeddedBatcherFactoryProvider, но для меня это не тривиально (использование одного для Oracle не сработало;))

Кто-нибудь сумел заставить Nhibarnate выполнить пакетную вставку в PostgreSQL или может подтвердить мой вывод?

...