سؤال

هل هناك حل لإدراج الدُفعات عبر السبات في جدول Postgresql المقسم؟حاليا أتلقى خطأ مثل هذا ...

ERROR org.hibernate.jdbc.AbstractBatcher - Exception executing batch:
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
   at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:61)
   at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:46)
   at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:68)....

لقد وجدت هذا الرابط http://lists.jboss.org/pipermail/hibernate-dev/2007-October/002771.html لكن لا يمكنني العثور على أي مكان على الويب هل تم حل هذه المشكلة أو كيف يمكن التغلب عليها

هل كانت مفيدة؟

المحلول

قد ترغب في تجربة استخدام Batcher مخصص عن طريق تعيين الخاصية hibernate.jdbc.factory_class.التأكد من أن السبات لن يتحقق من عدد تحديثات العمليات المجمعة قد يحل مشكلتك، ويمكنك تحقيق ذلك عن طريق جعل Batcher المخصص الخاص بك يقوم بتوسيع فئة BatchingBatcher، ثم تجاوز الطريقة doExecuteBatch(...) لتبدو كما يلي:

    @Override
    protected void doExecuteBatch(PreparedStatement ps) throws SQLException, HibernateException {
        if ( batchSize == 0 ) {
            log.debug( "no batched statements to execute" );
        }
        else {
            if ( log.isDebugEnabled() ) {
                log.debug( "Executing batch size: " + batchSize );
            }

            try {
//              checkRowCounts( ps.executeBatch(), ps );
                ps.executeBatch();
            }
            catch (RuntimeException re) {
                log.error( "Exception executing batch: ", re );
                throw re;
            }
            finally {
                batchSize = 0;
            }

        }

    }

لاحظ أن الطريقة الجديدة لا تتحقق من نتائج تنفيذ البيانات المعدة.ضع في اعتبارك أن إجراء هذا التغيير قد يؤثر على وضع السبات بطريقة غير متوقعة (أو ربما لا).

نصائح أخرى

يقولون أنه يجب استخدام مشغلين في جدول مقسم أو التعليق التوضيحي @SQLInsert هنا: http://www.redhat.com/f/pdf/jbw/jmlodgenski_940_scaling_hibernate.pdf الصفحات 21-26 (يشير أيضًا إلى @SQLInsert الذي يحدد أسلوب السلسلة).

فيما يلي مثال مع مشغل لاحق لحذف الصف الإضافي في الملف الرئيسي: https://Gist.github.com/copiousfreetime/59067

يظهر إذا كان بإمكانك استخدام RULES بدلاً من مشغلات الإدراج، فيمكنه إرجاع الرقم الصحيح، ولكن فقط باستخدام قاعدة واحدة بدون عبارة WHERE.

المرجع1

المرجع2

المرجع3

قد يكون هناك خيار آخر يتمثل في إنشاء طريقة عرض "تلتف" الجدول المقسم، ثم تقوم بإرجاع الصف الجديد للإشارة إلى تحديث صف ناجح، دون إضافة صف إضافي غير مرغوب فيه عن طريق الخطأ إلى الجدول الرئيسي.

create view tablename_view as select * from tablename; -- create trivial wrapping view

CREATE OR REPLACE FUNCTION partitioned_insert_trigger() -- partitioned insert trigger
RETURNS TRIGGER AS $$
BEGIN
   IF (NEW.partition_key>= 5500000000 AND
       NEW.partition_key <  6000000000) THEN
      INSERT INTO tablename_55_59 VALUES (NEW.*);
   ELSIF (NEW.partition_key >= 5000000000 AND
          NEW.partition_key <  5500000000) THEN
      INSERT INTO tablename_50_54 VALUES (NEW.*);
   ELSIF (NEW.partition_key >= 500000000 AND
          NEW.partition_key  <  1000000000) THEN
      INSERT INTO tablename_5_9 VALUES (NEW.*);
   ELSIF (NEW.partition_key >= 0 AND
          NEW.partition_key <  500000000) THEN
      INSERT INTO tablename_0_4 VALUES (NEW.*);
   ELSE
      RAISE EXCEPTION 'partition key is out of range.  Fix the trigger function';
   END IF;
   RETURN NEW; -- RETURN NEW in this case, typically you'd return NULL from this trigger, but for views we return NEW
END;
$$
LANGUAGE plpgsql;

CREATE TRIGGER insert_view_trigger
   INSTEAD OF INSERT ON tablename_view
   FOR EACH ROW EXECUTE PROCEDURE partitioned_insert_trigger(); -- create "INSTEAD OF" trigger

المرجع: http://www.postgresql.org/docs/9.2/static/trigger-definition.html

إذا ذهبت إلى مسار عرض المجمع، فإن أحد الخيارات هو أيضًا تحديد مشغلات تافهة "بدلاً من" للحذف والتحديث أيضًا، فيمكنك فقط استخدام اسم جدول العرض بدلاً من جدولك العادي في جميع المعاملات.

هناك خيار آخر يستخدم العرض وهو إنشاء قاعدة إدراج بحيث تنتقل أي إدراجات في الجدول الرئيسي إلى العرض [الذي يستخدم مشغله]، على سبيل المثال (بافتراض أن لديك بالفعل partitioned_insert_trigger وتم إنشاء tablename_view وinsert_view_trigger كما هو مذكور أعلاه)

create RULE use_right_inserter_tablename AS
      ON INSERT TO tablename
      DO INSTEAD insert into tablename_view VALUES (NEW.*);

بعد ذلك سوف يستخدم إدراج غلاف عرض العمل الجديد الخاص بك.

شكرا!لقد نجحت المهمة، ولم تظهر أي مشاكل، حتى الآن :)....شيء واحد أنت...كان علي أن أنفذ BatcherFactory الصف ووضعه في persistence.xml ملف ، مثل هذا:

property name="hibernate.jdbc.factory_class" value="path.to.my.batcher.factory.implementation"

من هذا المصنع قمت باستدعاء تطبيق المُجمِّع الخاص بي باستخدام الكود أعلاه

PS Hibernate Core 3.2.6 GA

شكرا مرة آخرى

لقد واجهت نفس المشكلة أثناء إدراج المستندات من خلال وضع السبات بعد أن وجدت الكثير من عمليات البحث أنه من المتوقع إرجاع الصفوف المحدثة، لذا بدلاً من تغييرها إلى قيمة فارغة جديدة في إجراء التشغيل الذي سيحل المشكلة كما هو موضح أدناه

إرجاع جديد

لقد وجدت حلاً آخر لنفس المشكلة على صفحة الويب هذه:

يشير هذا إلى نفس الحل الذي قاله @rogerdpack، وهو تغيير ملف العودة فارغة ل العودة جديد, وإضافة مشغل جديد يحذف المجموعة المكررة في الملف الرئيسي مع الاستعلام:

DELETE FROM ONLY master_table;
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top