Hibernate 获取带有字符串前缀的下一个 ID
Posted
技术标签:
【中文标题】Hibernate 获取带有字符串前缀的下一个 ID【英文标题】:Hibernate get next ID with string prefix 【发布时间】:2018-01-18 08:15:58 【问题描述】:我有一个关于 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)。 ***.com/questions/21829023/… 检查***.com/questions/31158509/…是否能解决您的问题 按降序排列并限制(1)。 您使用的是 Hibernate 还是 JPA? 如果你使用hibernate,你可以实现你自己的ID生成器,见here。 【参考方案1】:正如我在twocomments 中指出的,Informix 中可用的一种技术将使用触发器和SERIAL 列。另一种技术是使用 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 惯用语。系统目录有1
的tabid
记录的systables
表。在现代版本的 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 的缺点之一是排序顺序不是数字而是字典顺序。
【讨论】:
【参考方案2】:创建一个自定义 ID 生成器类,用于查询最后插入的 id 并提取数字部分。选择字符串长度等于 ID 的最大字符串长度的所有 id,按降序排列并将结果集限制为 1。然后像您在问题中所做的那样递增数字。
【讨论】:
按id列降序排列,例如表中有2条记录EXE9和EXE10。返回值为 EXE9,因为该列是 varchar。 添加 where 子句,指定 id 的字符串长度必须等于所有 id 的最大字符串长度 感谢您的建议。是的,我一开始也是这么想的。要先按字符串长度对 id 进行排序,然后得到最长的,然后仅根据结果进行排序。但是仅仅生成 id 有点复杂。 对不起,我对informix一无所知(你现在可能已经猜到了:))。最后一个建议,如果可能的话,创建一个跟踪 ids 数字的第二个表。在您的自定义 id 生成器类中更新它。祝你好运。 没问题。我想我会坚持首先根据字符串长度进行排序的逻辑。创建新表来存储当前最高数字是最好的解决方案。不幸的是,我们没有权限这样做,只是要求他们添加新表,必须经历很多过程。叹。再次感谢?以上是关于Hibernate 获取带有字符串前缀的下一个 ID的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces 1334C - Circle of Monsters(差值取前缀和 / 贪心)