使用Hibernate获取带有字符串前缀的下一个ID

3

我有一个关于Hibernate主键生成的问题。我在维护现有的注册系统。目前的设计使用字符串作为主键。规则是类似于"EXE" + max()。下面是表格的样子。

+----------+---------------------------+----------------+
| ID       |   Email                   |   Name         |
+----------+---------------------------+----------------+
|EXE1      | email1@gmail.com          | Name 1         |
+----------+---------------------------+----------------+
|EXE5      | email5@gmail.com          | Name 5         |
+----------+---------------------------+----------------+
|EXE14     | email14@gmail.com         | Name 14        |
+----------+---------------------------+----------------+
|EXE15     | email15@gmail.com         | Name 15        |
+----------+---------------------------+----------------+

目前我正在使用以下代码生成ID。

Long rowCount = (Long) getSession().createCriteria(Exemption168DB.class).setProjection(Projections.rowCount()).uniqueResult();
if(rowCount == null)
    rowCount = 0L;
return String.format("%s%d", CommonConstant.EXEMPTION_KEY_PREFIX, rowCount + 1);

但问题是:它使用行计数来获取下一个序列数字。因此,在上述情况下,该方法将返回EXE5(此 ID 已存在于表中,因此会引发异常),因为表中的行计数为4,然后加1。我需要的是EXE16。任何帮助都非常感激。额外信息,我们使用 Informix 作为数据库引擎。

为什么不查询最后插入的ID呢?类似这样的(需要适应Informix)。https://dev59.com/32Eh5IYBdhLWcg3w9ndj - lher
1
限制翻译为中文,返回翻译后的文本:按降序排序并限制(1)。 - lher
1
你使用的是Hibernate还是JPA? - Tom
1
如果你正在使用Hibernate,你可以实现自己的ID生成器,参考这里 - Tom
@Tom,我正在使用 Hibernate。 - khairul.ikhwan
显示剩余8条评论
2个回答

1
正如我在两个 评论中提到的,Informix中可用的一种技术是使用触发器和SERIAL列。另一种技术是使用SEQUENCE和存储过程。
以下是一些演示代码,使用了SEQUENCE和存储过程:
CREATE SEQUENCE registry_seq
    INCREMENT BY 3
    START WITH 37
    MINVALUE 21
    MAXVALUE 299
    CYCLE;

CREATE PROCEDURE get_next_registry_id() RETURNING VARCHAR(10) AS registry_id;

    DEFINE i INTEGER;
    DEFINE r VARCHAR(10);
    SELECT registry_seq.NEXTVAL INTO i FROM "informix".SysTables WHERE tabid = 1;

    LET r = "EXE" || i;

    RETURN r;

END PROCEDURE;

CREATE TEMP TABLE registry
(
    id              VARCHAR(10) NOT NULL UNIQUE,
    email           VARCHAR(64) NOT NULL UNIQUE,
    name            VARCHAR(64) NOT NULL UNIQUE
);

INSERT INTO registry VALUES('EXE1', 'email1@gmail.com', 'Name 1');
INSERT INTO registry VALUES('EXE5', 'email5@gmail.com', 'Name 5');
INSERT INTO registry VALUES('EXE14', 'email14@gmail.com', 'Name 14');
INSERT INTO registry VALUES('EXE15', 'email15@gmail.com', 'Name 15');

INSERT INTO registry VALUES(get_next_registry_id(), 'email' || registry_seq.currval || '@example.com', 'User ID ' || registry_seq.currval);
INSERT INTO registry VALUES(get_next_registry_id(), 'email' || registry_seq.currval || '@example.com', 'User ID ' || registry_seq.currval);
INSERT INTO registry VALUES(get_next_registry_id(), 'email' || registry_seq.currval || '@example.com', 'User ID ' || registry_seq.currval);

SELECT * FROM registry ORDER BY id;

显然,您会为CREATE SEQUENCE语句选择不同的控制值。 对于我的测试来说,它们半方便地工作了(最初是在不同的表上进行测试)。

FROM "informix".systables WHERE tabid = 1是选择单行数据的标准Informix习语。 系统目录以systables表记录,其中tabid1。 在现代版本的Informix(意味着您应该运行的任何内容;尽管可能仍有一些人在运行旧版本),您可以从sysmaster:sysdual(或者,如果您非常安全,则为sysmaster:"informix".sysdual)中进行选择,这是一个具有单个列的单行表。

最终输出为:

EXE1    email1@gmail.com        Name 1
EXE14   email14@gmail.com       Name 14
EXE15   email15@gmail.com       Name 15
EXE37   email37@example.com     User ID 37
EXE40   email40@example.com     User ID 40
EXE43   email43@example.com     User ID 43
EXE5    email5@gmail.com        Name 5

注意,字母数字 ID 的一个缺点是排序顺序不是数字而是词典序。

1
创建一个自定义的Id生成器类,查询最后插入的id并提取数字部分。选择所有字符串长度等于ID的最大字符串长度的ID,按降序排序并将结果集限制为1。然后像在您的问题中那样递增数字。

通过按照id列降序排列,例如在表中有2条记录EXE9和EXE10。返回值是EXE9,因为该列是varchar类型。 - khairul.ikhwan
1
添加一个where子句,在其中指定id的字符串长度必须等于所有id中的最大字符串长度。 - lher
谢谢您的建议。是的,我最初考虑过这个方法。首先按字符串长度排序id,然后获取最长的id,然后再根据结果进行排序。但这样生成id有点复杂。 - khairul.ikhwan
1
抱歉,我对Informix一无所知(你现在可能已经猜到了:)). 最后一个建议是创建第二个表来跟踪数字ID,如果可能的话。在您的自定义ID生成器类中进行更新。祝你好运。 - lher
1
没问题。我想我会坚持首先根据字符串长度进行排序的逻辑。创建一个新表来存储当前最高数是最好的解决方案。不幸的是,我们没有权限这样做,只能请求他们添加新表,需要经过很多流程。唉。再次感谢。 - khairul.ikhwan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接