当我有以下情况时,我有一个带有@Id列的实体:
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq")
@SequenceGenerator(name = "seq", sequenceName = "user_accounts_id_seq")
public Long getId(){
return id;
}
当我通过执行以下命令手动将数据插入表中时:
insert into user_accounts(id, name) values(1, "John");
我没有使用序列,而是手动添加等于 1 的 id。
然后我通过以下方式在 Java 中创建用户帐户
UserAccount user = new UserAccount(null, "Paul") // where null => id
并用UserAccountService's
方法保存save
并得到一个错误,例如 id 密钥中有重复项。
我不确定我是否很好地理解了这些策略。我希望能够在数据库编辑器中手动添加一些值,然后在保存时在程序中添加一些值,如果存在带有 id 的值,则休眠应该采用下一个可能的值。
将手工制作的ID 与数据库生成的 ID 混合在一起不是一个好主意。你应该尽可能避免它。
序列的工作原理
假设这是您的表:
CREATE TABLE user_accounts
(
id SERIAL PRIMARY KEY,
name TEXT NOT NULL CHECK(trim(name) > '')
) ;
-- The previous `CREATE` has actually worked as if it were defined like:
CREATE TABLE user_accounts
(
-- Name of sequence = name of table || '_' || name of column || '_id'
id INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('user_accounts_id_seq'),
name TEXT NOT NULL CHECK(trim(name) > '')
) ;
此时,您实际上可以检查是否已创建一个序列:
SELECT *
FROM information_schema.sequences;
sequence_catalog | sequence_schema | sequence_name | data_type | numeric_precision | numeric_precision_radix | numeric_scale | start_value | minimum_value | maximum_value | 增量 | cycle_option :--------------- |:-------------------------- |:------------------- |:-------- |----------------: |----------------------: |------------: |:---------- |:------------ |:------------------ |:-------- |:----------- 邮政 |fiddle_sehahfpstptzxjchrypb |user_accounts_id_seq |比金特 | 64 | 2 | 0 |1 |1 |9223372036854775807 |1 |NO
此时,您可以在表中插入一行,而无需指定列的值id
,id
列将采用其默认值,该值将被视为名为user_accounts_id_seq
的序列的nextval
:
INSERT INTO user_accounts
(name)
VALUES
('First inserted user account') ;
在单个用户设置中(没有其他人对user_accounts
做任何事情(,我们将得到id = 1
:
SELECT * FROM user_accounts;
ID | 名称 -: |:-------------------------- 1 |第一个插入的用户帐户
您现在可以检查他的序列当前值,它是1
: 选择库尔瓦尔('user_accounts_id_seq'( ;
库尔瓦尔 | |------: | | 1 |
我们现在可以做奇怪的事情了。首先,我们插入一行,其中包含 id 的显式NULL
值。它在SQL中不起作用(我不知道Hibernate
是否会自己操纵这个INSERT
并删除NULL
并将其转换为DEFAULT
(:
INSERT INTO user_accounts
(id, name)
VALUES
(NULL, 'It won''t work');
错误:列"id"中的空值违反了非空约束 详细信息:失败的行包含(空,它不起作用(。
序列电流值保持不变
SELECT currval('user_accounts_id_seq') ;
|库尔瓦尔 | |------: | | 1 |
此时,我们可以插入一行id = 2
.它将起作用,因为表中没有任何带有该id
的行:
INSERT INTO user_accounts
(id, name)
VALUES
(2, 'Inserted a 2 id, it will work, but will produce problems');
受影响的 1 行
但是,顺序不会更改,这将导致以后的问题:
SELECT currval('user_accounts_id_seq') ;
|库尔瓦尔 | |------: | | 1 |
如果我们现在尝试插入序列号,我们将不走运,序列将给出 2 的nextval
(currval
+ 1(。由于id = 2
已经在桌面上,它将产生一个PK violation
:
INSERT INTO user_accounts
(name)
VALUES
('This won''t work either, we broke the sequence');
错误:重复的键值违反了唯一约束"user_accounts_pkey" 详细信息:密钥 (id(=(2( 已存在。
您可以在以下位置查看所有设置和实验:dbfiddle 这里
解决方法:
如果您确实需要同时使用自动生成的id
和手动生成的id
s,最安全的方法是确保它们位于不重叠的范围内。例如,保留id
1 ...10000 用于手动输入,并以10001
开始您的序列以进行自动输入。
CREATE TABLE user_accounts
(
id SERIAL PRIMARY KEY,
name TEXT NOT NULL CHECK(trim(name) > '')
) ;
ALTER SEQUENCE user_accounts_id_seq RESTART WITH 10001 ;
我建议不要尝试让数据库(通过触发器或但是(使用下一个可用的id
因为它是一个PK violation
。需要具有隔离级别SERIALIZABLE
才能正常工作,否则在并发方案中仍很有可能出现问题。
在这里的dbfiddle上检查它
如果id
类型是串行的,并且user_accounts_id_seq
是自动生成的序列,则对于手动插入,请使用:
insert into user_accounts(name) values("John");
将从user_accounts_id_seq
序列中获取 ID。
因此,您和休眠将使用相同的user_accounts_id_seq
序列,以保证 id 的唯一值。