Как генерировать отформатированный уникальный восходящий идентификатор базы данных независимо - PullRequest
1 голос
/ 07 февраля 2012

Я хотел бы сгенерировать внутренний идентификатор (не @Id) для одного из объектов моего домена с фиксированным форматом:

2012-FLD-00000001

2012-FLD-00000002

...

2012-FLD-99999999

2013-FLD-00000001

2013-FLD-00000002

...

2013-FLD-99999999

Я понятия не имею, как реализовать эту функцию независимо от базы данных, используя JPA (и Hibernate в качестве поставщика).

Что мне нужно, это:

 - Uniqueness
 - Increasing numbers
 - Restarting the sequence in new years
 - A clear and hack-free solution 

Ответы [ 3 ]

0 голосов
/ 07 февраля 2012

взгляните на Hibernate.Id.TableGenerator, который использует таблицу для сохранения информации для генерации идентификатора

0 голосов
/ 08 февраля 2012

Если вы используете JPA, вы можете создать прослушиватель событий для события pre-persist.
Слушатель событий (я назвал его DomainIDGenerator) затем вставит следующий идентификатор домена в вашу сущность.

Класс слушателя

public class DomainIDGenerator {
    private static final AtomicInteger counter;
    private static int year = Calendar.getInstance().get(Calendar.YEAR);

    static {
        //TODO initialize 'counter' with the last value used in the DB
    }

    private static int getYear() {
        int currentYear = Calendar.getInstance().get(Calendar.YEAR);
        if (currentYear != year) {
            year = currentYear;
            counter.set(0);
        }
        return currentYear;
    }

    private static synchronized String getNextID() {
        return String.format("%d-FDL-%08d", getYear(), counter.incrementAndGet());
    }

    @PrePersist
    public void injectDomainID(Object entity) {
        if (entity instanceof MyEntity) {
            ((MyEntity) entity).setDomainID(getNextID());
        }
    }
}

META-INF / persistence.xml:

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
    version="2.0">
        <persistence-unit name="...">
            <mapping-file>META-INF/persistence-defaults.xml</mapping-file>
            ...
        </persistence-unit>
</persistence>

META-INF / живучесть-DEFAULTS.XML:

<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
    version="1.0">
        <persistence-unit-metadata>
            <persistence-unit-defaults>
                <entity-listeners>
                    <entity-listener class="org.example.DomainIDGenerator" />
                </entity-listeners>
            </persistence-unit-defaults>
        </persistence-unit-metadata>
</entity-mappings>
0 голосов
/ 07 февраля 2012

Основная проблема с этим - особенно с требованием уникальности - на уровне постоянства заключается в том, что вы не можете гарантировать, что ваше приложение является единственным приложением, попавшим в базу данных.На самом деле, держу пари, что сейчас есть еще как минимум два приложения, которые пройдут прямо за Java - графический интерфейс вашего dbms и интерфейс командной строки вашего dbms.(В последнем контракте с полным рабочим днем, который я заключил, заявки, написанные на более чем двух десятках языков в течение 30 лет, попали в основную производственную базу данных.)

Грубая сила всегда работает.(Но может создать горячую точку.)

Вы можете сгенерировать свои внутренние идентификаторы сегодня и сохранить их в таблице.

create table internal_iden (
  iden char(17) primary key 
    check (iden ~ '2[0-9]{3}-FLD-[0-9]{8}'),
  taken_by varchar(15) null,
  taken_at timestamp null
);

Ограничение CHECK () гарантирует, что вставленные значения соответствуют регулярному выражению.(PostgreSQL поддерживает регулярные выражения POSIX среди прочих; оператор "~" сообщает dbms, что это сравнение ожидает регулярного выражения POSIX.) В зависимости от требований вашего приложения, вы можете или не хотите, чтобы другие таблицы устанавливали ссылку на внешний ключ для этой таблицы internal_iden.

insert into internal_iden (iden) values
('2012-FLD-00000001'),
('2012-FLD-00000002'),
('2012-FLD-00000003'),
('2012-FLD-00000004'),
('2012-FLD-00000005'),
('2012-FLD-00000006'),
('2012-FLD-00000007'),
('2012-FLD-00000008'),
('2012-FLD-00000000'),
('2012-FLD-00000010'),
('2012-FLD-00000011'),
('2012-FLD-00000012');

Затем вы можете создать хранимую процедуру или функцию, чтобы обернуть эти два оператора в одну транзакцию.

select min(iden)
from internal_iden
where taken_by is null;

update internal_iden
set taken_by = 'server1',
    taken_at = current_timestamp
where iden = (select min(iden)
              from internal_iden
              where taken_by is null);

На самом деле, в хранимой процедуре вы можете заменить этоподзапрос в последнем предложении WHERE с буквальным значением идентификатора, который вы получили из инструкции SELECT.Частичный индекс take_by может повысить производительность.(Если ваша база данных поддерживает частичные индексы.)

create index ix1 on internal_iden (taken_by)
where (taken_by is null);

Максимальное количество строк в год составляет около 100 миллионов.Существует несколько способов обработки такого количества строк.

  • Создайте их все сразу и живите с ними.
  • Создайте их все сразу и разделите таблицу.
  • Создайте миллион и запустите задание cron, которое подсчитывает количество свободных идентификаторов и добавляет еще миллион, когда число уменьшается.
  • Пусть хранимая процедура позаботится о добавлении идентификаторов по мере необходимости.

Имейте в виду разделение, если продолжите эту идею.

...