根据数据库产品在 Jakarta JPA 中使用不同的 ID 生成器

Use different ID generator in Jakarta JPA depending on DB product

我将应用程序从 Java EE 7/Glassfish 4 迁移到 Jakarta EE 9.1/Glassfish 6.1。我们的产品支持 Oracle 和 MSSQL 数据库。

对于 Oracle,我们使用序列来生成 ID。对于 MSSQL,我们使用标识列。

这是我们对 Glassfish 4 的注解。它对 Oracle 和 MSSQL 都有效。 Glassfish 4 忽略了 MSSQL 的序列并自动选择标识列。

@Entity
@Table(name="tbl_metadata")
public class MetaData {
    
    @Id
    @SequenceGenerator(name = "METADATA_SEQUENCE_GENERATOR", sequenceName = "TBL_METADATA_ID_SEQ", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "METADATA_SEQUENCE_GENERATOR")
    @Column(name="id")
    private long id;

}

这就是实体的持久化方式。

public ENTITY saveAndFlush(ENTITY entity) {
    entityManager().persist(entity);
    entityManager().flush();
    return entity;
}

对于 Glassfish 6.1,我在程序刷新 MSSQL 上的实体时看到此错误。

Caused by: jakarta.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services -
3.0.2.v202107160933): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: com.microsoft.sqlserver.jdbc.SQLServerException: Ein expliziter Wert für die Identitätsspalte kann nicht in der tbl_logs-Tabelle eingefügt werden, wenn IDENTITY_INSERT auf OFF festgelegt ist. Error Code: 544 Call: INSERT INTO tbl_logs (id, str_message, dte_time, int_d1, id_line, id_user) VALUES (?, ?, ?, ?, ?, ?)   bind => [6 parameters bound] Query: InsertObjectQuery(de.pharmacontrol.pilot.entities.LogEntry@5950c7bd)    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:980)     at com.sun.enterprise.container.common.impl.EntityManagerWrapper.flush(EntityManagerWrapper.java:412)

身份插入已关闭,ID 应来自数据库,但 JPA 构造了一个尝试插入 ID 的查询。

我将注释更改为以下内容。

@Entity
@Table(name="tbl_metadata")
public class MetaData {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private long id;
}

它在 MSSQL 上工作正常但在 Oracle 上失败。然后以下适用于 Oracle 但不适用于 MSSQL。

@Entity
@Table(name="tbl_metadata")
public class MetaData {
    
    @Id
    @SequenceGenerator(name = "METADATA_SEQUENCE_GENERATOR", sequenceName = "TBL_METADATA_ID_SEQ", allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "METADATA_SEQUENCE_GENERATOR")
    @Column(name="id")
    private long id;
}

如何注释我的 ID,以便它们在 MSSQL 上使用身份列并在 Oracle 上使用序列?

对于有特殊要求的数据库,您可以使用 orm.xml 定制程序文件。考虑在您的代码中使用 @GeneratorValue(strategy=IDENTITY),并为您的 oracle 配置文件添加一个不同的 orm.xml,您可以在其中定义适当的序列生成器和策略。

orm.xml for oracle 配置文件也可能有助于克服一些关于 table 和列名的特殊情况,以及许多其他注意事项。

使用 EclipseLink 3.0.2 我最终得到了注释

@Entity
@Table(name="tbl_metadata")
public class MetaData {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY, generator = "TBL_METADATA_ID_SEQ")
    @Column(name="id")
    private long id;

}

GeneratedValue.generator 属性 中没有引用 @SequenceGeneratorGeneratedValue.generator 引用了 Oracle 数据库中的序列名称。这适用于具有标识列和 Oracle 序列的 Microsoft SQL 服务器。