PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Анотация за хибернация за сериен тип PostgreSQL

Опасност: Въпросът ви предполага, че може да правите грешка в дизайна – опитвате се да използвате последователност от база данни за „бизнес“ стойност, която се представя на потребителите, в този случай номера на фактури.

Не използвайте последователност, ако имате нужда от нещо повече от тестване на стойността за равенство. То няма ред. Тя няма "разстояние" от друга стойност. Просто е равно, или не е равно.

Отмяна: Последователностите обикновено не са подходящи за такива употреби, тъй като промените в последователности не се връщат обратно с транзакция ROLLBACK . Вижте долните колонтитули на функциите-последователност и CREATE SEQUENCE .

Отмяната се очакват и са нормални. Те възникват поради:

  • застой, причинен от конфликтна поръчка за актуализиране или други заключвания между две транзакции;
  • оптимистични връщания на заключване в хибернация;
  • преходни клиентски грешки;
  • поддръжка на сървъра от DBA;
  • конфликти на сериализация в SERIALIZABLE или транзакции за изолиране на моментни снимки

... и още.

Вашето приложение ще има „дупки“ в номерирането на фактурата, където се случват тези връщания. Освен това няма гаранция за поръчка, така че е напълно възможно транзакция с по-късен пореден номер да се извърши по-рано (понякога много по-рано) от един с по-късен номер.

Нарязване на парчета:

Също така е нормално някои приложения, включително Hibernate, да вземат повече от една стойност от последователност наведнъж и да ги раздават на транзакции вътрешно. Това е допустимо, защото не трябва да очаквате генерираните от последователности стойности да имат някакъв смислен ред или да бъдат сравними по какъвто и да е начин, освен по равенство. За номерирането на фактури също искате да поръчате, така че няма да изобщо щастлив, ако Hibernate грабне стойности 5900-5999 и започне да ги раздава от 5999 с броене надолу или алтернативно нагоре-после-надолу, така че номерата на вашите фактури да са:n, n+1, n+49, n+2, n+48, ... n+50, n+99, n+51, n+98, [n+52 загубени при връщане назад], n+97, ... . Да, разпределителят високо-по-ниско съществува в Hibernate.

Това не помага, освен ако не дефинирате отделен @SequenceGenerator във вашите картографии, Hibernate обича да споделя една последователност за всеки генериран идентификационен номер също. Грозно.

Правилна употреба:

Последователността е подходяща само ако само изискват номерацията да е уникална. Ако и вие имате нужда да бъде монотонна и редна, трябва да помислите за използването на обикновена таблица с поле за брояч чрез 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 колона и оставете оптимистичното заключване да се погрижи за конфликтите;
  • Дефинирайте обект за invoice_number и използвайте изрично песимистично заключване в Hibernate, за да изберете ... за актуализация и след това актуализация.

Всички тези опции ще сериализират транзакциите ви - или чрез връщане на конфликти с @Version, или чрез блокирането им (заключване), докато притежателят на заключване не се ангажира. Така или иначе, последователностите без пропуски ще наистина забавете тази област от приложението си, така че използвайте последователности без интервали само когато трябва.

@GenerationType.TABLE :Изкушаващо е да използвате @GenerationType.TABLE с @TableGenerator(initialValue=1, ...) . За съжаление, докато GenerationType.TABLE ви позволява да зададете размер на разпределение чрез @TableGenerator, той не предоставя никакви гаранции за подреждане или поведение за връщане назад. Вижте спецификацията на JPA 2.0, раздел 11.1.46 и 11.1.17. По-специално „Тази спецификация не дефинира точното поведение на тези стратегии. и бележка под линия 102 „Преносимите приложения не трябва да използват анотацията GeneratedValue в други постоянни полета или свойства [от @Id първични ключове]" . Така че не е безопасно да използвате @GenerationType.TABLE за номериране, което изисквате да бъде без празнини, или номериране, което не е в свойство на първичен ключ, освен ако вашият доставчик на JPA не даде повече гаранции от стандартните.

Ако сте закъсали с последователност :

Плакатът отбелязва, че те имат съществуващи приложения, използващи БД, които вече използват последователност, така че са останали с нея.

Стандартът JPA не гарантира, че можете да използвате генерирани колони, освен на @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 от масата на гишето.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Хуманизирано или естествено сортиране на числа от смесени низове от думи и числа

  2. Кортежите не се вмъкват последователно в таблицата на базата данни?

  3. Съхранение на изображения в PostgreSQL

  4. Как мога да слея колоните от две таблици в един изход?

  5. Как да картографирате PostgreSQL enum с JPA и Hibernate