我在数据库中有一个表和一个生成器。该表有一个主键,它是一个Integer。如果用户试图在表中插入记录,则hibernate会从数据库中的序列中进行选择,因此它会使序列自行增加。问题是,如果在提交事务时发生某些错误,则序列已经增加,并且要保存的下一个记录将不会使用与之前插入的最后一个记录相比具有一致序列的主键。
有没有办法解决这个问题?
--------编辑---------
我设法创建了一个执行此任务的触发器:
CREATE TRIGGER TBLTESTE_BI0 FOR TBLTEST
ACTIVE BEFORE INSERT POSITION 0
AS
declare variable next_value integer;
begin
select coalesce(max(my_sequence_field+1),1) from tbltest into :next_value;
new.my_sequence_field=:next_value;
end
我当然不会将此作为主键使用,主键仍将由序列生成器生成。 但只是一个问题,是否保证my_sequence_field将具有正确的序列,没有重复的值?请考虑该值仅由触发器设置。
答案 0 :(得分:4)
考虑这三个理想的属性:
您的系统中只能有两个这样的属性。如果您希望数字严格按顺序排列,则必须锁定表以进行新插入,或者在插入行后异步填充唯一值。
同样,如果您想避免序列化新行的创建,那么您必须推迟分配新的唯一标识符,或者不要严格分配顺序值。
第一项和第二项对于合成主键通常更为重要。如果您需要生成严格的顺序值(例如,发票编号有时必须按法律顺序排列),那么您通常会删除前两个属性中的一个。
答案 1 :(得分:1)
作为大卫explains,当你需要生成严格的顺序值时,主键并不适合。#34;。如果您有此要求,则可以使用select for update
。
例如,在发票编号的情况下,使用带有"最后发票编号的额外表格"并同步插入。在pseuso代码:
start transaction
// Lock the sequence record.
// All other transaction for the same debtor have to wait for this lock.
select id, lastnumber from invoicenumbers where debtor=1 for update
ResultSet rs = ps.executeQuery();
long lastNumberId = rs.getLong(1);
int lastNumber = rs.getInt(2);
insert into invoices (debtor, invoice_id, amount) values (1, ?, 1)
// Increase sequence with each insert
ps.setInt(1, ++lastNumber);
ps.executeUpdate();
// Update the sequence number
update invoicenumbers set lastnumber=? where id=?
ps.setInt(1, lastNumber);
ps.setLong(2, lastNumberId);
ps.executeUpdate();
commit transaction ON ERROR rollback
示例表:
create table invoicenumbers
(
id bigserial primary key,
debtor int,
lastnumber int
);
create table invoices
(
id bigserial primary key,
debtor int,
invoice_id int,
amount int
);