Spring boot oracle 11g存储的序列与模型中显示的值不同

问题描述 投票:0回答:1

嗨,我正在使用 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 触发器再次将其替换为下一个值。

在这种情况下,我们如何保存空值,以便数据库触发器更新该值?

任何想法将不胜感激。

java oracle spring-boot spring-data-jpa oracle11g
1个回答
0
投票
@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,一次来自数据库触发器)。

您可以:

  1. 尝试

    @GeneratedValue(strategy = GenerationType.IDENTITY)
    ,它告诉 Spring Boot 数据库将生成该值。

  2. 删除触发器并依靠 Spring Boot 来调用序列(如果您始终通过应用程序输入数据,则该序列有效,但如果您也手动输入数据并希望自动生成

    id
    列,则该序列无效)。

  3. 更改触发器看看是否

    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;
    /
    

    直到有人手动指定比序列更高的数字,然后序列达到该数字并尝试插入重复值为止,该方法才会起作用。

  4. 不要修复这个问题;是的,该序列被调用两次,但您不需要有连续的

    id
    值,因此这只是(可能)轻微的低效率,而不是实际的问题。

© www.soinside.com 2019 - 2024. All rights reserved.