खतरा: आपके प्रश्न का तात्पर्य है कि आप एक डिज़ाइन गलती कर रहे हैं - आप "व्यवसाय" मान के लिए डेटाबेस अनुक्रम का उपयोग करने का प्रयास कर रहे हैं जो उपयोगकर्ताओं को प्रस्तुत किया जाता है, इस मामले में चालान संख्या।
यदि आपको समानता के मूल्य का परीक्षण करने के अलावा और कुछ चाहिए तो अनुक्रम का उपयोग न करें। इसका कोई आदेश नहीं है। इसका दूसरे मूल्य से कोई "दूरी" नहीं है। यह बस बराबर है, या नहीं के बराबर है।
रोलबैक: अनुक्रम आमतौर पर ऐसे उपयोगों के लिए उपयुक्त नहीं होते हैं क्योंकि अनुक्रमों में परिवर्तन लेन-देन के साथ वापस नहीं किए जाते हैं ROLLBACK
. फंक्शन-सीक्वेंस पर पाद लेख देखें और CREATE SEQUENCE
।
रोलबैक अपेक्षित और सामान्य हैं। वे निम्न के कारण होते हैं:
- दो लेन-देन के बीच परस्पर विरोधी अद्यतन आदेश या अन्य अवरोधों के कारण गतिरोध;
- हाइबरनेट में आशावादी लॉकिंग रोलबैक;
- क्षणिक ग्राहक त्रुटियां;
- डीबीए द्वारा सर्वर रखरखाव;
- क्रमबद्धता विरोध
SERIALIZABLE
. में या स्नैपशॉट अलगाव लेनदेन
... और भी बहुत कुछ।
आपके आवेदन में चालान क्रमांकन में "छेद" होंगे जहां वे रोलबैक होते हैं। इसके अतिरिक्त, कोई आदेश देने की गारंटी नहीं है, इसलिए यह पूरी तरह से संभव है कि बाद की अनुक्रम संख्या वाला लेन-देन पहले (कभी-कभी बहुत किया जाएगा) पहले) एक से अधिक बाद की संख्या के साथ।
चंकिंग:
हाइबरनेट सहित कुछ अनुप्रयोगों के लिए, एक समय में एक अनुक्रम से एक से अधिक मान प्राप्त करना और उन्हें आंतरिक रूप से लेनदेन के लिए सौंपना भी सामान्य है। यह अनुमेय है क्योंकि आपको अनुक्रम-जनित मानों की अपेक्षा नहीं करनी चाहिए कि उनका कोई सार्थक क्रम हो या समानता को छोड़कर किसी भी तरह से तुलनीय हो। चालान क्रमांकन के लिए, आप भी आदेश देना चाहते हैं, इसलिए आप बिल्कुल . नहीं होंगे यदि हाइबरनेट 5900-5999 के मूल्यों को पकड़ लेता है और उन्हें 5999 से नीचे गिनना शुरू कर देता है, तो खुश हैं या वैकल्पिक रूप से ऊपर-फिर-नीचे, इसलिए आपके चालान नंबर चलते हैं:n, n+1, n+49, n+2, n+48, ... n+50, n+99, n+51, n+98, [n+52 रोलबैक से हार गया], n+97, ... . हां, हाइबरनेट में उच्च-तब-निम्न आवंटक मौजूद है।
यह तब तक मदद नहीं करता जब तक आप अलग-अलग @SequenceGenerator
. को परिभाषित नहीं करते हैं आपके मैपिंग में, हाइबरनेट हर . के लिए एक ही क्रम साझा करना पसंद करता है जनरेट की गई आईडी भी। बदसूरत।
सही उपयोग:
अनुक्रम केवल तभी उपयुक्त होता है जब आप केवल नंबरिंग अद्वितीय होने की आवश्यकता है। यदि आपको इसे एकरस और क्रमिक होने की भी आवश्यकता है, तो आपको UPDATE ... RETURNING
के माध्यम से काउंटर फ़ील्ड के साथ एक साधारण तालिका का उपयोग करने के बारे में सोचना चाहिए। या SELECT ... FOR UPDATE
(हाइबरनेट में "निराशावादी लॉकिंग") या हाइबरनेट आशावादी लॉकिंग के माध्यम से। इस तरह आप बिना छेद या आउट-ऑफ-ऑर्डर प्रविष्टियों के अंतराल रहित वेतन वृद्धि की गारंटी दे सकते हैं।
इसके बजाय क्या करें:
सिर्फ एक काउंटर के लिए एक टेबल बनाएं। इसमें एक पंक्ति रखें, और जैसे ही आप इसे पढ़ते हैं, इसे अपडेट करें। यह इसे लॉक कर देगा, अन्य लेन-देन को आपके द्वारा किए जाने तक आईडी प्राप्त करने से रोकेगा।
क्योंकि यह आपके सभी लेन-देनों को क्रमानुसार संचालित करने के लिए बाध्य करता है, ऐसे लेन-देनों को छोटा रखने का प्रयास करें जो इनवॉइस आईडी उत्पन्न करते हैं और उनमें आपकी आवश्यकता से अधिक काम करने से बचें।
CREATE TABLE invoice_number (
last_invoice_number integer primary key
);
-- PostgreSQL specific hack you can use to make
-- really sure only one row ever exists
CREATE UNIQUE INDEX there_can_be_only_one
ON invoice_number( (1) );
-- Start the sequence so the first returned value is 1
INSERT INTO invoice_number(last_invoice_number) VALUES (0);
-- To get a number; PostgreSQL specific but cleaner.
-- Use as a native query from Hibernate.
UPDATE invoice_number
SET last_invoice_number = last_invoice_number + 1
RETURNING last_invoice_number;
वैकल्पिक रूप से, आप यह कर सकते हैं:
- इनवॉइस_नंबर के लिए एक इकाई को परिभाषित करें, एक
@Version
जोड़ें कॉलम, और आशावादी लॉकिंग को संघर्षों का ध्यान रखने दें; - इनवॉइस_नंबर के लिए एक इकाई को परिभाषित करें और एक चयन करने के लिए हाइबरनेट में स्पष्ट निराशावादी लॉकिंग का उपयोग करें ... अपडेट के लिए फिर एक अपडेट के लिए।
ये सभी विकल्प आपके लेन-देन को क्रमबद्ध करेंगे - या तो @Version का उपयोग करके विरोधों को वापस लाकर, या लॉक होल्डर के प्रतिबद्ध होने तक उन्हें (लॉकिंग) अवरुद्ध करके। किसी भी तरह, गैपलेस सीक्वेंस वास्तव में होंगे अपने आवेदन के उस क्षेत्र को धीमा कर दें, इसलिए जब आपको करना हो तो केवल गैपलेस सीक्वेंस का उपयोग करें।
@GenerationType.TABLE
:@GenerationType.TABLE
. का उपयोग करना आकर्षक है एक @TableGenerator(initialValue=1, ...)
. के साथ . दुर्भाग्य से, जबकि GenerationType.TABLE आपको @TableGenerator के माध्यम से आवंटन आकार निर्दिष्ट करने देता है, यह ऑर्डरिंग या रोलबैक व्यवहार के बारे में कोई गारंटी प्रदान नहीं करता है। जेपीए 2.0 युक्ति, खंड 11.1.46, और 11.1.17 देखें। विशेष रूप से "यह विनिर्देश इन रणनीतियों के सटीक व्यवहार को परिभाषित नहीं करता है। और फुटनोट 102 "पोर्टेबल एप्लिकेशन को अन्य स्थायी क्षेत्रों या गुणों पर [@Id
से जेनरेट वैल्यू एनोटेशन का उपयोग नहीं करना चाहिए प्राथमिक कुंजी]" . इसलिए @GenerationType.TABLE
. का उपयोग करना असुरक्षित है जब तक आपका जेपीए प्रदाता मानक से अधिक गारंटी नहीं देता है, तब तक नंबरिंग के लिए आपको गैपलेस या नंबरिंग की आवश्यकता होती है जो प्राथमिक कुंजी संपत्ति पर नहीं है।
यदि आप किसी क्रम में फंस गए हैं :
पोस्टर नोट करता है कि उनके पास डीबी का उपयोग करने वाले मौजूदा ऐप्स हैं जो पहले से ही अनुक्रम का उपयोग करते हैं, इसलिए वे इसके साथ फंस गए हैं।
जेपीए मानक गारंटी नहीं देता है कि आप @Id को छोड़कर जेनरेट किए गए कॉलम का उपयोग कर सकते हैं, आप (ए) इसे अनदेखा कर सकते हैं और तब तक आगे बढ़ सकते हैं जब तक आपका प्रदाता आपको अनुमति देता है, या (बी) एक डिफ़ॉल्ट मान के साथ सम्मिलित करें और फिर से करें -डेटाबेस से पढ़ें। बाद वाला सुरक्षित है:
@Column(name = "inv_seq", insertable=false, updatable=false)
public Integer getInvoiceSeq() {
return invoiceSeq;
}
insertable=false
. के कारण प्रदाता कॉलम के लिए कोई मान निर्दिष्ट करने का प्रयास नहीं करेगा। अब आप एक उपयुक्त DEFAULT
. सेट कर सकते हैं डेटाबेस में, जैसे nextval('some_sequence')
और इसे सम्मानित किया जाएगा। आपको डेटाबेस से इकाई को EntityManager.refresh()
. के साथ फिर से पढ़ना पड़ सकता है इसे जारी रखने के बाद - मुझे यकीन नहीं है कि दृढ़ता प्रदाता आपके लिए ऐसा करेगा या नहीं और मैंने कल्पना की जाँच नहीं की है या कोई डेमो प्रोग्राम नहीं लिखा है।
केवल नकारात्मक पक्ष यह है कि ऐसा लगता है कि कॉलम @ NotNull या nullable=false
नहीं बनाया जा सकता है , क्योंकि प्रदाता यह नहीं समझता है कि डेटाबेस में कॉलम के लिए एक डिफ़ॉल्ट है। यह अभी भी NOT NULL
हो सकता है डेटाबेस में।
यदि आप भाग्यशाली हैं तो आपके अन्य ऐप्स भी INSERT
से अनुक्रम कॉलम को छोड़ने के मानक दृष्टिकोण का उपयोग करेंगे। की कॉलम सूची या कीवर्ड को स्पष्ट रूप से निर्दिष्ट करना DEFAULT
nextval
. को कॉल करने के बजाय मान के रूप में . log_statement = 'all'
. को सक्षम करके इसका पता लगाना कठिन नहीं होगा में postgresql.conf
और लॉग खोज रहे हैं। यदि वे करते हैं, तो आप अपने DEFAULT
को बदलकर वास्तव में सब कुछ गैपलेस में बदल सकते हैं यदि आप तय करते हैं कि आपको इसकी आवश्यकता है BEFORE INSERT ... FOR EACH ROW
. के साथ ट्रिगर फ़ंक्शन जो NEW.invoice_number
sets सेट करता है काउंटर टेबल से।