如何独立生成格式化的唯一升序标识符数据库

时间:2012-02-07 09:18:37

标签: database hibernate jpa

我想为我的一个具有修复格式的域实体生成内部标识符(不是@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 :(得分:0)

在持久层中处理此问题(尤其是唯一性要求)的主要问题是您不能保证您的应用程序是唯一一个命中数据库的应用程序。事实上,我敢打赌,现在至少还有两个应用程序可以直接通过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);

实际上,在存储过程中,您可以使用从SELECT语句获得的iden的文字值替换最后一个WHERE子句中的子查询。关于taken_by的部分索引可能有助于提高性能。 (如果dbms支持部分索引。)

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

每年的最大行数约为1亿。有几种方法可以处理这么多行。

  • 立即创建所有内容,并使用它。
  • 一次创建所有内容,然后对表进行分区。
  • 创建一百万,并运行一个计算免费标识的cron作业,并在计数变低时再增加一百万。
  • 让存储过程根据需要处理添加标识。

如果你继续这个想法,请记住分区。

答案 1 :(得分:0)

查看Hibernate.Id.TableGenerator,它使用表来保存信息以生成id

答案 2 :(得分:0)

如果您正在使用JPA,则可以为预先保留事件创建事件侦听器 事件监听器(我称之为DomainIDGenerator)然后将下一个域ID注入您的实体。

听众课程

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>