嗨,我正在使用 spring boot (2.7.9) 和 java 11。我的数据库是 Oracle 11g(相当旧)
我创建了一个表,如下
CREATE SEQUENCE BOOK_ENTRY_SEQ
MINVALUE 1
MAXVALUE 999999999999999999999999999
START WITH 1
INCREMENT BY 1 CACHE 20
NOORDER
NOCYCLE
;
CREATE TABLE BOOK_ENTRY
(
ID NUMBER PRIMARY KEY,
STATUS VARCHAR2(100 BYTE) NOT NULL,
MESSAGE VARCHAR2(4000 BYTE)
);
CREATE OR REPLACE TRIGGER "BOOK_ENTRY_TRG"
BEFORE INSERT
ON BOOK_ENTRY
REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
BEGIN
:new.ID := BOOK_ENTRY_SEQ.nextval;
END BOOK_ENTRY_TRG;
/
现在在我的 Spring Boot 应用程序中
我有以下型号
@Entity
@Table(name = "BOOK_ENTRY")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Data
@EqualsAndHashCode
public class BookEntry implements Serializable {
@Id
@NotNull
@Column(name = "ID", updatable = false, insertable = false)
@SequenceGenerator(name = "seq_generator", sequenceName = "BOOK_ENTRY_SEQ", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_generator")
private Long id;
@NotNull
@Column(name = "STATUS")
private String status;
@Column(name = "MESSAGE")
private String message;
}
存储库方法是
public interface BookEntryRepository extends JpaRepository<BookEntry, Long> {
}
还有服务类
private void createEntry() {
BookEntry bookEntry = new bookEntry();
bookEntry.setStatus("NEW");
bookEntry.setMessage("Test 1");
bookEntryRepository.save(bookEntry);
}
当我进行调试并打印条目时,例如 id 是 61,但保存时它是 62。
我的猜测是它从 jpa 中提取一次序列值,然后 db 触发器再次将其替换为下一个值。
在这种情况下,我们如何保存空值,以便数据库触发器更新该值?
任何想法将不胜感激。
@SequenceGenerator(name = "seq_generator", sequenceName = "BOOK_ENTRY_SEQ", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_generator")
告诉您的 Spring Boot 应用程序该值将由数据库序列生成
BOOK_ENTRY_SEQ
,当您保存实体时,Spring Boot 将与数据库通信,找到序列的下一个值并将 id
设置为该值然后 insert
将实体存入数据库。
当数据库检索实体时,它会运行
BEFORE INSERT
触发器,该触发器声明忽略任何旧的 id
值并生成下一个 BOOK_ENTRY_SEQ
值并将其用作新的 id
值。
因此,对于每个
BOOK_ENTRY
实体,您将两次生成 BOOK_ENTRY_SEQ
序列的下一个值(一次来自 Spring Boot,一次来自数据库触发器)。
您可以:
尝试
@GeneratedValue(strategy = GenerationType.IDENTITY)
,它告诉 Spring Boot 数据库将生成该值。
删除触发器并依靠 Spring Boot 来调用序列(如果您始终通过应用程序输入数据,则该序列有效,但如果您也手动输入数据并希望自动生成
id
列,则该序列无效)。
更改触发器看看是否
id IS NOT NULL
,然后忽略它:
CREATE OR REPLACE TRIGGER "BOOK_ENTRY_TRG"
BEFORE INSERT
ON BOOK_ENTRY
REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
BEGIN
IF :old.id IS NULL THEN
:new.ID := BOOK_ENTRY_SEQ.nextval;
END IF;
END BOOK_ENTRY_TRG;
/
直到有人手动指定比序列更高的数字,然后序列达到该数字并尝试插入重复值为止,该方法才会起作用。
不要修复这个问题;是的,该序列被调用两次,但您不需要有连续的
id
值,因此这只是(可能)轻微的低效率,而不是实际的问题。