我有一个带有@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");
我没有使用序列,但手动添加id等于1。
然后我用
创建Java用户帐户UserAccount user = new UserAccount(null, "Paul") // where null => id
并使用UserAccountService's
方法保存
save
并收到错误消息,例如ID键中有重复。
我不确定我是否理解这些策略。我希望能够在数据库编辑器中手动添加一些值,然后在保存时保存,如果id存在的值,则hibernate应该采用下一个可能的值。
答案 0 :(得分:0)
如果id
的类型是串行的,user_accounts_id_seq
是自动生成的此属性序列,则手动插入使用:
insert into user_accounts(name) values("John");
id将从user_accounts_id_seq
序列中获取
因此,您和hibernate将使用相同的user_accounts_id_seq
序列来保证id的唯一值。
答案 1 :(得分:0)
将手工制作的 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 | increment | cycle_option :--------------- | :-------------------------- | :------------------- | :-------- | ----------------: | ----------------------: | ------------: | :---------- | :------------ | :------------------ | :-------- | :----------- postgres | fiddle_sehahfpstptzxjchrypb | user_accounts_id_seq | bigint | 64 | 2 | 0 | 1 | 1 | 9223372036854775807 | 1 | NO
此时,您可以在表格中插入一行,而不指定列id
的值,id
列将采用其默认值,这将是对于名为nextval
的序列,我们将其视为user_accounts_id_seq
:
INSERT INTO user_accounts
(name)
VALUES
('First inserted user account') ;
在单个用户设置中(没有其他人使用user_accounts
执行任何操作),我们已经获得id = 1
:
SELECT * FROM user_accounts;
id | name -: | :-------------------------- 1 | First inserted user account
您现在可以查看序列当前值,它是1
:
SELECT currval(' user_accounts_id_seq');
| currval | | ------: | | 1 |
我们现在可以做奇怪的事情。首先,我们为id插入一个显式NULL
值的行。它不会在SQL中工作(我不知道Hibernate
是否可以自己操纵此INSERT
并删除 NULL
并转换它到DEFAULT
):
INSERT INTO user_accounts
(id, name)
VALUES
(NULL, 'It won''t work');
ERROR: null value in column "id" violates not-null constraint DETAIL: Failing row contains (null, It won't work).
序列当前值继续相同
SELECT currval('user_accounts_id_seq') ;
| currval | | ------: | | 1 |
在这一点上,我们可以插入一行id = 2
。它会起作用,因为表中已经没有id
的任何行:
INSERT INTO user_accounts
(id, name)
VALUES
(2, 'Inserted a 2 id, it will work, but will produce problems');
1 rows affected
但是,序列未更改,这将导致以后出现问题:
SELECT currval('user_accounts_id_seq') ;
| currval | | ------: | | 1 |
如果我们现在尝试使用序列号插入,我们将运气不好,序列将给出nextval
2(currval
+ 1)。由于id = 2
已经在桌面上,它将生成PK violation
:
INSERT INTO user_accounts
(name)
VALUES
('This won''t work either, we broke the sequence');
ERROR: duplicate key value violates unique constraint "user_accounts_pkey" DETAIL: Key (id)=(2) already exists.
您可以在以下位置查看所有设置和实验: dbfiddle here
如果真的需要同时使用自动生成的 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 here
上查看